新聞中心
理解 Net Device Ingress 和 Egress 雙重角色
作者:Lance Zhang 2023-02-18 21:31:31
云計(jì)算
云原生 本文我們從一個簡單的物理網(wǎng)卡開始,然后對 Veth、 Bridge 還有 Tc eBPF ,分別展開聊聊。

創(chuàng)新互聯(lián)專注于平羅企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站,商城網(wǎng)站建設(shè)。平羅網(wǎng)站建設(shè)公司,為平羅等地區(qū)提供建站服務(wù)。全流程按需求定制開發(fā),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)
本文是書稿《圖解 VPC & K8S 網(wǎng)絡(luò)模型》其中一篇。書稿還在繼續(xù)寫,進(jìn)度不快也不慢,因?yàn)槎绮患币膊辉?。好肉需要慢燉,好書需要多磨?/p>
為什么要單獨(dú)講這個話題呢?因?yàn)槲以诤屯掠懻?K8s 網(wǎng)絡(luò)尤其是網(wǎng)絡(luò)數(shù)據(jù)流向的時(shí)候,會反復(fù)提及到網(wǎng)絡(luò)設(shè)備,無論它是物理的還是虛擬的。而網(wǎng)絡(luò)設(shè)備在我們所討論到的數(shù)據(jù)流場景里,時(shí)而在接收數(shù)據(jù),時(shí)而在發(fā)送數(shù)據(jù)。也就是說它同時(shí)扮演著雙重身份:Ingress 和 Egress。
另外我在整理 eBPF 相關(guān)的內(nèi)容,尤其是 tc eBPF 的時(shí)候,再一次發(fā)現(xiàn)如果不能準(zhǔn)確地在數(shù)據(jù)流中識別出網(wǎng)絡(luò)設(shè)備是 Ingress 還是 Egress ,就無法將代碼邏輯和實(shí)際運(yùn)行結(jié)果對上號,更勿談能理解tc eBPF 了。
這樣的雙重角色扮演就如同一個調(diào)皮的孩子,總是帶上面具在錯綜復(fù)雜的網(wǎng)絡(luò)里面東躲西藏,肆意玩耍。而當(dāng)你好不容易抓到它時(shí),卻讓你猜猜此時(shí)此刻他是誰。
簡單來說:對于網(wǎng)卡而言,無論它是物理的還是虛擬的,對于 Ingress 角色,它是首先觸碰到數(shù)據(jù)的人,而對于 Egress 角色,它是最后一個碰到數(shù)據(jù)的人。
本文我們從一個簡單的物理網(wǎng)卡開始,然后對 veth、 bridge 還有 tc eBPF ,分別展開聊聊:
- 當(dāng)網(wǎng)卡扮演 Ingress 角色時(shí),它從哪里接收數(shù)據(jù),又將數(shù)據(jù)遞交給了誰?
- 當(dāng)網(wǎng)卡扮演 Egress 角色時(shí),它從哪里接收數(shù)據(jù),又將數(shù)據(jù)遞交給了誰?
1、單個物理網(wǎng)卡
圖1
這是一個簡單的圖,圖中有一張物理網(wǎng)卡。我們的臺式機(jī)通常是這樣的配置。橘色的線代表著輸入流程,而藍(lán)色的線表示輸出流程。
(1)輸入流程 Ingress
對于這張網(wǎng)卡而言,輸入過程伴隨著以下幾個重要的事情:物理網(wǎng)卡首先接收到物理信號 -> 物理網(wǎng)卡通過 DMA 機(jī)制將數(shù)據(jù)保存至其專屬的 RingBuffer 里面 -> 向 CPU 發(fā)起中斷 -> OS kernel thread ksoftirqd/x 不斷地消費(fèi) RingBuffer 里面的數(shù)據(jù)。
這里的 ksoftirqd 是一個內(nèi)核線程,每個 CPU 一個,x 為 CPU 編號。如 ksoftirqd/0 為 0 號 CPU 上運(yùn)行的內(nèi)核線程。
ksoftirqd/x 將數(shù)據(jù)以 skb 為處理粒度依次穿過鏈路層、網(wǎng)絡(luò)層、TCP/UDP 傳輸層 。不過 skb 在鏈路層和網(wǎng)絡(luò)層還可能直接 forward 給其它網(wǎng)卡,那這樣的話傳輸層就不會收到這個 skb 了。
整個過程如圖 2 所示,你可以從整體上感受一下。標(biāo)號 1 及 1.x 為數(shù)據(jù)輸入和生產(chǎn)過程,這是本文的重點(diǎn)。而標(biāo)號 3 為數(shù)據(jù)消費(fèi)過程,它帶著 skb 從入口處的 net_rx_action() 沿著協(xié)議棧由底向上穿越協(xié)議棧,這個過程對本文所述的所有 Ingress 場景都是通用的,故后文不再贅述這部分。
圖2
總結(jié):當(dāng)物理網(wǎng)卡扮演 Ingress 角色時(shí),它從主機(jī)外接收數(shù)據(jù),將數(shù)據(jù)遞交給了環(huán)形隊(duì)列,然后由 ksoftirqd/x 進(jìn)行后續(xù)的處理,這個處理過程也稱為網(wǎng)絡(luò)棧下半部分。
(2)輸出過程 Egress
從圖 1 中,我們大致可以看出來,對于輸出過程,數(shù)據(jù)來源有兩種,分別是通過 ip_forward() 過程和通過 ip_local_out() 過程送過來的數(shù)據(jù)。我們還會發(fā)現(xiàn),在發(fā)送數(shù)據(jù)的路徑上,這兩個過程只是起點(diǎn)有些不同,剩下的路程大家都一樣。
ip_forward() 過程與 skb 在 IP 層路由結(jié)果強(qiáng)相關(guān)。如圖 3 所示,具體來說經(jīng)過路由的判定,可能需要把 skb forward 至本機(jī)網(wǎng)絡(luò)設(shè)備或者網(wǎng)絡(luò)中的其它主機(jī)處理,不過無論是哪種情況,都需要將 skb 送往本機(jī)的一個網(wǎng)絡(luò)設(shè)備。
圖3
而 ip_local_out() 過程則對應(yīng)了本機(jī)進(jìn)程通過 socket 發(fā)送數(shù)據(jù)的場景,如圖 4 所示。這張圖最后標(biāo)注的“觸發(fā) NET_RX 類型軟中斷”是數(shù)據(jù)已經(jīng)被網(wǎng)卡發(fā)送完后發(fā)生的事情,中斷的目的是為了清理 skb ,略過不表。
圖 4 ,圖片來源:“開發(fā)內(nèi)功修煉”公眾號
總結(jié):當(dāng)物理網(wǎng)卡扮演 Egress 角色時(shí),它從本機(jī) TCP/IP 協(xié)議棧接收數(shù)據(jù),將數(shù)據(jù)通過驅(qū)動程序送離本機(jī)。
2、veth-pair
是不是覺得單個網(wǎng)卡的場景其實(shí)很容易分辨出來 Ingress 和 Egress ?
別得意,我們來加點(diǎn)難度。我們知道 K8s 的默認(rèn) CNI flannel 用到了 veth 。veth 是什么以及它的特性二哥就不細(xì)說了。我們聊一個話題:圖 5 中,當(dāng)左側(cè)進(jìn)程向右側(cè)進(jìn)程通信發(fā)送數(shù)據(jù)時(shí), 左端的 veth_left 是 Ingress 還是 Egress ? 右端的 veth_right 呢?
圖 5
結(jié)合圖 5 上的箭頭示意,答案應(yīng)該不難猜。對 veth_left 來說,它扮演的是 Egress 角色,因?yàn)檫M(jìn)程需要通過它把數(shù)據(jù)發(fā)送出去。對 veth_right 而言是 Ingress ,因?yàn)樗枰?fù)責(zé)接收數(shù)據(jù)并把它送給右側(cè)的進(jìn)程。
下一個問題:既然 veth_left 扮演了 Egress 角色,流量從離開 network namespace 1 之后去哪里了?既然 veth_right 是 Ingress ,那它從哪里接收到流量的?
答案都在圖 6 里面。圖中標(biāo)號 2 及 2.x 在進(jìn)行數(shù)據(jù)發(fā)送的工作,都屬于 veth_left 的 Egress 的過程,這個過程是發(fā)生在 network namespace 1 里面的,函數(shù)調(diào)用棧和圖 4 一樣。而標(biāo)號 3 為數(shù)據(jù)消費(fèi)也即 veth_right 的 Ingress 過程,這個過程和物理網(wǎng)卡一模一樣。
圖 6
總結(jié):既然 veth 是一對虛擬網(wǎng)卡,那我們把對它倆的總結(jié)放在一起。
當(dāng) veth 網(wǎng)卡扮演 Egress 角色時(shí),如圖 7 中的 veth_left,它從其所在的 network namespace TCP/IP 協(xié)議棧接收數(shù)據(jù),并將數(shù)據(jù)遞交給了 per CPU input_pkt_queue 隊(duì)列,并觸發(fā)軟件中斷。
當(dāng) veth 網(wǎng)卡扮演 Ingress 角色時(shí),如圖 7 中的 veth_right,它并沒有物理網(wǎng)卡那種環(huán)形隊(duì)列,而是由 ksoftirqd/x 直接從 per CPU input_pkt_queue 隊(duì)列讀取 veth_left 塞進(jìn)來的數(shù)據(jù)。
veth_left 和 veth_right 共享了同一個 queue 。典型的生產(chǎn)者 / 消費(fèi)者設(shè)計(jì)模式的即視感有沒有?
圖 7
3、bridge
上一節(jié),二哥把 veth pair 單獨(dú)拿出來和大家一起觀賞??伤鼈兘K究不是花瓶,它們被創(chuàng)造出來是要有實(shí)際使用價(jià)值的。veth 典型使用場景就是把一端插入到 bridge 里面,如圖 8 所示。
從 veth 的特性來說,流量從下圖 veth1-left 流出后,會進(jìn)入 veth1-right ,這也就意味著流量進(jìn)入了網(wǎng)橋。
我想這個時(shí)候你可以確定 veth1-left 是 Egress ,而 veth1-right 是 Ingress 。那么對于 bridge 的 Port 1 和 Port 2 呢?再進(jìn)一步,對于 veth2-left 和 veth2-right 呢 ?
圖 8
其實(shí)對于 bridge 這種虛擬的網(wǎng)橋,它的 port 口也是一個虛擬的概念,說得更直白一點(diǎn),在內(nèi)核里它就是一個數(shù)據(jù)結(jié)構(gòu):struct net_bridge_port 。這個結(jié)構(gòu)里有 3 個重要的成員:br / port_no / dev 。下面的代碼用于插入網(wǎng)絡(luò)設(shè)備到 bridge ,這 3 個成員的作用顯而易見。
//file: net/bridge/br_if.c
static struct net_bridge_port *new_nbp(struct net_bridge *br,
struct net_device *dev)
{
//申請插口對象
struct net_bridge_port *p;
p = kzalloc(sizeof(*p), GFP_KERNEL);
//初始化插口
index = find_portno(br);
p->br = br;
p->dev = dev;
p->port_no = index;
...
}
對于圖 8 來說, Port 1 (net_bridge_port) 就是一個粘合劑,左手 bridge ,右手 veth1-right 。理解了這點(diǎn)也就明白了對于 bridge 的 Port 而言,它是沒有所謂的 Ingress 和 Egress 的概念的。
Port 1 接收數(shù)據(jù)其實(shí)是 veth1-right 在 Ingress,而 bridge 把這個流量 forward 給 veth2-right 時(shí),veth2-right 其實(shí)在扮演 Egress 角色。那流量從 veth2-right 傳至 veth2-left 的過程和 veth1-left 向 veth1-right 發(fā)送數(shù)據(jù)的過程是完全一樣的。
總結(jié):當(dāng) veth 這樣的虛擬網(wǎng)卡插入在 bridge 上時(shí):
圖8 中 veth1-left 扮演 Egress 角色,它從其所在的 network namespace TCP/IP 協(xié)議棧接收數(shù)據(jù),將數(shù)據(jù)遞交給了 per CPU input_pkt_queue 隊(duì)列,并觸發(fā)軟件中斷。
veth1-right 扮演 Ingress 角色,它并沒有物理網(wǎng)卡那種環(huán)形隊(duì)列,而是由 ksoftirqd/x 直接從 per CPU input_pkt_queue 隊(duì)列讀取 veth1-left 塞進(jìn)來的數(shù)據(jù)。當(dāng) ksoftirqd/x 把流量送至鏈路層時(shí),從 br_forward() 開始進(jìn)入 forward 流程。這個流程的效果就是流量從 veth1-right 轉(zhuǎn)至 veth2-right 發(fā)送出去了。
那自然 veth2-right 這個時(shí)候就扮演了 Egress 角色,veth2-left 扮演了 Ingress 角色。
4、veth-pair plus
如果你沒有暈的話,那抖索一下精神,我們開始 veth-pair 的進(jìn)階版。
上一節(jié)我們看到 veth 和 bridge 搭配使用的場景。veth 另一端一定要插在 bridge 上嗎?從圖 9 你也看到了,答案是:不一定。
圖 9
現(xiàn)在我們知道,在圖 9 中,從 container-1 發(fā)出的流量經(jīng)過 veth 發(fā)出后,veth-p 會以 Ingress 角色開始接收。根據(jù)前文的解釋,當(dāng)網(wǎng)卡進(jìn)行 Ingress 時(shí),流量會被 ksoftirqd/x 送往協(xié)議棧進(jìn)一步處理。這個處理的過程當(dāng)然也就包括了圖 9 中的路由過程。
圖 9 這里巧妙的地方是:流量是產(chǎn)生于容器內(nèi),但對這份流量的路由卻發(fā)生在主機(jī) root(default) network namespance 里面,使用的也是主機(jī)的路由表。如果路由結(jié)果發(fā)現(xiàn)需要把這份流量發(fā)往其它主機(jī),那自然流量就從主機(jī)的 eth0 這個網(wǎng)卡設(shè)備離開了。在這個過程中,主機(jī)其實(shí)扮演了網(wǎng)關(guān)的角色。
說到這里,你能理解圖 10 的工作過程了嗎?它是 K8s host-gateway 網(wǎng)絡(luò)模型,顧名思義,這種網(wǎng)絡(luò)模型以 host 為 gateway ,更具體地說,host 的 root network namespace 充當(dāng)了路由的角色。
圖 10
5、tc eBPF
如果你對 tc 和 eBPF 了解得不多或者不感興趣,可以跳過這部分。
以 Cilium 為代表的 K8s CNI 提供商一直在嘗試使用 eBPF 代替 iptables 以便優(yōu)化服務(wù)網(wǎng)格數(shù)據(jù)面性能。其中 bpf_redicrect() 函數(shù)即為其中一項(xiàng)優(yōu)化產(chǎn)出。
bpf_redicrect() 函數(shù)的特性用一句話就能解釋清楚:當(dāng) veth Ingress 時(shí),將流量直接通過 bpf_redirect() 重定向到另一個 veth Ingress。如圖 11 所示。
不過如果你對 veth pair 哪一端在何時(shí)會扮演 Ingress 角色了解得不是很清楚的話,上面這句話其實(shí)會把你繞暈。
圖 11
但在看完二哥這篇文章后,希望你不會再暈了。在圖 11 中,從右下 Pod 出來的流量會流到位于 host network ns 這一端的 veth 上,這個 veth 是以 Ingress 角色工作的。
你看到在它身上附上了一個 eBPF 小蜜蜂圖標(biāo),表示這個時(shí)候 eBPF 程序會介入執(zhí)行,執(zhí)行的結(jié)果就是流量被直接通過 dev_forward_skb() forward 給了另外一個同樣位于 host network ns 端的 veth (如圖 11 箭頭所指的那個 veth),當(dāng)然對這個 veth 而言,它會扮演 Egress 角色。
這個過程也可以用下面這樣的函數(shù)調(diào)用層次圖來表示。
pkt -> NIC -> TC ingress -> handle_ing()
|-verdict = tc_classify() // exec BPF code
| |-bpf_redirect() // return verdict
|
|-switch (verdict) {
case TC_ACK_REDIRECT:
skb_do_redirect() // to the target net device
|-if ingress:
| dev_forward_skb()
|-else:
dev_queue_xmit()
下面是網(wǎng)易輕舟的一篇文章里面所附的圖。它畫出了網(wǎng)易輕舟基于 Cilium 網(wǎng)絡(luò)方案的探索和實(shí)踐細(xì)節(jié),包括跨節(jié)點(diǎn) Pod 間通信、同節(jié)點(diǎn) Pod 間通信、Pod 訪問外網(wǎng)等各類常見的場景。
圖中 cilium_net/cilium_host? 是一對 veth pair ,它們在 Kernel 4.19? + Cilium ?1.8? 部署中已經(jīng)沒什么作用了(事實(shí)上社區(qū)在考慮去掉它們)。
我想有了上面所有的鋪墊和知識,至少對標(biāo)號 ① ② 所示的流程,你應(yīng)該能看得懂了。
圖 12
6、總結(jié)
文末,二哥來做個小總結(jié):
- 對于網(wǎng)卡而言,無論它是物理的還是虛擬的,對于 Ingress 角色,它是首先觸碰到數(shù)據(jù)的人,而對于 Egress 角色,它是最后一個碰到數(shù)據(jù)的人。
- 對于 Ingress 過程,無論是物理網(wǎng)卡還是虛擬網(wǎng)卡,在它接收到數(shù)據(jù)后,總是通過 ksoftirqd/x 進(jìn)行網(wǎng)絡(luò)棧下半部分處理。
- 對于 Egress 過程,無論是物理網(wǎng)卡還是虛擬網(wǎng)卡,在它從鏈路層那里拿到數(shù)據(jù)后,總是通過網(wǎng)卡驅(qū)動程序?qū)?shù)據(jù)送離本設(shè)備。
- ?Ingress 過程和 Egress 過程在內(nèi)核中的處理路徑完全不同,也更無對稱可言。
- 對于 veth ,無論是搭配 bridge 還是用于 host-gateway 場景,在數(shù)據(jù)流經(jīng)過的關(guān)鍵位置區(qū)分是 Ingress 還是 Egress ,有助于理解系統(tǒng)對數(shù)據(jù)流的處理行為。
- tc eBPF 強(qiáng)依賴 Ingress 和 Egress,理解了它們也才能更好地理解 bpf_redirect() 。
本文標(biāo)題:理解NetDeviceIngress和Egress雙重角色
網(wǎng)站路徑:http://m.fisionsoft.com.cn/article/codgcso.html


咨詢
建站咨詢
