0%

Hello,eBPF

eBPF是当前在云原生领域比较火,之前用wasm弄了个hello world,接下来是eBPF。这篇文章主要完成eBPF的Helloworld开发,eBPF的详细介绍这里不是重点,可以到官网看看

一、开发环境准备

看了一下相关资料,有些使用借助 Vagrant 、Multipass 等工具,来创建本地的虚拟机,作为运行环境,有使用docker镜像作为环境的。我这里使用VS Code的Remote Development来作为代码开发环境,毕竟作为一名coder,用IDE有感觉些。因为平常用的是Jetbrains全家桶,看了一下也支持SSH开发。不过VS Down下来的也不容易,试试吧。安装完成后,装一下Remote Development插件。

image-20220410171452471

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
2
sudo sed -i -e "s|mirrorlist=|#mirrorlist=|g" /etc/yum.repos.d/CentOS-*
sudo sed -i -e "s|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g" /etc/yum.repos.d/CentOS-*

再次安装,终于可以装上了。

1
2
3
4
5
6
7
8
9
root@10-60-93-75 yum.repos.d]# dnf --enablerepo=PowerTools install libbpf-devel
Last metadata expiration check: 0:00:31 ago on Sat 09 Apr 2022 06:10:45 PM CST.

.......

Installed:
libbpf-devel-0.4.0-1.el8.x86_64

Complete!

同样,bcc-devel也可以通过dnf –enablerepo=PowerTools install bcc-devel安装。

二、开发eBPF程序

开发环境准备好了以后,下面开始开发,这个例子也是来源于网上的资料,先不求甚解,RUN一把再说。

打开VS,用Remote功能连到云主机上,建一下代码目录。

先写一下c的helloworld函数,这个是给ebpf内核态使用的。

1
2
3
4
5
int hello_world(void *ctx)
{
bpf_trace_printk("Hello, World!");
return 0;
}

再写一个用户态的调用函数,用python,这里会依赖bcc库

1
2
3
4
5
6
7
8
9
#!/usr/bin/env python3
# 1) import bcc library
from bcc import BPF
# 2) load BPF program
b = BPF(src_file="hello.c")
# 3) attach kprobe
b.attach_kprobe(event="do_sys_open", fn_name="hello_world")
# 4) read and print /sys/kernel/debug/tracing/trace_pipe
b.trace_print()

OK,写完了两货在VS里面的样子。

image-20220410174017470

三、运行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
2
3
4
[root@10-60-93-75 ebpf-hello]# uname -r
4.18.0-240.1.1.el8_3.x86_64
[root@10-60-93-75 ebpf-hello]# rpm -qa|grep -i kernel | grep -i devel
kernel-devel-4.18.0-240.el8.x86_64

可以看到内核是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
2
yum update kernel -y
reboot

查看一下内核,目测是个好内核:

1
2
uname -r
4.18.0-348.7.1.el8_5.x86_64

把旧的清理掉:

1
2
3
4
[root@10-60-93-75 ~]# rpm -q kernel
kernel-4.18.0-240.1.1.el8_3.x86_64
kernel-4.18.0-348.7.1.el8_5.x86_64
[root@10-60-93-75 ~]# yum remove -y kernel-4.18.0-240.1.1.el8_3.x86_64

继续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
2
3
4
5
[root@10-60-93-75 python]# /bin/python3 /home/codes/python/ebpf-hello/hello.py
b' <...>-37088 [000] d... 57553.127898: bpf_trace_printk: Hello, World!'
b' <...>-37088 [000] d... 57553.128059: bpf_trace_printk: Hello, World!'
b' <...>-37088 [000] d... 57553.128307: bpf_trace_printk: Hello, World!'
b' <...>-37137 [000] d... 57569.064313: bpf_trace_printk: Hello, World!'
  • <…>-37088 表示进程的名字和 PID,不知道是不是内核的原因,进程名显示<…>
  • [006] 表示 CPU 编号;
  • d… 表示一系列的选项;
  • 57553.127898 表示时间戳;
  • bpf_trace_printk 表示函数名;
  • 最后的 “Hello, World!” 就是调用 bpf_trace_printk() 传入的字符串。

折腾了周末一天,可见,eBPF的HelloWorld不那么容易啊。

四、发生了什么

我们现在来看看发生了什么,下图是eBPF的开发和执行过程

img

  • 第一步,使用 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 的各种事件和数据进行交互。