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 in kubernetes 实战_第1张图片

但如果想正常使用体验/使用大部分功能的话,建议还是升级到最新的 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 in kubernetes 实战_第2张图片

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 in kubernetes 实战_第3张图片

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 in kubernetes 实战_第4张图片

会展示出两种类型的 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 并提供帮助文档),供开发使用。

例如以下场景,可以预先编写脚本支持。

  • mysql 慢查询
  • fd 泄漏
  • 内存泄漏
  • 频繁 gc
  • tcp 丢包
  • DNS 查询失败

你可能感兴趣的:(eBPF in kubernetes 实战)