eBPF 基本架構(gòu)及使用

eBPF 介紹
Tcpdump 是 Linux 平臺(tái)常用的網(wǎng)絡(luò)數(shù)據(jù)包抓取及分析工具,tcpdump 主要通過(guò) libpcap 實(shí)現(xiàn),而 libpcap 就是基于 eBPF。
先介紹 BPF(Berkeley Packet Filter),BPF 是基于寄存器虛擬機(jī)實(shí)現(xiàn)的,支持 JIT(Just-In-Time),比基于棧實(shí)現(xiàn)的性能高很多。它能載入用戶態(tài)代碼并且在內(nèi)核環(huán)境下運(yùn)行,內(nèi)核提供 BPF 相關(guān)的接口,用戶可以將代碼編譯成字節(jié)碼,通過(guò) BPF 接口加載到 BPF 虛擬機(jī)中,當(dāng)然用戶代碼跑在內(nèi)核環(huán)境中是有風(fēng)險(xiǎn)的,如有處理不當(dāng),可能會(huì)導(dǎo)致內(nèi)核崩潰。因此在用戶代碼跑在內(nèi)核環(huán)境之前,內(nèi)核會(huì)先做一層嚴(yán)格的檢驗(yàn),確保沒(méi)問(wèn)題才會(huì)被成功加載到內(nèi)核環(huán)境中。
eBPF(extended Berkeley Packet Filter)起源于BPF,它提供了內(nèi)核的數(shù)據(jù)包過(guò)濾機(jī)制。其擴(kuò)充了 BPF 的功能,豐富了指令集。
最初,eBPF 僅在內(nèi)核內(nèi)部使用,并且 cBPF 程序在幕后無(wú)縫轉(zhuǎn)換。但是隨著 2014 年的 daedfb22451d 提交,eBPF 虛擬機(jī)直接暴露給用戶空間。

