深入探索 Kubernetes 網(wǎng)絡(luò)模型和網(wǎng)絡(luò)通信
Kubernetes 定義了一種簡單、一致的網(wǎng)絡(luò)模型,基于扁平網(wǎng)絡(luò)結(jié)構(gòu)的設(shè)計(jì),無需將主機(jī)端口與網(wǎng)絡(luò)端口進(jìn)行映射便可以進(jìn)行高效地通訊,也無需其他組件進(jìn)行轉(zhuǎn)發(fā)。該模型也使應(yīng)用程序很容易從虛擬機(jī)或者主機(jī)物理機(jī)遷移到 Kubernetes 管理的 pod 中。
這篇文章主要深入探索Kubernetes網(wǎng)絡(luò)模型,并了解容器、pod間如何進(jìn)行通訊。對于網(wǎng)絡(luò)模型的實(shí)現(xiàn)將會在后面的文章介紹。
Kubernetes 網(wǎng)絡(luò)模型
該模型定義了:
- 每個(gè) pod 都有自己的 IP 地址,這個(gè) IP 在集群范圍內(nèi)可達(dá)
- Pod 中的所有容器共享 pod IP 地址(包括 MAC 地址),并且容器之前可以相互通信(使用localhost)
- Pod 可以使用 pod IP 地址與集群中任一節(jié)點(diǎn)上的其他 pod 通信,無需 NAT
- Kubernetes 的組件之間可以相互通信,也可以與 pod 通信
- 網(wǎng)絡(luò)隔離可以通過網(wǎng)絡(luò)策略實(shí)現(xiàn)
上面的定義中提到了幾個(gè)相關(guān)的組件:
- Pod:Kubernetes 中的 pod 有點(diǎn)類似虛擬機(jī)有唯一的 IP 地址,同一個(gè)節(jié)點(diǎn)上的 pod 共享網(wǎng)絡(luò)和存儲。
- Container:pod 是一組容器的集合,這些容器共享同一個(gè)網(wǎng)絡(luò)命名空間。pod 內(nèi)的容器就像虛擬機(jī)上的進(jìn)程,進(jìn)程之間可以使用localhost 進(jìn)行通信;容器有自己獨(dú)立的文件系統(tǒng)、CPU、內(nèi)存和進(jìn)程空間。需要通過創(chuàng)建 Pod 來創(chuàng)建容器。
- Node:pod 運(yùn)行在節(jié)點(diǎn)上,集群中包含一個(gè)或多個(gè)節(jié)點(diǎn)。每個(gè) pod 的網(wǎng)絡(luò)命名空間都會連接到節(jié)點(diǎn)的命名空間上,以打通網(wǎng)絡(luò)。
講了這么多次網(wǎng)絡(luò)命名空間,那它到底是如何運(yùn)作的呢?
網(wǎng)絡(luò)命名空間如何工作
在 Kubernetes 的發(fā)行版 k3s 創(chuàng)建一個(gè) pod,這個(gè) pod 有兩個(gè)容器:發(fā)送請求的 curl? 容器和提供 web 服務(wù)的 httpbin 容器。
雖然使用發(fā)行版,但是其仍然使用 Kubernetes 網(wǎng)絡(luò)模型,并不妨礙我們了解網(wǎng)絡(luò)模型。
登錄到節(jié)點(diǎn)上,通過 lsns -t net? 當(dāng)前主機(jī)上的網(wǎng)絡(luò)命名空間,但是并沒有找到 httpbin? 的進(jìn)程。有個(gè)命名空間的命令是 /pause?,這個(gè) pause 進(jìn)程實(shí)際上是每個(gè) pod 中 不可見 的 sandbox
既然每個(gè)容器都有獨(dú)立的進(jìn)程空間,我們換下命令查看進(jìn)程類型的空間:
通過進(jìn)程 PID 129889 可以找到其所屬的命名空間:
然后可以在該命名空間下使用 exec 執(zhí)行命令:
從結(jié)果來看 pod 的 IP 地址 10.42.1.14? 綁定在接口 eth0? 上,而 eth0? 被連接到 17 號接口上。
在節(jié)點(diǎn)主機(jī)上,查看 17? 號接口信息。veth7912056b? 是主機(jī)根命名空間下的虛擬以太接口(vitual ethernet device),是連接 pod 網(wǎng)絡(luò)和節(jié)點(diǎn)網(wǎng)絡(luò)的 隧道,對端是 pod 命名空間下的接口 eth0。
上面的結(jié)果看到,該 veth? 連到了個(gè)網(wǎng)橋(network bridge)cni0 上。
網(wǎng)橋工作在數(shù)據(jù)鏈路層(OSI 模型的第 2 層),連接多個(gè)網(wǎng)絡(luò)(可多個(gè)網(wǎng)段)。當(dāng)請求到達(dá)網(wǎng)橋,網(wǎng)橋會詢問所有連接的接口(這里 pod 通過 veth 以網(wǎng)橋連接)是否擁有原始請求中的 IP 地址。如果有接口響應(yīng),網(wǎng)橋會將匹配信息(IP -> veth)記錄,并將數(shù)據(jù)轉(zhuǎn)發(fā)過去。
那如果沒有接口響應(yīng)怎么辦?具體流程就要看各個(gè)網(wǎng)絡(luò)插件的實(shí)現(xiàn)了。我準(zhǔn)備在后面的文章中介紹常用的網(wǎng)絡(luò)插件,比如 Calico、Flannel、Cilium 等。
接下來看下 Kubernetes 中的網(wǎng)絡(luò)通信如何完成,一共有幾種類型:
- 同 pod 內(nèi)容器間通信
- 同節(jié)點(diǎn)上的 pod 間通信
- 不同節(jié)點(diǎn)上的 pod 間通信
Kubernetes 網(wǎng)絡(luò)如何工作
同 pod 內(nèi)的容器間通信
同 pod 內(nèi)的容器間通信最簡單,這些容器共享網(wǎng)絡(luò)命名空間,每個(gè)命名空間下都有 lo? 回環(huán)接口,可以通過 localhost 來完成通信。
同節(jié)點(diǎn)上的 pod 間通信
當(dāng)我們將 curl? 容器和 httpbin? 分別在兩個(gè) pod 中運(yùn)行,這兩個(gè) pod 有可能調(diào)度到同一個(gè)節(jié)點(diǎn)上。curl?發(fā)出的請求根據(jù)容器內(nèi)的路由表到達(dá)了 pod 內(nèi)的 eth0? 接口。然后通過與 eth0? 相連的隧道 veth1 到達(dá)節(jié)點(diǎn)的根網(wǎng)絡(luò)空間。
veth1? 通過網(wǎng)橋 cni0? 與其他 pod 相連虛擬以太接口 vethX? 相連,網(wǎng)橋會詢問所有相連的接口是否擁有原始請求中的 IP 地址(比如這里的 10.42.1.9?)。收到響應(yīng)后,網(wǎng)橋會記錄映射信息(10.42.1.9? => veth0?),同時(shí)將數(shù)據(jù)轉(zhuǎn)發(fā)過去。最終數(shù)據(jù)經(jīng)過 veth0? 隧道進(jìn)入 pod httpbin 中。
不同節(jié)點(diǎn)的 pod 間通信
跨節(jié)點(diǎn)的 pod 間通信會復(fù)雜一些,且 不同網(wǎng)絡(luò)插件的處理方式不同,這里選擇一種容易理解的方式來簡單說明下。
前半部分的流程與同節(jié)點(diǎn) pod 間通信類似,當(dāng)請求到達(dá)網(wǎng)橋,網(wǎng)橋詢問哪個(gè) pod 擁有該 IP 但是沒有得到回應(yīng)。流程進(jìn)入主機(jī)的路由尋址過程,到更高的集群層面。
在集群層面有一張路由表,里面存儲著每個(gè)節(jié)點(diǎn)的 Pod IP 網(wǎng)段(節(jié)點(diǎn)加入到集群時(shí)會分配一個(gè) Pod 網(wǎng)段(Pod CIDR),比如在 k3s 中默認(rèn)的 Pod CIDR 是 10.42.0.0/16?,節(jié)點(diǎn)獲取到的網(wǎng)段是 10.42.0.0/24、10.42.1.0/24、10.42.2.0/24,依次類推)。通過節(jié)點(diǎn)的 Pod IP 網(wǎng)段可以判斷出請求 IP 的節(jié)點(diǎn),然后請求被發(fā)送到該節(jié)點(diǎn)。
總結(jié)
現(xiàn)在應(yīng)該對 Kubernetes 的網(wǎng)絡(luò)通信有初步的了解了吧。
整個(gè)通信的過程需要各種組件的配合,比如 Pod 網(wǎng)絡(luò)命名空間、pod 以太網(wǎng)接口 eth0?、虛擬以太網(wǎng)接口 vethX?、網(wǎng)橋(network bridge) cni0 等。其中有些組件與 pod 一一對應(yīng),與 pod 同生命周期。雖然可以通過手動的方式創(chuàng)建、關(guān)聯(lián)和刪除,但對于 pod 這種非永久性的資源會被頻繁地創(chuàng)建和銷毀,太多人工的工作也是不現(xiàn)實(shí)的。
實(shí)際上這些工作都是由容器委托給網(wǎng)絡(luò)插件來完成的,而網(wǎng)絡(luò)插件所遵循的規(guī)范 CNI(Container Network Interface)。
網(wǎng)絡(luò)插件都做了什么?
- 創(chuàng)建 pod(容器)的網(wǎng)絡(luò)命名空間
- 創(chuàng)建接口
- 創(chuàng)建 veth 對
- 設(shè)置命名空間網(wǎng)絡(luò)
- 設(shè)置靜態(tài)路由
- 配置以太網(wǎng)橋接器
- 分配 IP 地址
- 創(chuàng)建 NAT 規(guī)則
...
- 參考
https://www.tigera.io/learn/guides/kubernetes-networking/
https://kubernetes.io/docs/concepts/services-networking/
https://matthewpalmer.net/kubernetes-app-developer/articles/kubernetes-networking-guide-beginners.html
https://learnk8s.io/kubernetes-network-packets