聊聊最近很火的eBPF
來(lái)源:SegmentFault 思否社區(qū)
作者:iyacontrol

如果非要說(shuō)當(dāng)前計(jì)算機(jī)領(lǐng)域最有前途的兩個(gè)基礎(chǔ)軟件技術(shù),那非eBPF和wasm莫屬了。
什么是eBPF?
Linux內(nèi)核一直是實(shí)現(xiàn)監(jiān)視/可觀察性,網(wǎng)絡(luò)和安全性的理想場(chǎng)所。不幸的是,這通常是不切實(shí)際的,因?yàn)樗枰膬?nèi)核源代碼或加載內(nèi)核模塊,并導(dǎo)致彼此堆疊的抽象層。eBPF是一項(xiàng)革命性的技術(shù),可以在Linux內(nèi)核中運(yùn)行沙盒程序,而無(wú)需更改內(nèi)核源代碼或加載內(nèi)核模塊。通過使Linux內(nèi)核可編程,基礎(chǔ)架構(gòu)軟件可以利用現(xiàn)有的層,從而使它們更加智能和功能豐富,而無(wú)需繼續(xù)為系統(tǒng)增加額外的復(fù)雜性層。
eBPF導(dǎo)致了網(wǎng)絡(luò),安全性,應(yīng)用程序配置/跟蹤和性能故障排除等領(lǐng)域的新一代工具的開發(fā),這些工具不再依賴現(xiàn)有的內(nèi)核功能,而是在不影響執(zhí)行效率或安全性的情況下主動(dòng)重新編程運(yùn)行時(shí)行為。
如果直接解釋eBPF,有點(diǎn)不明所以。那我們就看看有哪些基于eBPF的工程,這些工程或許你已經(jīng)知道,或是已經(jīng)經(jīng)常使用,也許你會(huì)明白eBPF距離我們并不遙遠(yuǎn)。
基于eBPF的項(xiàng)目
1:bcc
BCC是用于創(chuàng)建基于eBPF的高效內(nèi)核跟蹤和操作程序的工具包,其中包括一些有用的命令行工具和示例。BCC簡(jiǎn)化了用C進(jìn)行內(nèi)核檢測(cè)的eBPF程序的編寫,包括LLVM的包裝器以及Python和Lua的前端。它還提供了用于直接集成到應(yīng)用程序中的高級(jí)庫(kù)。
2:bpftrace
bpftrace是Linux eBPF的高級(jí)跟蹤語(yǔ)言。它的語(yǔ)言受awk和C以及DTrace和SystemTap等以前的跟蹤程序的啟發(fā)。bpftrace使用LLVM作為后端將腳本編譯為eBPF字節(jié)碼,并利用BCC作為與Linux eBPF子系統(tǒng)以及現(xiàn)有Linux跟蹤功能和連接點(diǎn)進(jìn)行交互的庫(kù)。
3:Cilium
Cilium是一個(gè)開源項(xiàng)目,提供基于eBPF的聯(lián)網(wǎng),安全性和可觀察性。它是從頭開始專門設(shè)計(jì)的,旨在將eBPF的優(yōu)勢(shì)帶入Kubernetes的世界,并滿足容器工作負(fù)載的新可伸縮性,安全性和可見性要求。
4:Falco
Falco是一種行為活動(dòng)監(jiān)視器,旨在檢測(cè)應(yīng)用程序中的異常活動(dòng)。Falco在eBPF的幫助下審核Linux內(nèi)核層的系統(tǒng)。它使用其他輸入流(例如容器運(yùn)行時(shí)度量標(biāo)準(zhǔn)和Kubernetes度量標(biāo)準(zhǔn))豐富了收集的數(shù)據(jù),并允許連續(xù)監(jiān)視和檢測(cè)容器,應(yīng)用程序,主機(jī)和網(wǎng)絡(luò)活動(dòng)。
5:Katran
Katran是一個(gè)C ++庫(kù)和eBPF程序,用于構(gòu)建高性能的第4層負(fù)載平衡轉(zhuǎn)發(fā)平面。Katran利用Linux內(nèi)核中的XDP基礎(chǔ)結(jié)構(gòu)來(lái)提供用于快速數(shù)據(jù)包處理的內(nèi)核功能。它的性能與NIC接收隊(duì)列的數(shù)量成線性比例,并且使用RSS友好的封裝轉(zhuǎn)發(fā)到L7負(fù)載平衡器。

6:Sysdig
Sysdig是提供深層系統(tǒng)可見性的簡(jiǎn)單工具,并具有對(duì)容器的原生支持。
其他基于eBPF技術(shù)的項(xiàng)目還有很多,比如kubectl-trace ,ply 等,這里不再贅述。
如何編寫一個(gè)eBPF程序?
在很多情況下,不是直接使用eBPF,而是通過Cilium,bcc或bpftrace等項(xiàng)目間接使用eBPF,這些項(xiàng)目在eBPF之上提供了抽象,并且不需要直接編寫程序,而是提供了指定基于意圖的定義的功能,然后使用eBPF實(shí)施。
如果不存在更高級(jí)別的抽象,則需要直接編寫程序。Linux內(nèi)核希望eBPF程序以字節(jié)碼的形式加載。雖然當(dāng)然可以直接編寫字節(jié)碼,但更常見的開發(fā)實(shí)踐是利用LLVM之類的編譯器套件將偽C代碼編譯為eBPF字節(jié)碼。