eBPF 分用戶空間和內(nèi)核空間,用戶空間和內(nèi)核空間的交互有 2 種方式:
BPF map:統(tǒng)計(jì)摘要數(shù)據(jù) perf-event:用戶空間獲取實(shí)時(shí)監(jiān)測(cè)數(shù)據(jù)
如上,一般 eBPF 的工作邏輯是:
BPF Program 通過(guò) LLVM/Clang 編譯成 eBPF 定義的字節(jié)碼 prog.bpf。 通過(guò)系統(tǒng)調(diào)用 bpf() 將 bpf 字節(jié)碼指令傳入內(nèi)核中。 經(jīng)過(guò) verifier 檢驗(yàn)字節(jié)碼的安全性、合規(guī)性。 在確認(rèn)字節(jié)碼安全后將其加載對(duì)應(yīng)的內(nèi)核模塊執(zhí)行,通過(guò) Helper/hook 機(jī)制,eBPF 與內(nèi)核可以交換數(shù)據(jù)/邏輯。BPF 觀測(cè)技術(shù)相關(guān)的程序程序類型可能是 kprobes/uprobes/tracepoint/perf_events 中的一個(gè)或多個(gè),其中:
kprobes:實(shí)現(xiàn)內(nèi)核中動(dòng)態(tài)跟蹤。kprobes 可以跟蹤到 Linux 內(nèi)核中的函數(shù)入口或返回點(diǎn),但是不是穩(wěn)定 ABI 接口,可能會(huì)因?yàn)閮?nèi)核版本變化導(dǎo)致,導(dǎo)致跟蹤失效。理論上可以跟蹤到所有導(dǎo)出的符號(hào) /proc/kallsyms。
uprobes:用戶級(jí)別的動(dòng)態(tài)跟蹤。與 kprobes 類似,只是跟蹤的函數(shù)為用戶程序中的函數(shù)。
tracepoints:內(nèi)核中靜態(tài)跟蹤。tracepoints 是內(nèi)核開(kāi)發(fā)人員維護(hù)的跟蹤點(diǎn),能夠提供穩(wěn)定的 ABI 接口,但是由于是研發(fā)人員維護(hù),數(shù)量和場(chǎng)景可能受限。
perf_events:定時(shí)采樣和 PMC。
用戶空間通過(guò) BPF map 與內(nèi)核通信。
eBPF 可以做什么
eBPF 主要功能列表
| 特性 | 引入版本 | 功能介紹 | 應(yīng)用場(chǎng)景 |
|---|---|---|---|
| Tc-bpf | 4.1 | eBPF 重構(gòu)內(nèi)核流分類 | 網(wǎng)絡(luò) |
| XDP | 4.8 | 網(wǎng)絡(luò)數(shù)據(jù)面編程技術(shù)(主要面向 L2/L3 層業(yè)務(wù)) | 網(wǎng)絡(luò) |
| Cgroup socket | 4.10 | Cgroup 內(nèi) socket 支持 eBPF 擴(kuò)展邏輯 | 容器 |
| AF_XDP | 4.18 | 網(wǎng)絡(luò)原始報(bào)文直送用戶態(tài)(類似 DPDK) | 網(wǎng)絡(luò) |
| Sockmap | 4.20 | 支持 socket 短接 | 容器 |
| Device JIT | 4.20 | JIT/ISA 解耦,host 可以編譯指定 device 形態(tài)的 ISA 指令 | 異構(gòu)編程 |
| Cgroup sysctl | 5.2 | Cgroup 內(nèi)支持控制系統(tǒng)調(diào)用權(quán)限 | 容器 |
| Struct ops Prog ext | 5.3 | 內(nèi)核邏輯可動(dòng)態(tài)替換 eBPF Prog 可動(dòng)態(tài)替換 | 框架基礎(chǔ) |
| Bpf trampoline | 5.5 | 三種用途:1.內(nèi)核中代替 K(ret)probe,性能更優(yōu) 2.eBPF Prog 中使用,解決 eBPF Prog 調(diào)試問(wèn)題 3.實(shí)現(xiàn) eBPF Prog 動(dòng)態(tài)鏈接功能(未來(lái)功能) | 性能跟蹤 |
| KRSI(lsm + eBPF) | 5.7 | 內(nèi)核運(yùn)行時(shí)安全策略可定制 | 安全 |
| Ring buffer | 5.8 | 提供 CPU 間共享的環(huán)形 buffer,并能實(shí)現(xiàn)跨 CPU 的事件保序記錄。用以代替 perf/ftrace 等 buffer。 | 跟蹤/性能分析 |
eBPF 在 Linux 3.18 版本以后引入,并不代表只能在內(nèi)核 3.18+ 版本上運(yùn)行,低版本的內(nèi)核升級(jí)到最新也可以使用 eBPF 能力,只是可能部分功能受限,比如我就是在 Linux 發(fā)行版本 CentOS Linux release 7.7.1908 內(nèi)核版本 3.10.0-1062.9.1.el7.x86_64 上運(yùn)行 eBPF 在生產(chǎn)環(huán)境上搜集和排查網(wǎng)絡(luò)問(wèn)題。
和內(nèi)核模塊對(duì)比
| 維度 | Linux 內(nèi)核模塊 | eBPF |
|---|---|---|
| kprobes/tracepoints | 支持 | 支持 |
| 安全性 | 可能引入安全漏洞或?qū)е聝?nèi)核 Panic | 通過(guò)驗(yàn)證器進(jìn)行檢查,可以保障內(nèi)核安全 |
| 內(nèi)核函數(shù) | 可以調(diào)用內(nèi)核函數(shù) | 只能通過(guò) BPF Helper 函數(shù)調(diào)用 |
| 編譯性 | 需要編譯內(nèi)核 | 不需要編譯內(nèi)核,引入頭文件即可 |
| 運(yùn)行 | 基于相同內(nèi)核運(yùn)行 | 基于穩(wěn)定 ABI 的 BPF 程序可以編譯一次,各處運(yùn)行 |
| 與應(yīng)用程序交互 | 打印日志或文件 | 通過(guò) perf_event 或 map 結(jié)構(gòu) |
| 數(shù)據(jù)結(jié)構(gòu)豐富性 | 一般 | 豐富 |
| 入門門檻 | 高 | 低 |
| 升級(jí) | 需要卸載和加載,可能導(dǎo)致處理流程中斷 | 原子替換升級(jí),不會(huì)造成處理流程中斷 |
| 內(nèi)核內(nèi)置 | 視情況而定 | 內(nèi)核內(nèi)置支持 |
eBPF 的使用場(chǎng)景
網(wǎng)絡(luò)場(chǎng)景
在網(wǎng)絡(luò)加速場(chǎng)景中,DPDK 技術(shù)大行其道,在某些場(chǎng)景 DPDK 成了唯一選擇。XDP 的出現(xiàn)為廠商提供了一種新的選擇,借助于 kernel eBPF 社區(qū)的蓬勃發(fā)展,為網(wǎng)絡(luò)加速場(chǎng)景注入了一股清流。下面我們總結(jié)下兩種差異:
DPDK 優(yōu)勢(shì)/價(jià)值:優(yōu)勢(shì)(性能、生態(tài))、價(jià)值(帶動(dòng)硬件銷售)
性能:總體上 XDP 性能全面弱于 DPDK(但是差距不大),注意:只是比較 DPDK/XDP 自身性能 生態(tài):DPDK 歷經(jīng)多年發(fā)展,生態(tài)體現(xiàn)在:驅(qū)動(dòng)支持豐富、基礎(chǔ)庫(kù)豐富(無(wú)鎖隊(duì)列、大頁(yè)內(nèi)存、多核調(diào)度、性能分析工具等)、協(xié)議支持豐富(社區(qū)強(qiáng)大,例如 VPP,支持眾多協(xié)議 ARP/VLAN/IP/MPLS 等) 價(jià)值:將網(wǎng)絡(luò)類專有硬件的工作轉(zhuǎn)嫁給軟件實(shí)現(xiàn),進(jìn)而拓展硬件廠商市場(chǎng)范圍。 XDP 優(yōu)勢(shì):可編程、內(nèi)核協(xié)同工作
可編程:在網(wǎng)絡(luò)硬件智能化趨勢(shì)下,可編程可以適用多種場(chǎng)景。 內(nèi)核協(xié)同:XDP 并不是完全 bypass kernel,所以在必要的時(shí)候可以與內(nèi)核協(xié)同工作,利于網(wǎng)絡(luò)統(tǒng)一管理、部署。 DPDK 一些固有缺陷:
獨(dú)占 Device:設(shè)備利用率低。
部署復(fù)雜:由于獨(dú)占 Device,網(wǎng)絡(luò)部署需要與 OS 協(xié)議棧協(xié)同部署。
開(kāi)發(fā)困難:DPDK 定位就是網(wǎng)絡(luò)數(shù)據(jù)面開(kāi)發(fā)包,所以它對(duì)使用者要求具備專業(yè)網(wǎng)絡(luò)知識(shí)、專業(yè)硬件知識(shí),所以入門門檻高。
端到端性能不高:DPDK 只是提供數(shù)據(jù)包從 NIC 到用戶態(tài)軟件的零拷貝,但是用戶態(tài)傳輸協(xié)議依然需要 CPU 參與。所以端到端性能不高。
進(jìn)階閱讀 Polycube 項(xiàng)目。
容器場(chǎng)景
背景:云原生場(chǎng)景中容器比虛擬化技術(shù)有著更好的低底噪、輕便、易管理等優(yōu)點(diǎn),基本已經(jīng)成為云原生應(yīng)用的事實(shí)標(biāo)準(zhǔn)。容器場(chǎng)景對(duì)網(wǎng)絡(luò)需求實(shí)際是應(yīng)用對(duì)網(wǎng)絡(luò)的需求,即面向應(yīng)用的網(wǎng)絡(luò)服務(wù)。
云原生應(yīng)用特點(diǎn)以及對(duì)網(wǎng)絡(luò)的訴求:
生命周期短:要求提供基于 PoD 靜態(tài)身份信息實(shí)施的網(wǎng)絡(luò)安全策略。
(不能基于 IP/Port) 租戶間隔離:要求提供 API 級(jí)別的網(wǎng)絡(luò)隔離策略。
ServiceMesh 拓?fù)涔芾恚阂筇峁?side-car 加速。
服務(wù)入口位置透明:要求提供跨集群 Ingress 服務(wù)能力。
安全策略跨集群:要求網(wǎng)絡(luò)安全策略能夠在集群間共享、繼承。
服務(wù)實(shí)例冗余保證高可用性:要求提供 L3/4 層 LB 能力。
進(jìn)階閱讀 Cilium 項(xiàng)目。
安全場(chǎng)景
背景:Linux 系統(tǒng)的運(yùn)行安全始終是在動(dòng)態(tài)平衡中,系統(tǒng)安全性通常要評(píng)估兩方面的契合度:signals(系統(tǒng)中一些異?;顒?dòng)跡象)、mitigation(針對(duì) signals 的一些補(bǔ)救措施)。
內(nèi)核中的 signal/mitigation 設(shè)置散布在多個(gè)地方,配置時(shí)費(fèi)時(shí)費(fèi)力。
解決方案:引入 eBPF,提供一些 eBPF Helper 實(shí)現(xiàn)“unified policy API”,由 API 來(lái)統(tǒng)一配置 signal 和 mitigation。

