eBPF是当前在云原生领域比较火,之前用wasm弄了个hello world,接下来是eBPF。这篇文章主要完成eBPF的Helloworld开发,eBPF的详细介绍这里不是重点,可以到官网看看
一、开发环境准备
看了一下相关资料,有些使用借助 Vagrant 、Multipass 等工具,来创建本地的虚拟机,作为运行环境,有使用docker镜像作为环境的。我这里使用VS Code的Remote Development来作为代码开发环境,毕竟作为一名coder,用IDE有感觉些。因为平常用的是Jetbrains全家桶,看了一下也支持SSH开发。不过VS Down下来的也不容易,试试吧。安装完成后,装一下Remote Development插件。
linux的环境,前段时候ucloud做活动时,买了个2C8G云主机,3年,够玩了。
登录上去安装eBPF开发依赖:
1 | sudo yum install libbpf-devel make clang llvm elfutils-libelf-devel bpftool bcc-tools bcc-devel |
一把安装时,发现libbpf-devel/bcc-devel装不上,yum repo找不到这两个东西。只好分开把make clang llvm elfutils-libelf-devel bpftool bcc-tools 这几个才装上。装完后,再google一下,搜索https://centos.pkgs.org/8-stream/centos-powertools-x86_64/bcc-devel-0.19.0-2.el8.x86_64.rpm.html,发现要用PowerTools这个库:
1 | dnf --enablerepo=powertools install libbpf-devel |
注意这里repo的名字要改一下,查一下本机的,改为:
1 | dnf --enablerepo=PowerTools install libbpf-devel |
还有报错:
1 | Error: Failed to download metadata for repo 'PowerTools': Cannot prepare internal mirrorlist: No URLs in mirrorlist |
从https://www.cnblogs.com/axinno1/p/15884647.html 这里找到解决方法:
1 | sudo sed -i -e "s|mirrorlist=|#mirrorlist=|g" /etc/yum.repos.d/CentOS-* |
再次安装,终于可以装上了。
1 | root@10-60-93-75 yum.repos.d]# dnf --enablerepo=PowerTools install libbpf-devel |
同样,bcc-devel也可以通过dnf –enablerepo=PowerTools install bcc-devel安装。
二、开发eBPF程序
开发环境准备好了以后,下面开始开发,这个例子也是来源于网上的资料,先不求甚解,RUN一把再说。
打开VS,用Remote功能连到云主机上,建一下代码目录。
先写一下c的helloworld函数,这个是给ebpf内核态使用的。
1 | int hello_world(void *ctx) |
再写一个用户态的调用函数,用python,这里会依赖bcc库
1 | #!/usr/bin/env python3 |
OK,写完了两货在VS里面的样子。
三、运行helloworld
RUN一把吧,不出意料,爆错了:
1 | modprobe: FATAL: Module kheaders not found in directory /lib/modules/4.18.0-240.1.1.el8_3.x86_64 |
又google了一把,应该是内核和安装的依赖不兼容,不符合BCC的要求:
1 | [root@10-60-93-75 ebpf-hello]# uname -r |
可以看到内核是4.18.0-240.1.1.el8_3.x86_64,kernel-devel的是4.18.0-240.el8.x86_64. 试了一下安装4.18.0-240.1.1.el8_3.x86_64的kernel-devel,找不到包。折腾了一阵,无解。想了想,干脆升级内核吧:
1 | yum update kernel -y |
查看一下内核,目测是个好内核:
1 | uname -r |
把旧的清理掉:
1 | [root@10-60-93-75 ~]# rpm -q kernel |
继续RUN,Module kheaders not found的错误解决了,不容易啊。又爆了一个错误:
1 | Exception: Failed to attach BPF program b'hello_world' to kprobe b'do_sys_openat2' |
这个查了一下,因为5.6 内核才支持openat,把它改成4.18支持的open方法。改一下python程序中的追踪内核函数为do_sys_open:
1 | b.attach_kprobe(event="do_sys_open", fn_name="hello_world") |
再RUN,大功告成了:
1 | [root@10-60-93-75 python]# /bin/python3 /home/codes/python/ebpf-hello/hello.py |
- <…>-37088 表示进程的名字和 PID,不知道是不是内核的原因,进程名显示<…>
- [006] 表示 CPU 编号;
- d… 表示一系列的选项;
- 57553.127898 表示时间戳;
- bpf_trace_printk 表示函数名;
- 最后的 “Hello, World!” 就是调用 bpf_trace_printk() 传入的字符串。
折腾了周末一天,可见,eBPF的HelloWorld不那么容易啊。
四、发生了什么
我们现在来看看发生了什么,下图是eBPF的开发和执行过程
- 第一步,使用 C 语言开发一个 eBPF 程序;
- 第二步,借助 LLVM 把 eBPF 程序编译成 BPF 字节码;
- 第三步,通过 bpf 系统调用,把 BPF 字节码提交给内核;
- 第四步,内核验证并运行 BPF 字节码,并把相应的状态保存到 BPF 映射中;
- 第五步,用户程序通过 BPF 映射查询 BPF 字节码的运行状态。
Helloworld借助了BCC(BPF Compiler Collection)进行编译和调用。BCC 是一个 BPF 编译器集合,包含了用于构建 BPF 程序的编程框架和库,并提供了大量可以直接使用的工具。使用 BCC 的好处是,它把上述的 eBPF 执行过程通过内置框架抽象了起来,并提供了 Python、C++ 等编程语言接口。这样,你就可以直接通过 Python 语言去跟 eBPF 的各种事件和数据进行交互。