BPF简介,以及使用kubectl trace插件为kubernetes集群运行BPF程序

0x00 文章楔子

BPF(Berkeley Packet Filter)本质上是一个运行在内核态的虚拟机,使得用户可以在内核中加载运行自定义程序的技术,其构想在1992年通过一篇论文被提出,最初被用于优化tcpdump包过滤效率。2013年开始,社区对BPF进行了大刀阔斧的改进,使其重获生机,并一跃成为了近几年Linux社区中最受人追捧的“新”技术之一。
改进后的BPF被称为eBPF,e代表extended,而旧的BPF则被保留,并改名为cBPF,c代表classical。

现在人们再提起BPF时,通常指的都是改进后的eBPF,本文也意在介绍eBPF,因此我在本文标题中,以及接下来的篇幅中将直接用BPF代指eBPF。

本文将简述BPF的历史及其原理,最后提供一个实例,指导如何在kubernetes集群中使用kubectl trace插件运行BPF程序。

0x01 What is BPF

2001年,Linux2.5版本将BPF的第一个实现合并到了内核主干,之后直到2011年的很长一段时间里,BPF的代码都没有发生大的变化。然而从2013年的Linux3.18开始,Alexei Starovoitov基于BPF的思想对原来的BPF架构进行了改进,发布了一个新的BPF JIT(Just-In-Time)引擎,为扩充BPF的功能创造了条件。随后,新架构下的BPF逐渐演进,并扩充了大量的新特性,直到Linux4.9,改进后的BPF功能已经不再局限于处理网络包,而是可以附着到kprobe,uprobe,dtrace,seccomp之上,对NIC,用户态调用,系统调用,IO等进行精细的观测乃至控制。

如文章开头所述,BPF技术本质上是一个运行在内核态的虚拟机。它最初的目的是为了避免包过滤时从内核态到用户态的数据复制,因此提供了一种方法让用户在内核态中定义处理数据包,这种方法就是BPF JIT虚拟机。它允许用户注册一段受到严格限制的自定义代码到BPF上,这段代码通过了Verifier检测后,JIT会将其编译为机器码,并将其插桩到特定的内核代码上,实现类似于探针的效果。

0x01 Why is BPF

初出茅庐的BPF在几年的时间里崭露头角,迅速的获得了大量公司、社区的簇拥。 Linux社区计划使用BPF重新实现iptables; Facebook使用BPF做负载均衡器,抵御DDoS攻击;Netflix使用BPF进行程序的tracing和profiling;P4,Open vSwitch正在考虑使用BPF;CETH, Cilium则是完全基于BPF设计的,Cilium还利用BPF实现了一些激动人心的优化

为什么BPF会在改进后收获如此大量的关注?

在没有BPF之前,用户只能选择在内核的特定领域进行有限的控制(如iptables、ipvs、ipset),或者只能通过编写内核模块(如KProbes)甚至重新编译内核(如Tracepoints)来控制内核的行为,开发门槛较高,并且难以保证内核的安全。

而BPF提供了一种统一的方法,让用户可以在不重新编译内核的情况下,在内核中动态地(on the fly)插入一段自己的代码。这使得用户可以用同一种范式在几乎任何位置对内核的行为进行监控和一定程度上的控制。并且这些代码只有通过了BPF的检查后才会被加载,保障了内核不会因为用户失误或者恶意代码而陷入故障或Panic。

通过这种特性,开发人员甚至可以利用hardware offload将执行防火墙规则的处理单元从CPU变更为网卡上的NPU,由网卡来执行防火墙规则,从而获得近10x的网络包处理量。

0x02 How is BPF

典型的BPF程序是一段看起来非常像x86汇编的字节码,通过BPF loader被加载到内核空间,然后经过Verifier的检查后,再被JIT编译为机器码,并插桩到内核中,等待被调用。

IO Visor在此基础上,基于LLVM/clang后端编写了一个BPF字节码编译器,可以将用户编写的类C语法的tracing脚本编译为BPF字节码。

如,下面一段代码来自bpftrace/tools/bashreadline.bt,可以经由bpftrace被编译为追踪bash执行命令的BPF程序。

#!/usr/bin/env bpftrace
BEGIN
{
	printf("Tracing bash commands... Hit Ctrl-C to end.\n");
	printf("%-9s %-6s %s\n", "TIME", "PID", "COMMAND");
}

uretprobe:/bin/bash:readline
{
	time("%H:%M:%S  ");
	printf("%-6d %s\n", pid, str(retval));
}

0x03 What is kubectl-trace

kubectl-trace是IO Visor开源的,帮助用户在Kubernetes集群中安排执行BPF程序的kubectl插件,安装kubectl-trace非常简单,直接在kubectl所在机器上执行

go get -u github.com/iovisor/kubectl-trace/cmd/kubectl-trace

等待命令执行完毕后,kubectl trace子命令就可以使用了。

下面给出一个示例的执行过程,首先创建一个bpftrace程序文件read.bt,然后使用kubectl trace run指定一个节点来执行这个程序,在我的例子中,节点名字为“micro-server-76-114”,最后attach到这个trace上,按Ctrl + C停止追踪,并打印追踪的结果。

$ cat > read.bt <

更多bpftrace程序可以在bpftrac/tools中找到

0xFF 参考文档

  1. The BSD Packet Filter
  2. A brief introduction to XDP and eBPF
  3. Why is the kernel community replacing iptables with BPF?
  4. Golang bcc/BPF Function Tracing
  5. Accelerating Envoy with the Linux Kernel
  6. BPF: Tracing and More
  7. Cilium: Network and Application Security with BPF and XDP
文章已创建 23

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部