eBPF 的使用
eBPF 提供多種使用方式:BCC、BPFTrace、libbpf C/C++ Library、eBPF GO library 等
更早期的工具使用 C 語(yǔ)言來(lái)編寫 BPF 程序,使用 LLVM clang 編譯成 BPF 代碼,這對(duì)于普通使用者上手有不少門檻當(dāng)前僅限于對(duì)于 eBPF 技術(shù)更加深入的學(xué)習(xí)場(chǎng)景。
對(duì)于大多數(shù)開(kāi)發(fā)者而言,更多的是基于 BPF 技術(shù)之上編寫解決我們?nèi)粘S龅降母鞣N問(wèn)題。
BCC 和 BPFTrace 作為 BPF 的兩個(gè)前端,當(dāng)前這兩個(gè)項(xiàng)目在觀測(cè)和性能分析上已經(jīng)有了諸多靈活且功能強(qiáng)大的工具箱,完全可以滿足我們?nèi)粘J褂谩?/p>
BCC 提供了更高階的抽象,可以讓用戶采用 Python、C++ 和 Lua 等高級(jí)語(yǔ)言快速開(kāi)發(fā) BPF 程序 BPFTrace 采用類似于 awk 語(yǔ)言快速編寫 eBPF 程序
libbpf C/C++ Library
基于 libbpf C/C++ library 的開(kāi)發(fā)架構(gòu)如下:

