eBPF in kubernetes 实战

2021年11月25日 阅读数:7
这篇文章主要向大家介绍eBPF in kubernetes 实战,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

背景

众所周知 eBPF 是很是有前景的项目,甚至成立了专门的基金会(https://ebpf.io/)来推进其生态的发展和标准化。java

关于 eBPF 的基础知识以前守仁也作过相关分享(【开发者社区】技术分享会内容整理) 所以再也不赘述。node

本文旨在探索 eBPF 和 kubernetes 结合时会有什么化学反应,以及如何结合现有工具链解决实际问题。python

涉及的相关开源项目主要以下:mysql

  • bcc
  • bpftrace
  • kubectl-trace
  • kubectl-flame
  • cilium

前置条件

kernel

eBPF 的概念很早就有了,所以其实一些功能在老版本的 kernel 下也是能够支持的,以下表所示,以 x86_64 体系为例,JIT 编译在 3.16 版本就支持了。git

image.png

但若是想正常使用体验/使用大部分功能的话,建议仍是升级到最新的 LTS 版本的内核。例如,目前使用的 CentOS 7.9 升级后使用的 kernel 版本是 5.15.4。github

如下连接展现了大部分 eBPF 依赖的功能的版本。sql

https://github.com/iovisor/bc...docker

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 的功能脚本。segmentfault

image.png

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,或直接使用官方的例子。

image.png

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

image.png

会展现出两种类型的 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 查询失败