在編寫eBPF程序之前,需要簡(jiǎn)單了解幾個(gè)概念。
1)map(映射) :BPF最令人著迷的方面之一是,內(nèi)核上運(yùn)行的代碼和加載了該代碼的程序可以在運(yùn)行時(shí)使用消息傳遞相互通信。
BPF映射是駐留在內(nèi)核中的鍵/值存儲(chǔ)。任何BPF程序都可以訪問它們。在用戶態(tài)中運(yùn)行的程序也可以使用文件描述符訪問這些映射。只要事先正確指定數(shù)據(jù)大小,就可以在映射中存儲(chǔ)任何類型的數(shù)據(jù)。內(nèi)核將鍵和值視為二進(jìn)制 blobs,它并不關(guān)心您在映射中保留的內(nèi)容。
BPF驗(yàn)證程序包括多種保護(hù)措施,以確保您創(chuàng)建和訪問映射的方式是安全的。當(dāng)我們解釋如何訪問這些映射中的數(shù)據(jù)時(shí),我們也將解釋這些保護(hù)措施。
當(dāng)然BPF映射類型有很多,比如哈希表映射,數(shù)組映射,Cgroup 數(shù)組映射等,分別滿足不同的場(chǎng)景。
2)驗(yàn)證器:BPF驗(yàn)證程序也是在您的系統(tǒng)上運(yùn)行的程序,因此,對(duì)其進(jìn)行嚴(yán)格審查是確保其正確執(zhí)行工作的目標(biāo)。
驗(yàn)證程序執(zhí)行的第一項(xiàng)檢查是對(duì)VM即將加載的代碼的靜態(tài)分析。第一次檢查的目的是確保程序有預(yù)期的結(jié)果。為此,驗(yàn)證程序?qū)⑹褂么a創(chuàng)建有向循環(huán)圖(DAG)。驗(yàn)證程序分析的每個(gè)指令將成為圖中的一個(gè)節(jié)點(diǎn),并且每個(gè)節(jié)點(diǎn)都鏈接到下一條指令。驗(yàn)證程序生成此圖后,它將執(zhí)行深度優(yōu)先搜索(DFS),以確保程序完成并且代碼不包含危險(xiǎn)路徑。這意味著它將遍歷圖的每個(gè)分支,一直到分支的底部,以確保沒有遞歸循環(huán)。
這些是驗(yàn)證器在第一次檢查期間可能拒絕您的代碼的情形,要求有以下幾個(gè)方面:
該程序不包含控制循環(huán)。為確保程序不會(huì)陷入無(wú)限循環(huán),驗(yàn)證程序會(huì)拒絕任何類型的控制循環(huán)。已經(jīng)提出了在BPF程序中允許循環(huán)的建議,但是截至撰寫本文時(shí),沒有一個(gè)被采用。 該程序不會(huì)嘗試執(zhí)行超過內(nèi)核允許的最大指令數(shù)的指令。此時(shí),可執(zhí)行的最大指令數(shù)為4,096。此限制是為了防止BPF永遠(yuǎn)運(yùn)行。在第3章,我們討論如何嵌套不同的BPF程序,以安全的方式解決此限制。 該程序不包含任何無(wú)法訪問的指令,例如從未執(zhí)行過的條件或功能。這樣可以防止在VM中加載無(wú)效代碼,這也會(huì)延遲BPF程序的終止。 該程序不會(huì)嘗試越界。
驗(yàn)證者執(zhí)行的第二項(xiàng)檢查是BPF程序的空運(yùn)行。這意味著驗(yàn)證者將嘗試分析程序?qū)⒁獔?zhí)行的每條指令,以確保它不會(huì)執(zhí)行任何無(wú)效的指令。此執(zhí)行還將檢查所有內(nèi)存指針是否均已正確訪問和取消引用。最后,空運(yùn)行向驗(yàn)證程序通知程序中的控制流,以確保無(wú)論程序采用哪個(gè)控制路徑,它都會(huì)到達(dá)BPF_EXIT指令。為此,驗(yàn)證程序會(huì)跟蹤堆棧中所有訪問過的分支路徑,并在采用新路徑之前對(duì)其進(jìn)行評(píng)估,以確保它不會(huì)多次訪問特定路徑。經(jīng)過這兩項(xiàng)檢查后,驗(yàn)證者認(rèn)為程序可以安全執(zhí)行。
3) hook :由于eBPF是事件驅(qū)動(dòng)的,所以ebpf是作用于具體的hook的。根據(jù)不同的作用,常用的有XDP,trace,套接字等。
4)幫助函數(shù):eBPF程序無(wú)法調(diào)用任意內(nèi)核功能。允許這樣做會(huì)將eBPF程序綁定到特定的內(nèi)核版本,并使程序的兼容性復(fù)雜化。取而代之的是,eBPF程序可以調(diào)用幫助函數(shù),該函數(shù)是內(nèi)核提供的眾所周知且穩(wěn)定的API。
總結(jié)
安全,網(wǎng)絡(luò),負(fù)載均衡,故障分析,追蹤等領(lǐng)域都是eBPF的主戰(zhàn)場(chǎng)。
對(duì)于云原生領(lǐng)域,Cilium 已經(jīng)使用eBPF 實(shí)現(xiàn)了無(wú)kube-proxy的容器網(wǎng)絡(luò)。利用eBPF解決iptables帶來(lái)的性能問題。
整個(gè)eBPF生態(tài)發(fā)展比較好,社區(qū)已經(jīng)提供了諸多工具方便大家編寫自己的eBPF程序。