獲取 libbpf:
$ git clone https://github.com/libbpf/libbpf
$ cd libbpf/src
$ make -j8 && make install
原生 C Hello world
參考:https://github.com/bpftools/linux-observability-with-bpf/tree/master/code/chapter-2/hello_world
$ git clone https://github.com/bpftools/linux-observability-with-bpf
$ cd linux-observability-with-bpf/code/chapter-2/hello_world
獲取內(nèi)核源碼,將 Makefile 中 kenel-src 路徑替換為實(shí)際內(nèi)核源碼路徑
$ make
make 后會(huì)創(chuàng)建 BPF ELF bpf-program.o 及 Loader monitor-exec
這時(shí)執(zhí)行
$ ./monitor-exec
將 bpf 指令加載至內(nèi)核。
之后,執(zhí)行任意的 execve 系統(tǒng)調(diào)用都會(huì)打印:Hello, BPF World!
如執(zhí)行 ls:


此時(shí)可以看到 BPF 程序打印出 Hello, BPF World!
注意:centos 默認(rèn) yum 安裝的 clang 版本是 3.4,不支持 tagert bpf,需要升級(jí) clang 至 3.9
BCC 的安裝及使用
bcc 即 BPF Compiler Collection,bcc 是一個(gè)關(guān)于 BPF 技術(shù)的工具集。

