从入门到精通 eBPF 追踪
Jan 15, 2022 17:30 · 1886 words · 4 minute read
eBPF 全称是 extended Berkeley Packet Filter(扩展的伯克利包过滤器),可用于很多方向:网络性能、防火墙、安全、追踪和设备驱动。其中一些在网络上就能查到大量文档,比如追踪技术。追踪这个术语指的是性能分析;而追踪器是可观察性工具,用于生产相关事件的信息。我们经常使用的 tcpdump 和 strace 就是典型的追踪器。
这篇博客我将介绍学习 eBPF 追踪,面向初级、中级、高级用户。先抛结论:
初级
1. 什么是 eBPF、bcc、bpftrace 和 iovisor
eBPF 是 Linux 内核的一部分。eBPF 之于 Linux 相当于 JavaScript 对 HTML 的作用。JavaScript 让你定义小程序,监听鼠标点击这样的事件,运行在浏览器的安全虚拟机中,代替全静态的 HTML 网页。有了 eBPF,你现在可以编写小程序,在磁盘 I/O 等事件发生时呼起,在内核的安全虚拟机中运行。实际上 eBPF 更像是运行 JavaScript 的 v8 虚拟机,而非 JavaScript 本身。
直接用 eBPF 编程难的一比,和用 v8 字节码编码没啥两样。没人直接用 v8 编码——大家用 JavaScript,甚至基于 JavaScript 上层的框架(jQuery、Angular、React等)。eBPF 也如是,最好通过框架对其编码。追踪方面主流的是 bcc 和 bpftrace 这两个项目,但并不在内核的代码仓库中,而是由 github 上一个名为 iovisor 的 Linux 项目托管。
2. eBPF 追踪案例
tcplife 这款基于 eBPF 的工具展示了完整的 TCP 会话,包括了进程 ID(PID)和命令的名称(COMM),已发和接收的字节(TX_KB,RX_KB),还有持续时间(MS):
# tcplife
PID COMM LADDR LPORT RADDR RPORT TX_KB RX_KB MS
22597 recordProg 127.0.0.1 46644 127.0.0.1 28527 0 0 0.23
3277 redis-serv 127.0.0.1 28527 127.0.0.1 46644 0 0 0.28
22598 curl 100.66.3.172 61620 52.205.89.26 80 0 1 91.79
22604 curl 100.66.3.172 44400 52.204.43.121 80 0 1 121.38
22624 recordProg 127.0.0.1 46648 127.0.0.1 28527 0 0 0.22
3277 redis-serv 127.0.0.1 28527 127.0.0.1 46648 0 0 0.27
22647 recordProg 127.0.0.1 46650 127.0.0.1 28527 0 0 0.21
3277 redis-serv 127.0.0.1 28527 127.0.0.1 46650 0 0 0.26
[...]
eBPF 并不能实现所有功能——那得使用老的内核技术,但如果这么搞,出于性能和安全考虑,我们不能在生产环境中运行这样的工具。eBPF 所做的是让这个工具变得实用:既安全又高效:它不像旧技术那样追踪每一个数据包,这样会导致过多的性能开销;相反地,只追踪 TCP 会话事件,而这些事件的频率要低得多。这个工具的开销非常低,我们甚至可以在生产环境全天候(7 x 24)运行。
3. 如何使用
初学者直接尝试 bcc 软件包中的工具。查看 bcc 安装文档,每种 Linux 操作系统发行版的安装方式都不同。Ubuntu 呢只需要执行:
# sudo apt-get update
# sudo apt-get install bpfcc-tools
# sudo /usr/share/bcc/tools/opensnoop
PID COMM FD ERR PATH
25548 gnome-shell 33 0 /proc/self/stat
10190 opensnoop -1 2 /usr/lib/python2.7/encodings/ascii.x86_64-linux-gnu.so
10190 opensnoop -1 2 /usr/lib/python2.7/encodings/ascii.so
10190 opensnoop -1 2 /usr/lib/python2.7/encodings/asciimodule.so
10190 opensnoop 18 0 /usr/lib/python2.7/encodings/ascii.py
10190 opensnoop 19 0 /usr/lib/python2.7/encodings/ascii.pyc
25548 gnome-shell 33 0 /proc/self/stat
29588 device poll 4 0 /dev/bus/usb
^C
通过运行 opensnoop 来测试这些工具是否安装正确。走到了这一步,你就已经在使用 eBPF 了!
包括 Netflix 和 Facebook 在内的很多公司都会在所有服务器上默认安装好 bcc 工具包。
4. 初学者教程
Brendan Gregg 写的 bcc Tutorial,非常适合 eBPF 追踪技术的初学者。
初来乍到你无需编写 eBPF 代码,bcc 有 70 多个现成的工具拿来就用。
全都有完备的文档,示例文件(bcc/tools 的 *_example.txt)还带了不少截屏,比如 biolatency_example.txt。随着时间的推移,越来越多的生产案例会被添加到文档中。
中级
这时你应该已经尝试了这些工具,要是对定制和编写自己的工具感兴趣,最好的办法是切换到 bpftrace,它有一个更易于学习的高级语言。但缺点是不像 bcc 那样可以定制,所以你可能最终会碰到一些限制,想换回 bcc。
每种 Linux 操作系统发行版的安装方式有所不同,查看 bpftrace 安装文档。
1. bpftrace 教程
Brendan Gregg 写的 bpftrace One-Liners Tutorial,提供了 12 讲一点一点教你怎么用 bpftrace:
# bpftrace -e 'tracepoint:syscalls:sys_enter_open { printf("%d %s\n", pid, str(args->filename)); }'
Attaching 1 probe...
181 /proc/cpuinfo
181 /proc/stat
1461 /proc/net/dev
1461 /proc/net/if_inet6
^C
使用 open 系统调用的追踪点来追踪 PID 和打开的路径。
2. bpftrace 参考指南
如果想要深入了解 bpftrace,Brendan Gregg 写了一份参考指南 bpftrace Reference Guide,有相关语法、探针和内置程序的例子。编排后的目录链接至对应的内容,我觉得可以当成工具书来使用。
3. bpftrace 案例
在 bpftrace 的仓库中有 20 多种工具 https://github.com/iovisor/bpftrace/tree/master/tools。
举个栗子:
# cat tools/biolatency.bt
[...]
BEGIN
{
printf("Tracing block device I/O... Hit Ctrl-C to end.\n");
}
kprobe:blk_account_io_start
{
@start[arg0] = nsecs;
}
kprobe:blk_account_io_completion
/@start[arg0]/
{
@usecs = hist((nsecs - @start[arg0]) / 1000);
delete(@start[arg0]);
}
和 bcc 一样,这些工具都有说明书和示例文件。
高级
1. 学习 bcc 开发
这方面请先阅读以下两份文档:
bcc/tools/ 路径下有不少 Python 代码示例。bcc 工具由两部分组成:用 C 编写的内核 BPF 代码和用 Python(或者 lua 和 C++)编写的用户空间工具。开发 bcc 工具有点难,因为要深入内核或应用程序内部。
2. 为社区贡献
在这提交问题:
在 llvm IR 中编码是极其困难的,你想接受挑战也不是不可以。先看看这份指南 bpftrace Internals Development Guide。
还有内核中的 eBPF 引擎,浏览 bcc 和 bpftrace 的 issue 时能看到有些增强功能的请求。
重要的事再说一遍
Good luck!