當從Pod訪問百度時會用到VTEP嗎
大家好,我是二哥。
一個公眾號關注者私信問我一個問題:從 Pod 內發起的,向外網的訪問過程會涉及到 VTEP 嗎?涉及到的 NAT 細節是什么?
我大概整理了一下,寫成此文。
寫公眾號大概有半年左右的時間了,寫一段小感想:于我,每篇文章的選題和文章所涉及知識點的匯編、學習、整理、成文都是一個歷練過程;于你,是一個恰好數分鐘的的短暫停留;于我們,是一個交流的紐帶。利是斷然沒有的,虛名會有一丁點,但那絕非我初衷。
相關話題
在文章《??tun設備的妙用-Flannel UDP模式篇??》中,二哥借助 tun 設備,畫了幾張高清大圖和老鐵們詳細聊了下 Pod 間通信時,數據流向是什么樣子的,以及在這個數據流淌過程中涉及到了哪些網絡設備,這些設備又是如何在各自的位置上盡心盡責地處理、搬移每一份網絡包的。
那篇文章涉及到的是 Pod 間網絡通信的場景。在 K8s Overlay 網絡模型下,這個過程不需要對進、出 Pod 的 traffic 進行任何的 NAT。在 VXLAN 這個神器加持下,宿主機網絡根本不關心、也看不到 Pod traffic 的細節,因為 traffic 都被封裝起來了。
但當 Pod 想訪問外網,比如 www.baidu.com 的時候,就是另外一個場景了。一是 VXLAN 在這里幫不上忙,VXLAN 是在通信雙方所涉及到的 work node 上利用各自的 VTEP 分別進行封包和解包,很顯然外網 remote service 不可能有這個對等的 VTEP。二是也沒有必要使用 VXLAN 。
其實大家多多少少能大概猜得出來或隱約覺得這里面涉及到了 NAT 。可架不住細問,比如:
- 一定需要NAT嗎?
- 假如要NAT的話,從 Pod a 看來,既然 NAT 涉及到對去程的網絡包進行 IP 或 Port 的修改,那它勢必需要記得這些連接在被修改過之前的信息,這樣才能在網絡包回來的時候再修改一番。那這些連接信息記錄在那里呢?
- NAT 這個過程到底發生在哪里呢?Pod 里還是宿主機上?具體是網絡包流經到哪個位置發生了 NAT 呢?
先回答第一個問題:當 traffic 離開宿主機的時候不一定需要NAT。這取決于 K8s 所使用的網絡模型。比如在 Underlay 模型下,就完全沒有必要用到NAT, 具體內容詳見二哥在《多圖匯總梳理VPC與三種K8s網絡模型》中的總結。但在 Overlay 模型下,NAT就很有必要了,所以本文的場景限定于 Overlay 模型。
另外,本文所討論的 NAT 只會到 work node 這一級。我們知道從網絡包離開 work node 到最終達到百度服務器,中間可能會經過若干次 NAT ,那些我們就管不著了。不過二哥在《廣角-聊聊Underlay》這篇文章里,畫出了數據中心網絡拓撲高清大圖,如果你感興趣的話,可以打開看看。
來吧,進入正題。
Netfilter conntrack
照例,我們先從 NAT 所涉及到的基礎知識開始。
Netfilter conntrack 又名 CT ,譯作連接跟蹤。它是一個內核模塊(nf_conntrack),用于維護可跟蹤協議(trackable protocols)的連接狀態。目前只支持以下六種協議:TCP、UDP、ICMP、DCCP、SCTP、GRE。
既然說跟蹤,得有一個或一組標記可供記錄、查找、回溯。在這里 CT 利用到了每個網絡包里面的源 IP ,目的 IP ,源 Port ,目的 Port 和 Protocol 這5個參數所組成的一組標記。更簡單地說,CT 認為這5個參數可用來唯一標識一個單向連接。注意,這里說的是單向連接。網絡通信涉及到去和回兩個單向連接。
你可以用命令 conntrack -L 查看本機的 CT 記錄。如下表所示,第一條記錄了去、回兩個方向的記錄。每一個這樣的記錄叫做:connection tracking entry (conntrack entry)。
# conntrack -L
udp 17 172 src=127.0.0.1 dst=127.0.0.53 sport=59837 dport=53 src=127.0.0.53 dst=127.0.0.1 sport=53 dport=59837 [ASSURED] mark=0 use=1
tcp 6 5 TIME_WAIT src=127.0.0.1 dst=127.0.0.1 sport=35074 dport=2379 src=127.0.0.1 dst=127.0.0.1 sport=2379 dport=35074 [ASSURED] mark=0 use=1
看完了一條 conntrack entry 長什么樣子,我們再來看下記錄是在哪里發生的。下圖中 routing + iptables of root namespace 里面,你會看到一共兩處有橢圓形所標記的 conntrack 。一處是在 PREROUTING 鏈附近,一處是在 OUTPUT 鏈附近。
為什么是這兩個 hook 點創建連接跟蹤記錄呢?因為它們都是新連接的第一個包最先達到的地方:
- PRE_ROUTING 是外部包或者源自本機其它 network namepsace 的包最先到達的地方。從 eth0 進來的包為外部包,而從 1.5 處 cni0 bridge 進來的則為本機上其它 network namepsace 的包。
- LOCAL_OUT 是本機通過 root network namepsace 主動與對方通信時,網絡包最先到達的地方。這里的“對方”既可以是外部服務,也可以是位于本機上的,但使用的是其它 network namepsace 的進程。
當然,在LOCAL_IN和POSTROUTING處還會執行一次 confirm 操作,以確保這樣一件事情:剛才所說的這個新創建的 conntrack entry ,它對應的是一條新的單向連接,這個連接的第一個包經過各種處理,一直到這里仍然健在、沒有被丟棄。這并非本文重點,略過不聊。
沒有特殊情況,我們可以粗略地認為前文所提 conntrack entry 在 PREROUTING 和 OUTPUT 鏈被成功地記錄下來了,以備后用。
圖 1:從 Pod a 訪問外網時的數據流
NAT
連接跟蹤是許多網絡應用的基礎,例如 Kubernetes Service、ServiceMesh sidecar、 軟件四層負載均衡器 LVS/IPVS、Docker network、OVS、iptables 主機防火墻等等都依賴連接跟蹤功能。NAT更是離不開 CT 。
我們結合上圖來看看當 Pod a 訪問百度的時候,網絡包流經協議棧以及在 routing table 和 iptables 共同作用下,具體發生了什么。
www.baidu.com IP 地址是 180.101.49.11 , 我們的 Pod a IP 地址為 10.244.0.2 。
1.1:從Pod a發起的請求會從 1.1 一路來到 1.5 的位置。二哥在《??tun設備的妙用-Flannel UDP模式篇??》里面已細聊過細節,這里就跳過去了。
1.6:當bridge將網絡包遞交到網絡層后,網絡包在 1.6 處便開始了它在 root network namespace 的新旅程。與之對應的是從 1.1 ~ 1.4 網絡包還依舊只是在 Pod a 自己的 network namespace 里面轉悠。在 1.5 處,網絡包從一個 network namespace 跳轉到了另外一個。
1.7:如前一節所述,這里的 conntrack 會記錄新的連接。
1.8:經過路由選擇,網絡包需要從宿主機的 eth0 離開。于是走 FORWARD 鏈,來到 POSTROUTING 鏈。
1.9:現在來到了 NAT 現場。結合下面的 iptables dump,我們可以清楚地看到當源IP位于 CIDR 10.244.0.0/16 ,且不是去 docker0 這個interface ,則會進行 NAT 。將Pod a的源 IP 地址修改成了 Node 1 的 IP 地址。
1.10:開始將網絡包送到鏈路層。
1.11:網絡包從 eth0 離開宿主機。
-A POSTROUTING -s 10.244.0.0/16 ! -o docker0 -j MASQUERADE
再看問題
看完 Pod 訪問百度時,CT 和 NAT 的介入細節,我們再來梳理一下這位同學的問題:為什么 Pod a 訪問百度時 VTEP 不會介入?
圖2是文章《tun設備的妙用-Flannel UDP模式篇》的配圖。它詳細地標出了從 Node 1 上的 Pod a 訪問 Node X 的 Pod b 時的數據流。為了方便理解,我們將 Flannel VXLAN 模式所用的 VTEP 換成了 UDP 模式所用的 tun 設備 + flannel daemon 組合。這樣的更換只是把原本位于內核態的數據解、封裝動作挪動到用戶態的 daemon 里面來完成了,換湯不換藥。
當請求從 Pod a 發起,無論是訪問外網還是訪問另一個 Pod,圖1和圖2中 1.1 ~ 1.5 這個流程是不變的。
當網絡包從 1.5 處進入網絡層后,網絡包的去向發生了變化:
- 如果是 Pod 間通信,網絡包會經由圖2中的 1.7 流動到 1.9,再完成后續的數據封包過程。
- 與之相比,當 Pod 訪問百度時,網絡包在圖1中 1.9 處經過 NAT 處理后,直接從 1.11 處離開了宿主機,不需要任何的封包過程,也就不需要VTEP的介入。
圖 2:Pod間通信時的數據流
以上就是本文的全部內容。