容器網(wǎng)絡(luò)|深入理解Cilium

本文翻譯自 2019 年 DigitalOcean 的工程師 Nate Sweet 在 KubeCon 的一篇分享:
Understanding (and Troubleshooting) the eBPF Datapath in Cilium 。
由于水平有限,本文不免存在遺漏或錯誤之處。如有疑問,請查閱原文。
1 為什么要關(guān)注 eBPF?
1.1 網(wǎng)絡(luò)成為瓶頸
1.2 eBPF 無處不在
1.3 性能就是金錢
2 eBPF 是什么?
3 為什么 eBPF 如此強(qiáng)大?
3.1 快速
3.2 靈活
3.3 數(shù)據(jù)與功能分離
4 eBPF 簡史
5 Cilium 是什么,為什么要關(guān)注它?
6 內(nèi)核數(shù)據(jù)通路?datapath
7 Kubernets、Cilium 和 Kernel:原子對象對應(yīng)關(guān)系
1.1 網(wǎng)絡(luò)成為瓶頸
大家已經(jīng)知道網(wǎng)絡(luò)成為瓶頸,但我是從下面這個角度考慮的:近些年業(yè)界使用網(wǎng)絡(luò)的方式 ,使其成為瓶頸(it is the bottleneck in a way that is actually pretty recent) 。
網(wǎng)絡(luò)一直都是 I/O 密集型的,但直到最近,這件事情才變得尤其重要。
分布式任務(wù)(workloads)業(yè)界一直都在用,但直到近些年,這種模型才成為主流。?雖然何時成為主流眾說紛紜,但我認(rèn)為最早不會早于 90 年代晚期。
公有云的崛起,我認(rèn)為可能是網(wǎng)絡(luò)成為瓶頸的最主要原因。
這種情況下,用于管理依賴和解決瓶頸的工具都已經(jīng)過時了。
但像 eBPF 這樣的技術(shù)使得網(wǎng)絡(luò)調(diào)優(yōu)和整流(tune and shape this traffic)變得簡單很多。?eBPF 提供的許多能力是其他工具無法提供的,或者即使提供了,其代價也要比 eBPF 大 的多。
1.2 eBPF 無處不在
eBPF 正在變得無處不在,我們可能會爭論這到底是一件好事還是壞事(eBPF 也確實帶了一 些安全問題),但當(dāng)前無法忽視的事實是:Linux 內(nèi)核的網(wǎng)絡(luò)開發(fā)者們正在將 eBPF 應(yīng)用 于各種地方(putting it everywhere)。其結(jié)果是,eBPF 與內(nèi)核的默認(rèn)收發(fā)包路徑( datapath)耦合得越來越緊(more and more tightly coupled with the default datapath)。
1.3 性能就是金錢
“Metrics are money”, 這是今年 Paris Kernel Recipes 峰會上,來自 Synthesio 的 Aurelian Rougemont 的 精彩分享。
他展示了一些史詩級的調(diào)試(debugging)案例,感興趣的可以去看看;但更重要的是,他 從更高層次提出了這樣一個觀點(diǎn):理解這些東西是如何工作的,最終會產(chǎn)生資本收益( understanding how this stuff works translates to money)。為客戶節(jié)省金錢,為 自己帶來收入。
如果你能從更少的資源中榨取出更高的性能,使軟件運(yùn)行更快,那 顯然你對公司的貢獻(xiàn)就更大。Cilium 就是這樣一個能讓你帶來更大價值的工具。
在進(jìn)一步討論之前,我先簡要介紹一下 eBPF 是什么,以及為什么它如此強(qiáng)大。
BPF 程序有多種類型,圖 2.1 是其中一種,稱為 XDP BPF 程序。
XDP 是?eXpress DataPath(特快數(shù)據(jù)路徑)。
XDP 程序可以直接加載到網(wǎng)絡(luò)設(shè)備上。
XDP 程序在數(shù)據(jù)包收發(fā)路徑上很前面的位置就開始執(zhí)行,下面會看到例子。
BPF 程序開發(fā)方式:
編寫一段 BPF 程序
編譯這段 BPF 程序
用一個特殊的系統(tǒng)調(diào)用將編譯后的代碼加載到內(nèi)核
這實際上就是編寫了一段內(nèi)核代碼,并動態(tài)插入到了內(nèi)核(written kernel code and dynamically inserted it into the kernel)。

