eBPF in kubernetes 實戰

FingerLiu發表於2021-11-24

背景

眾所周知 eBPF 是非常有前景的專案,甚至成立了專門的基金會(https://ebpf.io/)來推動其生態的發展和標準化。

關於 eBPF 的基礎知識之前守仁也做過相關分享(【開發者社群】技術分享會內容整理) 因此不再贅述。

本文旨在探索 eBPF 和 kubernetes 結合時會有什麼化學反應,以及如何結合現有工具鏈解決實際問題。

涉及的相關開源專案主要如下:

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

前置條件

kernel

eBPF 的概念很早就有了,因此其實一些功能在老版本的 kernel 下也是可以支援的,如下表所示,以 x86_64 體系為例,JIT 編譯在 3.16 版本就支援了。

image.png

但如果想正常使用體驗/使用大部分功能的話,建議還是升級到最新的 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 的功能指令碼。

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 查詢失敗

相關文章