初探eBPF技術(shù)的強(qiáng)大
隨著G行應(yīng)用上全棧云越來越多,云上應(yīng)用的需求也越發(fā)變得多樣,對網(wǎng)絡(luò)、安全、可觀測性等各類需求也逐漸從傳統(tǒng)面向"點(diǎn)、線"的場景轉(zhuǎn)向了"面、分布式"的場景,對云上很多領(lǐng)域而言,eBPF這種新技術(shù)能提供簡單、便捷、快速的手段來實(shí)現(xiàn)各類工具、服務(wù)。本文對eBPF的概念、主要使用場景、使用方式進(jìn)行了簡單介紹,并結(jié)合全棧云實(shí)際運(yùn)維場景對eBPF技術(shù)進(jìn)行了實(shí)踐,拋磚引玉。
一、eBPF基本概念
傳統(tǒng)BPF工作方式: 基于事件驅(qū)動的框架,用戶使用BPF虛擬機(jī)指令集(RISC精簡指令集)定義過濾規(guī)則,然后傳遞給kernel再進(jìn)行JIT即時編譯成CPU原生指令,在事件被觸發(fā)時執(zhí)行。通過這種在kernel層實(shí)行過濾的方式,降低用戶層定義的過濾成本,提升包過濾性能。傳統(tǒng)的tcpdump就是這種過濾方式。
eBPF是在研究軟件定義網(wǎng)絡(luò)方案時擴(kuò)展出來的技術(shù),使BPF擴(kuò)展成為了一個更通用的虛擬機(jī),仍然是基于事件驅(qū)動的框架。eBPF patch在2014年3月開始合入kernel主分支中,JIT組件在2014年發(fā)布的Linux 3.15版本中被合入,應(yīng)用層控制BPF程序的bpf系統(tǒng)調(diào)用在Linux 3.18中被合入,接下來的Linux 4.x版本系列中添加了eBPF支持kprobes,uprobes,tracepoints和perf_events等事件類型。
eBPF相比傳統(tǒng)BPF技術(shù),寄存器從32位擴(kuò)展到64位,寄存器數(shù)量也擴(kuò)展到10個以上,擴(kuò)展了map技術(shù)實(shí)現(xiàn)內(nèi)核態(tài)與用戶態(tài)共享存儲空間用于高效讀取數(shù)據(jù),并從包過濾事件類型擴(kuò)展到動態(tài)插樁內(nèi)核函數(shù)、靜態(tài)插樁內(nèi)核函數(shù)、用戶函數(shù)插樁、性能監(jiān)控、安全等領(lǐng)域。當(dāng)前BPF名稱默認(rèn)即指eBPF。
二、eBPF內(nèi)部工作機(jī)制示意圖
圖1 eBPF工作示意圖
主要過程:
- 用戶編寫eBPF代碼,并使用LLVM、GCC等把代碼編譯成eBPF字節(jié)碼;
- 用戶態(tài)程序或工具通過bpf系統(tǒng)調(diào)用加載eBPF字節(jié)碼到kernel中;
- 內(nèi)核驗(yàn)證器驗(yàn)證字節(jié)碼是否合規(guī)、安全,確保不會造成kernel異常,驗(yàn)證通過后,內(nèi)核中的JIT即時編譯器把字節(jié)碼翻譯成CPU原生指令并加載到對應(yīng)的事件接口;
當(dāng)內(nèi)核中對應(yīng)事件被觸發(fā)時,執(zhí)行被加載的CPU原生指令,分析數(shù)據(jù)放入map共享區(qū)供用戶態(tài)程序使用。
三、eBPF重要應(yīng)用場景
1. eBPF定義網(wǎng)絡(luò)
在與Linux Kernel解耦的同時,通過eBPF可編程性及高效處理可以在Linux內(nèi)核包處理上下文中動態(tài)添加處理邏輯,包括過濾、流量控制、轉(zhuǎn)發(fā)、執(zhí)行路徑優(yōu)化、協(xié)議解析等幾乎任意操作,同時以近似于本地編譯的內(nèi)核代碼效率執(zhí)行。比如Linux內(nèi)核XDP(快速數(shù)據(jù)路徑)框架,通過在框架中掛載eBPF程序后,可實(shí)現(xiàn)三層路由轉(zhuǎn)發(fā)、四層負(fù)載均衡、分布式防火墻、訪問控制ACL等功能定制,可以編寫eBPF程序掛載到網(wǎng)卡驅(qū)動層直接處理網(wǎng)絡(luò)流量,繞過Linux Kernel,進(jìn)而可以使用專用的網(wǎng)絡(luò)處理器(NPU)進(jìn)行網(wǎng)絡(luò)流量處理,釋放CPU資源。開源社區(qū)比較典型的有facebook開源的Katran四層負(fù)載均衡器等。騰訊使用Cilium作為TKE底層引擎,阿里云使用eBPF技術(shù)實(shí)現(xiàn)CNI網(wǎng)卡。G行全棧云使用的DeepFlow流量采集和分析技術(shù)也使用了eBPF技術(shù)。
2. eBPF定義安全
除了早期基于bpf技術(shù)實(shí)現(xiàn)的內(nèi)核運(yùn)行時安全計(jì)算模型Seccomp和LSM Linux安全模塊之外,業(yè)界有很多基于eBPF技術(shù)來高效靈活實(shí)現(xiàn)網(wǎng)絡(luò)安全策略,比如Flacon異常行為檢測工具;容器網(wǎng)絡(luò)領(lǐng)域的開源項(xiàng)目Cilium,重度使用eBPF技術(shù)來實(shí)現(xiàn)云原生場景下的三層/四層/七層網(wǎng)絡(luò)安全策略等,在不更改應(yīng)用程序代碼或容器配置的情況下能夠發(fā)布和更新 Cilium 安全策略;用于Linux的運(yùn)行時安全和取證工具Tracee,使用Linux eBPF 技術(shù)在運(yùn)行時跟蹤系統(tǒng)和應(yīng)用程序,收集事件并分析檢測可疑行為模式。
3. eBPF可觀測性和實(shí)時跟蹤
Netflix公司基于eBPF實(shí)現(xiàn)生產(chǎn)環(huán)境tracing, AWS公司使用eBPF作為RPC觀測工具,國內(nèi)互聯(lián)網(wǎng)巨頭字節(jié)跳動使用eBPF技術(shù)實(shí)現(xiàn)主機(jī)可觀測性和ACL訪問控制等。
網(wǎng)絡(luò)包全鏈路排查開源工具pwru(package where r u)是基于 eBPF 開發(fā)的網(wǎng)絡(luò)數(shù)據(jù)包排查工具,提供了完整的細(xì)粒度網(wǎng)絡(luò)數(shù)據(jù)包排查方案 (kernel版本需大于5.5)。
四、 eBPF主要使用方式
1. 使用C語言、Go語言等編程語言編寫原始eBPF程序,實(shí)現(xiàn)邏輯控制、觀測跟蹤等功能,具體可參考社區(qū)教程。
2. 使用高階封裝工具 BCC編寫eBPF觀測跟蹤程序。為了降低BPF程序開發(fā)門檻,社區(qū)發(fā)起了BCC項(xiàng)目,提供簡單易用的編寫、加載和運(yùn)行eBPF程序的一個框架,并可以通過Python、Lua等腳本語言來編寫。除此之外還提供了很多現(xiàn)成的用于對內(nèi)核、CPU、內(nèi)存、調(diào)度、網(wǎng)段等子系統(tǒng)的觀測跟蹤,參考https://github.com/iovisor/bcc。
3. 使用高階封裝工具bpftrace編寫eBPF觀測跟蹤程序。通過命令行就能實(shí)現(xiàn)eBPF性能觀測工具,更加簡化eBPF使用,用于追蹤、調(diào)試Linux kernel、了解kernel運(yùn)行機(jī)制非常有用,缺點(diǎn)是不能調(diào)用內(nèi)核函數(shù)或者自定義函數(shù)(此類場景需要使用BCC或C、GO語言開發(fā)),可參考https://github.com/iovisor/bpftrace
五、 G行全棧云Caas環(huán)境下eBPF技術(shù)初體驗(yàn)案例一:全棧云hyper主機(jī)ping延時高
在全棧云某些hyper物理機(jī)上,發(fā)現(xiàn)ping 127.0.0.1延時高(圖2)
圖2 ping延時高
通過perf性能分析工具分別對正常ping、有ping延時進(jìn)程分別進(jìn)行trace采樣,制作成火焰圖,分析出耗時部分。
正常ping(圖3):
圖3 正常ping火焰圖
異常ping(延時大,圖4):
圖4 異常ping火焰圖
在火焰圖里可以看到,相比正常ping,延時高ping過程在try_to_wakeup_up()調(diào)用過程中耗費(fèi)較大。
為了進(jìn)一步搞清ping延時過程中try_to_wakeup_up具體是什么情況,編寫bpftrace kprobe類型程序掛載到try_to_wakeup_up內(nèi)核函數(shù):
#include <linux/sched.h>
kprobe:try_to_wake_up
/ pid == $1 /
{
$task = ((struct task_struct *) arg0);
$pid = $task->pid;
printf("from %s -> wakeup comm %s pid %d\n", comm, $task->comm, $pid);
}
用bpftrace執(zhí)行此eBPF程序,監(jiān)控ping進(jìn)程被try_to_wakeup_up的詳細(xì)過程(圖5):
圖5 eBPF tracing數(shù)據(jù)
從圖中我們可以看到,在ping的過程中,try_to_wakeup_up頻繁喚醒isc-socket進(jìn)程,經(jīng)分析,此為dhcpd相關(guān)進(jìn)程,是IaaS層分布式虛擬路由dvr master與client之間的處理邏輯。把dhcpd相關(guān)進(jìn)程遷移到別的機(jī)器后,本機(jī)器上的ping 127.0.0.1延時恢復(fù)正常。
從以上案例可以看出,eBPF具備強(qiáng)大的可觀測性和實(shí)時跟蹤能力,可以很容易根據(jù)場景定制出合適的trace能力,對于觀測定位kernel、進(jìn)程的運(yùn)行邏輯十分便利。
案例二:觀測收發(fā)包主要過程耗時
在全棧云CaaS環(huán)境下,各業(yè)務(wù)以pod的形式運(yùn)行在自己的namespace中,如果不同pod之間通信偶爾抖動變慢,如何判斷是網(wǎng)卡、協(xié)議棧、應(yīng)用層等哪個環(huán)節(jié)出現(xiàn)了問題?傳統(tǒng)的tcpdump抓包工具(底層基于了classic bpf庫)抓包位置在軟中斷從網(wǎng)卡隊(duì)列(ring buffer)中讀取數(shù)據(jù)后發(fā)送給協(xié)議棧的時候,只能從tcpdump看到sequence數(shù)據(jù)包在網(wǎng)卡接口處收發(fā)的時間,在正常情況下無法直觀看到更深層次的延時原因,比如是內(nèi)核處理延時還是用戶態(tài)延時?
如果我們知道veth驅(qū)動收發(fā)包關(guān)鍵kernel函數(shù),以及協(xié)議棧處理與veth驅(qū)動的銜接點(diǎn),就可以編寫eBPF程序掛載到這些關(guān)鍵函數(shù)入口或出口處,在可通過kprobe或者tracepoint在協(xié)議棧各層的關(guān)鍵函數(shù)中添加hook點(diǎn),當(dāng)數(shù)據(jù)包經(jīng)過該函數(shù)時,打印出seq、network namespace、時間戳等關(guān)鍵信息,幫助我們快速定位或者縮小問題范圍。
本文模擬node節(jié)點(diǎn)某塊虛擬網(wǎng)卡延時(圖6),此時node節(jié)點(diǎn)上與pod節(jié)點(diǎn)(與延時虛擬網(wǎng)卡配對)如何判斷是網(wǎng)卡慢還是協(xié)議棧處理慢?
圖6 測試環(huán)境
首先需要分析出此場景下eBPF程序合適的kernel掛載點(diǎn),基于bpftrace工具編寫eBPF程序并進(jìn)行觀測跟蹤體驗(yàn):
1. veth發(fā)送關(guān)鍵內(nèi)核函數(shù):
__dev_queue_xmit(將數(shù)據(jù)發(fā)送到驅(qū)動層)
在該掛載點(diǎn),獲取tcp四元組信息,獲取tcp sequence,并使用全局變量保存接收時間(納秒)@rcvpkg[$seq] = nsecs;
2. veth接收關(guān)鍵內(nèi)核函數(shù):
__netif_receive_skb(將報文收到協(xié)議棧)
tcp_rcv_state_process(tcp狀態(tài)機(jī)處理函數(shù))
tcp_rcv_established(tcp establish過程處理)
選取上面3個示例掛載點(diǎn),獲取tcp四元組信息,獲取tcp sequence與當(dāng)前時間,減去__dev_queue_xmit記錄的起始時間,就可以得到發(fā)送到接收、協(xié)議棧主要處理函數(shù)耗時,對于超過一定時間的可以進(jìn)行告警打印。
if( ($seq) == @sequence ){
$delta = ((nsecs - @rcvpkg[$seq]) / 1000000) % 1000;
if( $delta >= $1 ){
time("\n%H:%M:%S ");
printf("%-19u %-5s %d,%s,%s,%-10d ", $nsid, $netif, pid, comm, func, cpu);
printf("flags:%s, seq:%-u, ack:%u, win:%-25u ", $pkgflag, $seq, $ack, $win);
printf("%s:%-15d %s:%-15d %d ms\n", $srcip, $sport, $dstip, $dport, (nsecs / 1000000) % 1000);
printf("Slow pkg: duratinotallow=%u ms, seq=%-u\n", $delta, $seq);
}
}
模擬網(wǎng)絡(luò)延時:
tc qdisc add dev vnice9657d91c32 root netem delay 10ms
tc qdisc add dev vnicb8898168feb root netem delay 20ms
在node節(jié)點(diǎn)上執(zhí)行eBPF程序:
bpftrace netpod.bt 5 > tt
在node節(jié)點(diǎn)上執(zhí)行測試命令:
curl 30.254.10.7:8099;
nsenter -n -t 20720 telnet 30.254.10.6 22627
eBPF捕獲數(shù)據(jù)如下,可以看出tc設(shè)置的延時是在xmit發(fā)送時候產(chǎn)生的,接收方及tcp協(xié)議棧處理耗時正常(圖7-1,圖7-2)。
圖7-1 eBPF tracing網(wǎng)絡(luò)延時數(shù)據(jù)
圖7-2 eBPF tracing網(wǎng)絡(luò)延時數(shù)據(jù)
六、eBPF演進(jìn)趨勢展望
eBPF技術(shù)像是一個任意門,可以隨意穿梭到你想去的地方進(jìn)行探索甚至改造,在云時代大顯身手。Linux kernel面臨不斷增長的復(fù)雜度、性能、可擴(kuò)展、向后兼容性等需求,需要保持kernel漸進(jìn)式發(fā)展,更多新功能、新特性無法及時合并到kernel中。eBPF的內(nèi)核可編程性,既能保證安全,又能在不改變kernel代碼的情況下實(shí)現(xiàn)新功能、新特性的快速應(yīng)用,可為kernel的發(fā)展提供tick-tock迭代新方案,可以想象未來kernel的發(fā)展極有可能在eBPF技術(shù)基礎(chǔ)上實(shí)現(xiàn)軟件定義kernel。對于全棧云平臺而言,可以跟進(jìn)eBPF技術(shù)發(fā)展,研究eBPF適用的應(yīng)用場景,更好支持云上應(yīng)用。