圖 2.1. eBPF 代碼示例:丟棄源 IP 命中黑名單的 ARP 包
圖 2.1 中的程序使用了一種稱為?map?的東西,這是一種特殊的數(shù)據(jù)結(jié)構(gòu),可用于?在內(nèi)核和用戶態(tài)之間傳遞數(shù)據(jù),例如通過一個特殊的系統(tǒng)從用戶態(tài)向 map 里插入數(shù)據(jù)。
這段程序的功能:丟棄所有源 IP 命中黑名單的 ARP 包。右側(cè)四個框內(nèi)的代碼功能:
初始化以太幀結(jié)構(gòu)體(ethernet packet)。
如果不是 ARP 包,直接退出,將包交給內(nèi)核繼續(xù)處理。
至此已確定是 ARP,因此初始化一個 ARP 數(shù)據(jù)結(jié)構(gòu),對包進(jìn)行下一步處理。例 如,提取出 ARP 中的源 IP,去之前創(chuàng)建好的黑名單中查詢該 IP 是否存在。
如果存在,返回丟棄判決(
XDP_DROP);否則,返回允許通行判決(?XDP_PASS),內(nèi)核會進(jìn)行后續(xù)處理。
你可能不會相信,就這樣一段簡單的程序,會讓服務(wù)器性能產(chǎn)生質(zhì)的飛躍,因為它此時已 經(jīng)擁有了一條極為高效的網(wǎng)絡(luò)路徑(an extremely efficient network path)。
三方面原因:
快速(fast)
靈活(flexible)
數(shù)據(jù)與功能分離(separates data from functionality)
3.1 快速
eBPF 幾乎總是比 iptables 快,這是有技術(shù)原因的。
eBPF 程序本身并不比 iptables 快,但 eBPF 程序更短。
iptables 基于一個非常龐大的內(nèi)核框架(Netfilter),這個框架出現(xiàn)在內(nèi)核 datapath 的多個地方,有很大冗余。
因此,同樣是實現(xiàn) ARP drop 這樣的功能,基于 iptables 做冗余就會非常大,導(dǎo)致性能很低。
3.2 靈活
這可能是最主要的原因。你可以用 eBPF 做幾乎任何事情。
eBPF 基于內(nèi)核提供的一組接口,運(yùn)行 JIT 編譯的字節(jié)碼,并將計算結(jié)果返回給內(nèi)核。例如?內(nèi)核只關(guān)心 XDP 程序的返回是 PASS, DROP 還是 REDIRECT。至于在 XDP 程序里做什么, 完全看你自己。
3.3 數(shù)據(jù)與功能分離
eBPF separates data from functionality.
nftables?和?iptables?也能干這個事情,但功能沒有 eBPF 強(qiáng)大。例如,eBPF 可以使 用 per-cpu 的數(shù)據(jù)結(jié)構(gòu),因此能取得更極致的性能。
eBPF 真正的優(yōu)勢是將“數(shù)據(jù)與功能分離”這件事情做地非常干凈(clean separation):可以在 eBPF 程序不中斷的情況下修改它的運(yùn)行方式。具體方式是修改它訪 問的配置數(shù)據(jù)或應(yīng)用數(shù)據(jù),例如黑名單里規(guī)定的 IP 列表和域名。

