eBPF是Linux内核提供可以在不借助内核模块的场景下,对内核进行观测,注入来实现高阶功能的机制。
eBPF技术作为内核当前最为突破性的技术,具有轻量级,无侵入的特性,在排查网络问题上可以提供很多帮助,在当前的eBPF生态中,bpftrace和bcc都提供了很多出色的功能协助我们进行观测,本章节会简单介绍eBPF的实现原理以及eBPF在云原生场景下的应用的简单介绍。
eBPF能够在实现接近内核模块的功能的基础上,具备上述的有点,得益于内核为eBPF提供的多个改变:
● 提供了高效的jit,eBPF的代码在内核中会被编译为机器码从而向真正的内核代码一样被执行。
● bpf()系统调用和map机制以及bpf helper辅助函数的提供,让内核操作更加便捷。
● 内核在高频通用的代码中提供了安全的注入点,让eBPF程序只需要关注于代码逻辑。
下图是eBPF程序的生命流程概述。
对于一个eBPF程序来说,他的完整的生命流程包括:
● 使用eBPF兼容的C语言子集进行代码开发,然后通过clang进行编译。
● 通过各种开发框架进行初步的检查,然后通过bpf()系统调用进行加载,在这个过程中会完成map的创建和替换以及符号的重定位。
● 加载到内核的过程中,内核的verifier会对程序进行校验,如果通过了校验,则可以被JIT编译为字节码然后被添加到指定的位置。
● 当内核代码执行到某个位置,内核会自动执行已经完成加载和attach的eBPF程序,例如socket的读取和写入会触发sockops这个执行点的eBPF程序的执行,完成流量的统计或者劫持动作。
eBPF在在安全,网络和可观测行上都有很广泛的应用,也诞生了许多影响力较大的开源项目。
Linux内核为提升可观测性而提供了多个可执行的点,我们常用的包括:
● kprobe,kretprobe,fentry,fexit 通过执行到某个函数是,触发INT3中断,使得eBPF程序可以在进程现场状态下观测到信息。
● tracepoint 内核的debugfs子系统提供的可供执行的点,内核在固定的tracepoint会执行注册的eBPF程序。
我们可以通过一些开源的项目很快体验到eBPF的价值,例如通过bpftrace,我们能够很快看到所有经过内核的数据报文在netfilter中的返回值:
bpftrace -e 'kretprobe:nf_hook_slow {printf("%d %s\n",retval,comm)}'
此外,eBPF在容器网络中也扮演着越来越关键的角色,包括cilium,terway,calico等多个知名的网络插件都开始使用eBPF来取代传统的netfilter框架等机制的作用,达到更加高效的抽象。