以 CentOS 7 為例
安裝
Linux 3.15 開(kāi)始引入 eBPF,而又因?yàn)閎cc 在5以上的內(nèi)核版本中存在bug(https://github.com/iovisor/bcc/issues/2329),建議將內(nèi)核升級(jí)至4+,如lt 版本4.19.
升級(jí) Linux 內(nèi)核
因?yàn)槎鄶?shù) elrepo 中的 kernel 版本默認(rèn)是最新的 5.4 或 5.12 等,可以直接下載 4.19 的 kernel rpm 包本地安裝;
rpm 包參考:https://buildlogs.centos.org/c7-kernels.x86_64/kernel/20190918210642/4.19.72-300.el7.x86_64/
下載 rpm 包至本地:
kernel-4.19.72-300.el7.x86_64.rpm
kernel-core-4.19.72-300.el7.x86_64.rpm
kernel-modules-4.19.72-300.el7.x86_64.rpm
kernel-headers-4.19.72-300.el7.x86_64.rpm
本地安裝:
$ yum localinstall kernel-core-4.19.72-300.el7.x86_64.rpm kernel-4.19.72-300.el7.x86_64.rpm kernel-modules-4.19.72-300.el7.x86_64.rpm kernel-headers-4.19.72-300.el7.x86_64.rpm
更新 Grub 后重啟:
$ grub2-mkconfig -o /boot/grub2/grub.cfg
$ awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg
0 : CentOS Linux (5.2.8-1.el7.elrepo.x86_64) 7 (Core)
1 : CentOS Linux (3.10.0-862.14.4.el7.x86_64) 7 (Core)
$ grub2-set-default 0
$ reboot
重新登錄后確認(rèn)當(dāng)前內(nèi)核版本
$ grub2-editenv list uname -r
安裝 bcc-tools
$ yum install -y bcc-tools
$ export PATH=$PATH:/usr/share/bcc/tools
使用 bcc-tools
如對(duì)于一些生命周期很短的進(jìn)程很難通過(guò) top 工具去監(jiān)測(cè),這是可以通過(guò) execsnoop 去監(jiān)測(cè):