這里是簡單介紹幾句,后面 datapath 才是重點(diǎn)。
兩篇論文,可讀性還是比較好的,感興趣的自行閱讀:
Steven McCanne, et al, in 1993 -?The BSD Packet Filter
Jeffrey C. Mogul, et al, in 1987 - first open source implementation of a packet filter.
我認(rèn)為理解 eBPF 代碼還比較簡單,多看看內(nèi)核代碼就行了,但配置和編寫 eBPF 就要難多了。
Cilium 是一個很好的 eBPF 之上的通用抽象,覆蓋了分布式系統(tǒng)的絕大多數(shù)場景。Cilium 封裝了 eBPF,提供一個更上層的 API。如果你使用的是 Kubernetes,那你至少應(yīng)該聽說過 Cilium。
Cilium 提供了 CNI 和 kube-proxy replacement 功能,相比 iptables 性能要好很多。
接下來開始進(jìn)入本文重點(diǎn)。
本節(jié)將介紹數(shù)據(jù)包是如何穿過 network datapath(網(wǎng)絡(luò)數(shù)據(jù)路徑)的:包括從硬件到 內(nèi)核,再到用戶空間。
這里將只介紹?Cilium 所使用的 eBPF 程序,其中有 Cilium logo 的地方,都是 datapath 上 Cilium 重度使用 BPF 程序的地方。
本文不會過多介紹硬件相關(guān)內(nèi)容,因為理解 eBPF 基本不需要硬件知識,但顯然理解了硬件 原理也并無壞處。另外,由于時間限制,我將只討論接收部分。
6.1 L1 -> L2(物理層 -> 數(shù)據(jù)鏈路層)

網(wǎng)卡收包簡要流程:
網(wǎng)卡驅(qū)動初始化。
網(wǎng)卡獲得一塊物理內(nèi)存,作用收發(fā)包的緩沖區(qū)(ring-buffer)。這種方式稱為 DMA(直接內(nèi)存訪問)。
驅(qū)動向內(nèi)核 NAPI(New API)注冊一個輪詢(poll )方法。
網(wǎng)卡從云上收到一個包,將包放到 ring-buffer。
如果此時 NAPI 沒有在執(zhí)行,網(wǎng)卡就會觸發(fā)一個硬件中斷(HW IRQ),告訴處理器 DMA 區(qū)域中有包等待處理。
收到硬中斷信號后,處理器開始執(zhí)行 NAPI。
NAPI 執(zhí)行網(wǎng)卡注冊的 poll 方法開始收包。
關(guān)于 NAPI poll 機(jī)制:
這是 Linux 內(nèi)核中的一種通用抽象,任何等待不可搶占狀態(tài)發(fā)生(wait for a preemptible state to occur)的模塊,都可以使用這種注冊回調(diào)函數(shù)的方式。
驅(qū)動注冊的這個 poll 是一個主動式 poll(active poll),一旦執(zhí)行就會持續(xù)處理 ,直到?jīng)]有數(shù)據(jù)可供處理,然后進(jìn)入 idle 狀態(tài)。
在這里,執(zhí)行 poll 方法的是運(yùn)行在某個或者所有 CPU 上的內(nèi)核線程(kernel thread)。?雖然這個線程沒有數(shù)據(jù)可處理時會進(jìn)入 idle 狀態(tài),但如前面討論的,在當(dāng)前大部分分布 式系統(tǒng)中,這個線程大部分時間內(nèi)都是在運(yùn)行的,不斷從驅(qū)動的 DMA 區(qū)域內(nèi)接收數(shù)據(jù)包。
poll 會告訴網(wǎng)卡不要再觸發(fā)硬件中斷,使用軟件中斷(softirq)就行了。此后這些 內(nèi)核線程會輪詢網(wǎng)卡的 DMA 區(qū)域來收包。之所以會有這種機(jī)制,是因為硬件中斷代價太 高了,因為它們比系統(tǒng)上幾乎所有東西的優(yōu)先級都要高。
我們接下來還將多次看到這個廣義的 NAPI 抽象,因為它不僅僅處理驅(qū)動,還能處理許多 其他場景。內(nèi)核用 NAPI 抽象來做驅(qū)動讀取(driver reads)、epoll 等等。
NAPI 驅(qū)動的 poll 機(jī)制將數(shù)據(jù)從 DMA 區(qū)域讀取出來,對數(shù)據(jù)做一些準(zhǔn)備工作,然后交給比 它更上一層的內(nèi)核協(xié)議棧。
6.2 L2 續(xù)(數(shù)據(jù)鏈路層 - 續(xù))
同樣,這里不會深入展開驅(qū)動層做的事情,而主要關(guān)注內(nèi)核所做的一些更上層的事情,例如
分配 socket buffers(skb)
BPF
iptables
將包送到網(wǎng)絡(luò)棧(network stack)和用戶空間
Step 1:NAPI poll

