eBPF|eBPF in kubernetes 实战

背景 众所周知 eBPF 是非常有前景的项目,甚至成立了专门的基金会(https://ebpf.io/)来推动其生态的发展和标准化。
关于 eBPF 的基础知识之前守仁也做过相关分享(【开发者社区】技术分享会内容整理) 因此不再赘述。
本文旨在探索 eBPF 和 kubernetes 结合时会有什么化学反应,以及如何结合现有工具链解决实际问题。
涉及的相关开源项目主要如下:

  • bcc
  • bpftrace
  • kubectl-trace
  • kubectl-flame
  • cilium
前置条件 kernel eBPF 的概念很早就有了,因此其实一些功能在老版本的 kernel 下也是可以支持的,如下表所示,以 x86_64 体系为例,JIT 编译在 3.16 版本就支持了。
eBPF|eBPF in kubernetes 实战
文章图片

但如果想正常使用体验/使用大部分功能的话,建议还是升级到最新的 LTS 版本的内核。例如,目前使用的 CentOS 7.9 升级后使用的 kernel 版本是 5.15.4。
以下链接展示了大部分 eBPF 依赖的功能的版本。
https://github.com/iovisor/bc...
kernel header 一些功能依赖 kernel header 中的头文件,所有需要安装和 kernel 对应版本的 kernel header。
相关项目简介 bcc
BCC makes BPF programs easier to write, with kernel instrumentation in C (and includes a C wrapper around LLVM),
and front-ends in Python and lua. It is suited for many tasks, including performance analysis and network traffic control.
提供了一套易用的编程接口,使开发者可以在无需详细 kernel 代码(近千万行代码)的情况下用 python 或 lua 编写基于 eBPF 的功能脚本。
eBPF|eBPF in kubernetes 实战
文章图片

bpftrace
bpftrace is a high-level tracing language for Linux enhanced Berkeley Packet Filter (eBPF) available in recent Linux kernels (4.x).
bpftrace uses LLVM as a backend to compile scripts to BPF-bytecode and makes use of BCC for interacting with the Linux BPF system,
as well as existing Linux tracing capabilities: kernel dynamic tracing (kprobes), user-level dynamic tracing (uprobes), and tracepoints.
bpftrace 构建在 bcc 之上,借鉴 C/awk 实现了一套 DSL,比较适合些一些 one-liner 简单的命令来监控或 trace,或直接使用官方的例子。
eBPF|eBPF in kubernetes 实战
文章图片

kubectl-trace eBPF 的 kubectl 插件,能够对 node/pod 等 k8s 资源使用 bpftrace 监控。最新的版本 v0.1.2 (2021.7)还仅支持 bpftrace,未来的版本会同时支持 bpftrace 和 bcc(代码已合入主分支但还没 release)。
整体使用下来体验比较顺畅。
kubectl-flame yahoo 开源的为程序提供火焰图的 kubectl 插件,官方说是可以在不更改业务程序的情况 attach 到业务容器进行分析。
试验后社区版本 bug 较多,无法顺畅运行,且其实是对业务容器,包括 JDK 有依赖的,体验一般,感觉较难大范围落地。
clilium 使用 eBPF 技术来提升网络转发性能、提升可观测性的 kubernetes 网络插件。
Get Your Hands Dirty 以下实验使用的 CentOS 7,内核版本为 4.4。
更新 kernel
yum -y update rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-3.el7.elrepo.noarch.rpm yum --disablerepo="*" --enablerepo="elrepo-kernel" list available

eBPF|eBPF in kubernetes 实战
文章图片

会展示出两种类型的 kernel, lt 和 ml。其中 lt 表示 LongTerm,类似 Ubuntu 的 LTS; ml 表示 MainLine,选择哪个都可以,但装 header 时需对应。这里选择 ml。
yum --enablerepo=elrepo-kernel install -y kernel-ml

安装 header 安装之前需要先删除老的 headers。如果之前有安装 header, 再安装可能会报错不兼容。
yum remove kernel-headers

安装新内核
yum --enablerepo=elrepo-kernel install -y kernel-ml-headers

使用新内核
查看当前所有可用内核
$ sudo awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg 0 : CentOS Linux (4.18.7-1.el7.elrepo.x86_64) 7 (Core) 1 : CentOS Linux (3.10.0-862.11.6.el7.x86_64) 7 (Core) 2 : CentOS Linux (3.10.0-514.el7.x86_64) 7 (Core) 3 : CentOS Linux (0-rescue-063ec330caa04d4baae54c6902c62e54) 7 (Core)

编辑 grub 修改默认内核,重启机器。
grub2-set-default 0 reboot

安装 bcc/bpftrace
# bcc yum install bcc-tools# bpftrace curl https://repos.baslab.org/rhel/7/bpftrace-daily/bpftrace-daily.repo --output /etc/yum.repos.d/bpftools.repo curl https://repos.baslab.org/rhel/7/bpftools/bpftools.repo --output /etc/yum.repos.d/bpftrace-daily.repo yum install bpftrace bpftrace-tools bpftrace-doc

安装 kubectl-trace 与 bcc btftrace 需要在每台宿主机执行不同, kubectl-trace 是客户端插件,在执行 kubectl 的客户端机器安装即可。
kubectl krew install trace

使用 bcc
git clone https://github.com/iovisor/bcc.git cd ./bcc/tools/ # 查看某 java 进程的 gc 事件 ./javagc.sh -l java 24682

使用 bpftrace
git clone https://github.com/iovisor/bpftrace.git cd ./bpftrace/tools/ # 查看 DNS 解析请求的延迟 bpftrace gethostlatency.bt

这里需要注意的是,官方的这个 tools 中引用的 libc.so 路径是 hardcode 的,可能会报错(https://github.com/iovisor/bp...),可以按需改成正确的 libc.so 路径。(后面这个问题应该会修复)
kubectl trace node 不管是 node 还是 pod, trace 都是在对应的节点启动一个 job,然后针对宿主机,或者 attach 到 pod 的容器中进行探测。
这里有个 bug,就是对应 CentOS 的环境,即便我在宿主机已经安装了 kernel-headers, 这里还是需要 --fetch-headers 才能执行成功。
k trace run node1 -e "tracepoint:syscalls:sys_enter_* { @[probe] = count(); }" --fetch-headers

还有个问题是 --fetch-headers 可能会从外网拉取 tar 包,根据网络环境情况可能拉取失败。解决办法是预先拉取下来,然后参考官网手动 build 一个 initContainer 的镜像即可,如下例。
k trace run node1 -e "tracepoint:syscalls:sys_enter_* { @[probe] = count(); }" --fetch-headers k trace run -nkube-system pod/calico-kube-controllers-7d5d95c8c9-mkp54 -e "tracepoint:syscalls:sys_enter_* { @[probe] = count(); }" --fetch-headers --init-imagename=docker.4pd.io/tmp/kubectl-trace-init:5.15.4 kubectl trace pod

kubectl trace pod k trace run -nkube-system pod/calico-kube-controllers-7d5d95c8c9-mkp54 -e "tracepoint:syscalls:sys_enter_* { @[probe] = count(); }" --fetch-headers
Future Step 不管是原生 eBPF, 还是 bcc、bpftrace,使用时其实还是有一定门槛的,因此需要根据实际情况按照场景封装对应的脚(或复用官方 tool 并提供帮助文档),供开发使用。
【eBPF|eBPF in kubernetes 实战】例如以下场景,可以预先编写脚本支持。
  • mysql 慢查询
  • fd 泄漏
  • 内存泄漏
  • 频繁 gc
  • tcp 丢包
  • DNS 查询失败

    推荐阅读