BCC 的程序一般情況下都需要 root 用戶或 sudo 來(lái)運(yùn)行。
BCC hello world
BCC 前端綁定語(yǔ)言 Python
#!/usr/bin/python3
from bcc import BPF
# This may not work for 4.17 on x64, you need replace kprobe__sys_clone with kprobe____x64_sys_clone
prog = """
int kprobe__sys_clone(void *ctx) {
bpf_trace_printk("Hello, World!\\n");
return 0;
}
"""
b = BPF(text=prog, debug=0x04)
b.trace_print()1.2.3.4.5.6.7.8.9.10.11.12.13.14.
其中,
text='...':自定義的C 代碼 BPF 程序。kprobe__sys_clone():通過(guò) kprobes 執(zhí)行內(nèi)核動(dòng)態(tài)追蹤的捷徑。以kprobe__為前綴的C函數(shù),被當(dāng)作內(nèi)核函數(shù)名使用,本文是``sys_clone()。void *ctx: ctx 傳遞參數(shù),當(dāng)前不傳遞參數(shù)則使用void *。bpf_trace_printk():一個(gè)簡(jiǎn)單的內(nèi)核工具,用于 printf 輸出至 trace_pipe (/sys/kernel/debug/tracing/trace_pipe)。對(duì)于一些簡(jiǎn)單的用法是沒(méi)問(wèn)題的,不過(guò)有三個(gè)限制:最多 3 個(gè)參數(shù)、只有 1%s、trace_pipe 全局共享,所以當(dāng)前程序的輸出會(huì)有不清晰的情況。更好的接口是利用 BPF_PERF_OUTPUT(),而后覆蓋。return 0;: 必要的步驟 (參考 #139)。.trace_print(): 常規(guī)的 bcc 代碼,讀取 trace_pipe 并打印輸出。
輸出:bash-21720 是 ls,11789 是執(zhí)行 C BPF 程序 ./monitor-exec

BPFTrace
BPFTrace 使用 LLVM 將腳本編譯成 BPF 二進(jìn)制碼,后續(xù)使用 BCC 與 Linux 內(nèi)核進(jìn)行交互。
從功能層面上講,BPFTrace 的定制性和靈活性不如 BCC,但是比 BCC 工具更加易于理解和使用,降低了 BPF 技術(shù)的使用門檻。
# 獲取bpftrace 源碼:
$ git clone https://github.com/iovisor/bpftrace
$ cd bpftrace
$ mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=Release ..
$ make -j8 && make install
# 統(tǒng)計(jì)內(nèi)核中函數(shù)堆棧的次數(shù)
$ bpftrace -e 'profile:hz:99 { @[kstack] = count(); }'
Further Reading
https://github.com/xdp-project/xdp-tutorial
eBPF 發(fā)展歷程
1992 年:BPF 全稱 Berkeley Packet Filter,誕生初衷提供一種內(nèi)核中自定義報(bào)文過(guò)濾的手段(類匯編),提升抓包效率。(tcpdump) 2011 年:linux kernel 3.2 版本對(duì) BPF 進(jìn)行重大改進(jìn),引入 BPF JIT,使其性能得到大幅提升。 2014 年:linux kernel 3.15 版本,BPF 擴(kuò)展成 eBPF,其功能范疇擴(kuò)展至:內(nèi)核跟蹤、性能調(diào)優(yōu)、協(xié)議棧 QoS 等方面。與之配套改進(jìn)包括:擴(kuò)展 BPF ISA 指令集、提供高級(jí)語(yǔ)言(C)編程手段、提供 MAP 機(jī)制、提供 Help 機(jī)制、引入 Verifier 機(jī)制等。 2016 年:linux kernel 4.8 版本,eBPF 支持 XDP,進(jìn)一步拓展該技術(shù)在網(wǎng)絡(luò)領(lǐng)域的應(yīng)用。隨后 Netronome 公司提出 eBPF 硬件卸載方案。 2018 年:linux kernel 4.18 版本,引入 BTF,將內(nèi)核中 BPF 對(duì)象(Prog/Map)由字節(jié)碼轉(zhuǎn)換成統(tǒng)一結(jié)構(gòu)對(duì)象,這有利于 eBPF 對(duì)象與 Kernel 版本的配套管理,為 eBPF 的發(fā)展奠定基礎(chǔ)。 2018 年:從 kernel 4.20 版本開(kāi)始,eBPF 成為內(nèi)核最活躍的項(xiàng)目之一,新增特性包括:sysctrl hook、flow dissector、struct_ops、lsm hook、ring buffer 等。場(chǎng)景范圍覆蓋容器、安全、網(wǎng)絡(luò)、跟蹤等。
原文鏈接:https://blog.51cto.com/dengchj/2944202


你可能還喜歡
點(diǎn)擊下方圖片即可閱讀

云原生是一種信仰 ??
關(guān)注公眾號(hào)
后臺(tái)回復(fù)?k8s?獲取史上最方便快捷的 Kubernetes 高可用部署工具,只需一條命令,連 ssh 都不需要!


點(diǎn)擊 "閱讀原文" 獲取更好的閱讀體驗(yàn)!
發(fā)現(xiàn)朋友圈變“安靜”了嗎?