首先,NAPI poll 機(jī)制不斷調(diào)用驅(qū)動實現(xiàn)的 poll 方法,后者處理 RX 隊列內(nèi)的包,并最終 將包送到正確的程序。這就到了我們前面的 XDP 類型程序。
Step 2:XDP 程序處理

如果驅(qū)動支持 XDP,那 XDP 程序?qū)⒃?poll 機(jī)制內(nèi)執(zhí)行。如果不支持,那 XDP 程序?qū)⒅荒?strong style="outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">在更后面執(zhí)行(run significantly upstack,見 Step 6),性能會變差, 因此確定你使用的網(wǎng)卡是否支持 XDP 非常重要。
XDP 程序返回一個判決結(jié)果給驅(qū)動,可以是 PASS, TRANSMIT, 或 DROP。
Transmit 非常有用,有了這個功能,就可以用 XDP?實現(xiàn)一個 TCP/IP 負(fù)載均衡器。XDP 只適合對包進(jìn)行較小修改,如果是大動作修改,那這樣的 XDP 程序的性能 可能并不會很高,因為這些操作會降低 poll 函數(shù)處理 DMA ring-buffer 的能力。
更有趣的是 DROP 方法,因為一旦判決為 DROP,這個包就可以直接原地丟棄了,而 無需再穿越后面復(fù)雜的協(xié)議棧然后再在某個地方被丟棄,從而節(jié)省了大量資源。如果本次 分享我只能給大家一個建議,那這個建議就是:在 datapath 越前面做 tuning 和 dropping 越好,這會顯著增加系統(tǒng)的網(wǎng)絡(luò)吞吐。
如果返回是 PASS,內(nèi)核會繼續(xù)沿著默認(rèn)路徑處理包,到達(dá)?
clean_rx()?方法。
Step 3:clean_rx():創(chuàng)建 skb

如果返回是 PASS,內(nèi)核會繼續(xù)沿著默認(rèn)路徑處理包,到達(dá)?clean_rx()?方法。
這個方法創(chuàng)建一個 socket buffer(skb)對象,可能還會更新一些統(tǒng)計信息,對 skb 進(jìn)行硬件校驗和檢查,然后將其交給?gro_receive()?方法。
Step 4:gro_receive()

GRO 是一種較老的硬件特性(LRO)的軟件實現(xiàn),功能是對分片的包進(jìn)行重組然后交給更 上層,以提高吞吐。
GRO 給協(xié)議棧提供了一次將包交給網(wǎng)絡(luò)協(xié)議棧之前,對其檢查校驗和 、修改協(xié)議頭和發(fā)送應(yīng)答包(ACK packets)的機(jī)會。
如果 GRO 的 buffer 相比于包太小了,它可能會選擇什么都不做。
如果當(dāng)前包屬于某個更大包的一個分片,調(diào)用?
enqueue_backlog?將這個分片放到某個 CPU 的包隊列。當(dāng)包重組完成后,會交給?receive_skb()?方法處理。如果當(dāng)前包不是分片包,直接調(diào)用?
receive_skb(),進(jìn)行一些網(wǎng)絡(luò)棧最底層的處理。
Step 5:receive_skb()

receive_skb()?之后會再次進(jìn)入 XDP 程序點(diǎn)。
6.3 L2 -> L3(數(shù)據(jù)鏈路層 -> 網(wǎng)絡(luò)層)
Step 6:通用 XDP 處理(gXDP)

receive_skb()?之后,我們又來到了另一個 XDP 程序執(zhí)行點(diǎn)。這里可以通過?receive_xdp()?做一些通用(generic)的事情,因此我在圖中將其標(biāo)注為?(g)XDP
Step 2 中提到,如果網(wǎng)卡驅(qū)動不支持 XDP,那 XDP 程序?qū)⒀舆t到更后面執(zhí)行,這個?“更后面”的位置指的就是這里的?(g)XDP。
Step 7:Tap 設(shè)備處理

圖中有個?*check_taps?框,但其實并沒有這個方法:receive_skb()?會輪詢所有的 socket tap,將包放到正確的 tap 設(shè)備的緩沖區(qū)。
tap 設(shè)備監(jiān)聽的是三層協(xié)議(L3 protocols),例如 IPv4、ARP、IPv6 等等。如果 tap 設(shè) 備存在,它就可以操作這個 skb 了。
Step 8:tc(traffic classifier)處理
接下來我們遇到了第二種 eBPF 程序:tc eBPF。

tc(traffic classifier,流量分類器)是 Cilium 依賴的最基礎(chǔ)的東西,它提供了多種功 能,例如修改包(mangle,給 skb 打標(biāo)記)、重路由(reroute)、丟棄包(drop),這 些操作都會影響到內(nèi)核的流量統(tǒng)計,因此也影響著包的排隊規(guī)則(queueing discipline )。
Cilium 控制的網(wǎng)絡(luò)設(shè)備,至少被加載了一個 tc eBPF 程序。
譯者注:如何查看已加載的 eBPF 程序,可參考 Cilium Network Topology and Traffic Path on AWS。
Step 9:Netfilter 處理
如果 tc BPF 返回 OK,包會再次進(jìn)入 Netfilter。

Netfilter 也會對入向的包進(jìn)行處理,這里包括?nftables?和?iptables?模塊。
有一點(diǎn)需要記住的是:Netfilter 是網(wǎng)絡(luò)棧的下半部分(the “bottom half” of the network stack),因此 iptables 規(guī)則越多,給網(wǎng)絡(luò)棧下半部分造成的瓶頸就越大。
*def_dev_protocol?框是二層過濾器(L2 net filter),由于 Cilium 沒有用到任何 L2 filter,因此這里我就不展開了。
Step 10:L3 協(xié)議層處理:ip_rcv()
最后,如果包沒有被前面丟棄,就會通過網(wǎng)絡(luò)設(shè)備的?ip_rcv()?方法進(jìn)入?yún)f(xié)議棧的三層( L3)—— 即 IP 層 —— 進(jìn)行處理。

接下來我們將主要關(guān)注這個函數(shù),但這里需要提醒大家的是,Linux 內(nèi)核也支持除了 IP 之 外的其他三層協(xié)議,它們的 datapath 會與此有些不同。
6.4 L3 -> L4(網(wǎng)絡(luò)層 -> 傳輸層)
Step 11:Netfilter L4 處理

ip_rcv()?做的第一件事情是再次執(zhí)行 Netfilter 過濾,因為我們現(xiàn)在是從四層(L4)的 視角來處理 socker buffer。因此,這里會執(zhí)行 Netfilter 中的任何四層規(guī)則(L4 rules )。
Step 12:ip_rcv_finish()?處理
Netfilter 執(zhí)行完成后,調(diào)用回調(diào)函數(shù)?ip_rcv_finish()。

ip_rcv_finish()?立即調(diào)用?ip_routing()?對包進(jìn)行路由判斷。
Step 13:ip_routing()?處理
ip_routing()?對包進(jìn)行路由判斷,例如看它是否是在 lookback 設(shè)備上,是否能 路由出去(could egress),或者能否被路由,能否被 unmangle 到其他設(shè)備等等。

在 Cilium 中,如果沒有使用隧道模式(tunneling),那就會用到這里的路由功能。相比 隧道模式,路由模式會的 datapath 路徑更短,因此性能更高。
Step 14:目的是本機(jī):ip_local_deliver()?處理
根據(jù)路由判斷的結(jié)果,如果包的目的端是本機(jī),會調(diào)用?ip_local_deliver()?方法。

ip_local_deliver()?會調(diào)用?xfrm4_policy()。
Step 15:xfrm4_policy()?處理
xfrm4_policy()?完成對包的封裝、解封裝、加解密等工作。例如,IPSec 就是在這里完成的。

最后,根據(jù)四層協(xié)議的不同,ip_local_deliver()?會將最終的包送到 TCP 或 UDP 協(xié)議 棧。這里必須是這兩種協(xié)議之一,否則設(shè)備會給源 IP 地址回一個?ICMP destination unreachable?消息。
接下來我將拿 UDP 協(xié)議作為例子,因為 TCP 狀態(tài)機(jī)太復(fù)雜了,不適合這里用于理解 datapath 和數(shù)據(jù)流。但不是說 TCP 不重要,Linux TCP 狀態(tài)機(jī)還是非常值得好好學(xué)習(xí)的。
6.5 L4(傳輸層,以 UDP 為例)
Step 16:udp_rcv()?處理

udp_rcv()?對包的合法性進(jìn)行驗證,檢查 UDP 校驗和。然后,再次將包送到?xfrm4_policy()?進(jìn)行處理。
Step 17:xfrm4_policy()?再次處理

這里再次對包執(zhí)行 transform policies 是因為,某些規(guī)則能指定具體的四層協(xié)議,所以只 有到了協(xié)議層之后才能執(zhí)行這些策略。
Step 18:將包放入?socket_receive_queue
這一步會拿端口(port)查找相應(yīng)的 socket,然后將 skb 放到一個名為?socket_receive_queue?的鏈表。

Step 19:通知 socket 收數(shù)據(jù):sk_data_ready()
最后,udp_rcv()?調(diào)用?sk_data_ready()?方法,標(biāo)記這個 socket 有數(shù)據(jù)待收。

本質(zhì)上,一個 socket 就是 Linux 中的一個文件描述符,這個描述符有一組相關(guān)的文件操 作抽象,例如?read、write?等等。
網(wǎng)絡(luò)棧下半部分小結(jié)
以上?Step 1~19 就是 Linux 網(wǎng)絡(luò)棧下半部分(bottom half of the network stack)的全部內(nèi)容。
接下來我們還會介紹幾個內(nèi)核函數(shù),但它們都是與進(jìn)程上下文相關(guān)的。
6.6 L4 - User Space
下圖左邊是一段 socket listening 程序,這里省略了錯誤檢查,而且?epoll?本質(zhì)上也 是不需要的,因為 UDP 的 recv 方法以及在幫我們 poll 了。

由于大家還是對 TCP 熟悉一些,因此在這里我假設(shè)這是一段 TCP 代碼。事實上當(dāng)我們調(diào) 用?recvmsg()?方法時,內(nèi)核所做的事情就和上面這段代碼差不多。對照右邊的圖:
首先初始化一個 epoll 實例和一個 UDP socket,然后告訴 epoll 實例我們想 監(jiān)聽這個 socket 上的 receive 事件,然后等著事件到來。
當(dāng) socket buffer 收到數(shù)據(jù)時,其 wait queue 會被上一節(jié)的?
sk_data_ready()?方法置位(標(biāo)記)。epoll 監(jiān)聽在 wait queue,因此 epoll 收到事件通知后,提取事件內(nèi)容,返回給用戶空間。
用戶空間程序調(diào)用?
recv?方法,它接著調(diào)用?udp_recv_msg?方法,后者又會 調(diào)用?cgroup eBPF 程序?—— 這是本文出現(xiàn)的第三種 BPF 程序。Cilium 利用 cgroup eBPF 實現(xiàn) socket level 負(fù)載均衡,這非常酷:一般的客戶端負(fù)載均衡對客戶端并不是透明的,即,客戶端應(yīng)用必須將負(fù)載均衡邏輯內(nèi)置到應(yīng)用里。
有了 cgroup BPF,客戶端根本感知不到負(fù)載均衡的存在。
本文介紹的最后一種 BPF 程序是?sock_ops BPF,用于 socket level 整流(traffic shaping ),這對某些功能至關(guān)重要,例如客戶端級別的限速(rate limiting)。
最后,我們有一個用戶空間緩沖區(qū),存放收到的數(shù)據(jù)。
以上就是?Cilium 基于 eBPF 的內(nèi)核收包之旅(traversing the kernel’s datapath)。太壯觀了!
| Kubernetes | Cilium | Kernel |
|---|---|---|
| Endpoint (includes Pods) | Endpoint | tc, cgroup socket BPF, sock_ops BPF, XDP |
| Network Policy | Cilium Network Policy | XDP, tc, sock-ops |
| Service (node ports, cluster ips, etc) | Service | XDP, tc |
| Node | Node | ip-xfrm (for encryption), ip tables for initial decapsulation routing (if vxlan), veth-pair, ipvlan |
以上就是 Kubernetes 的所有網(wǎng)絡(luò)對象(the only artificial network objects)。什么意思??這就是 k8s CNI 所依賴的全部網(wǎng)絡(luò)原語(network primitives)。例如,LoadBalancer 對象只是 ClusterIP 和 NodePort 的組合,而后二者都屬于 Service 對象,所以他們并不 是一等對象。
這張圖非常有價值,但不幸的是,實際情況要比這里列出的更加復(fù)雜,因為 Cilium 本身的 實現(xiàn)是很復(fù)雜的。這有兩個主要原因,我覺得值得拿出來討論和體會:
首先,內(nèi)核 datapath 要遠(yuǎn)比我這里講的復(fù)雜。
前面只是非常簡單地介紹了協(xié)議棧每個位置(Netfilter、iptables、eBPF、XDP)能執(zhí)行的動作。
這些位置提供的處理能力是不同的。例如
XDP 可能是能力最受限的,因為它只是設(shè)計用來做快速丟包(fast dropping)和?非本地重定向(non-local redirecting);但另一方面,它又是最快的程序,因為 它在整個 datapath 的最前面,具備對整個 datapath 進(jìn)行短路處理(short circuit the entire datapath)的能力。
tc 和 iptables 程序能方便地 mangle 數(shù)據(jù)包,而不會對原來的轉(zhuǎn)發(fā)流程產(chǎn)生顯著影響。
理解這些東西非常重要,因為這是Cilium 乃至廣義 datapath 里非常核心的東西。如果遇到底層網(wǎng)絡(luò)問題,或者需要做 Cilium/kernel調(diào)優(yōu),那你必須要理解包的收發(fā)/轉(zhuǎn)發(fā)路徑,有時你會發(fā)現(xiàn)包的某些路徑非常反直覺。
第二個原因是,eBPF還非常新,某些最新特性只有在 5.x 內(nèi)核中才有。尤其是 XDP BPF, 可能一個節(jié)點(diǎn)的內(nèi)核版本支持,調(diào)度到另一臺節(jié)點(diǎn)時,可能就不支持。
參考
https://kccncna19.sched.com/event/Uae7/understanding-and-troubleshooting-the-ebpf-datapath-in-cilium-nathan-sweet-digitalocean
