通過(guò)本文,你將了解在 Kubernetes 內(nèi)外,數(shù)據(jù)包是如何轉(zhuǎn)發(fā)的,從原始的 Web 請(qǐng)求開(kāi)始,到托管應(yīng)用程序的容器。 在深入了解在 Kubernetes 集群中數(shù)據(jù)包如何流轉(zhuǎn)的細(xì)節(jié)之前,先明確一下 Kubernetes 對(duì)網(wǎng)絡(luò)的要求。
Kubernetes 網(wǎng)絡(luò)模型定義了一組基本規(guī)則:
在不使用網(wǎng)絡(luò)地址轉(zhuǎn)換 (NAT) 的情況下,集群中的 Pod 能夠與任意其他 Pod 進(jìn)行通信。
在不使用網(wǎng)絡(luò)地址轉(zhuǎn)換 (NAT) 的情況下,在集群節(jié)點(diǎn)上運(yùn)行的程序能與同一節(jié)點(diǎn)上的任何 Pod 進(jìn)行通信。
每個(gè) Pod 都有自己的 IP 地址(IP-per-Pod),并且任意其他 Pod 都可以通過(guò)相同的這個(gè)地址訪問(wèn)它。
這些要求,不會(huì)將具體實(shí)現(xiàn)限制在某種解決方案上。
相反,它們籠統(tǒng)地描述了集群網(wǎng)絡(luò)的特性。
為了滿足這些限制,你必須解決以下挑戰(zhàn):
如何確保同一個(gè) Pod 中的容器行為就像它們?cè)谕粋€(gè)主機(jī)上一樣?
集群中的 Pod 能否訪問(wèn)其他 Pod?
Pod 可以訪問(wèn)服務(wù)嗎?服務(wù)是負(fù)載均衡的嗎?
Pod 可以接收集群外部的流量嗎?
在本文中,將重點(diǎn)關(guān)注前三點(diǎn),從 Pod 內(nèi)的網(wǎng)絡(luò),容器到容器的通信說(shuō)起。
Linux 網(wǎng)絡(luò)命名空間如何在 Pod 中工作
讓我們來(lái)看一個(gè)運(yùn)行應(yīng)用的主容器和伴隨一起的另一個(gè)容器。
在示例中,有一個(gè)帶有 nginx 和 busybox 容器的 Pod:
apiVersion:v1 kind:Pod metadata: name:multi-container-Pod spec: containers: -name:container-1 image:busybox command:['/bin/sh','-c','sleep1d'] -name:container-2 image:nginx
部署時(shí),會(huì)發(fā)生以下事情:
Pod 在節(jié)點(diǎn)上擁有獨(dú)立的網(wǎng)絡(luò)命名空間。
分配一個(gè) IP 地址給 Pod ,兩個(gè)容器之間共享端口。
兩個(gè)容器共享相同的網(wǎng)絡(luò)命名空間,并在本地彼此可見(jiàn)。
網(wǎng)絡(luò)配置在后臺(tái)迅速完成。
但是,讓我們退后一步,嘗試?yán)斫鉃槭裁催\(yùn)行容器需要上述動(dòng)作。
在 Linux 中,網(wǎng)絡(luò)命名空間是獨(dú)立的、隔離的邏輯空間。
你可以將網(wǎng)絡(luò)命名空間視為,將物理網(wǎng)絡(luò)接口分割小塊之后的獨(dú)立部分。
每個(gè)部分都可以單獨(dú)配置,并擁有自己的網(wǎng)絡(luò)規(guī)則和資源。
這些包括防火墻規(guī)則、接口(虛擬的或物理的)、路由以及與網(wǎng)絡(luò)相關(guān)的所有內(nèi)容。
物理網(wǎng)絡(luò)接口持有根網(wǎng)絡(luò)命名空間。
? ?2. 你可以使用 Linux 網(wǎng)絡(luò)命名空間來(lái)創(chuàng)建獨(dú)立的網(wǎng)絡(luò)。每個(gè)網(wǎng)絡(luò)都是獨(dú)立的,除非你進(jìn)行配置,默認(rèn)不會(huì)與其他網(wǎng)絡(luò)互通。
但最終,還是需要物理接口處理所有真實(shí)的數(shù)據(jù)包,所有虛擬接口都是基于物理接口創(chuàng)建的。
網(wǎng)絡(luò)命名空間可以通過(guò) ip-netns 進(jìn)行管理,使用 ip netns list 可以列出主機(jī)上的命名空間。
需要注意的是,創(chuàng)建的網(wǎng)絡(luò)命名空間會(huì)出現(xiàn)在 /var/run/netns 下面,但 Docker 并沒(méi)有遵循這一規(guī)則。
例如,這是 Kubernetes 節(jié)點(diǎn)的一些命名空間:
$ipnetnslist cni-0f226515-e28b-df13-9f16-dd79456825ac(id:3) cni-4e4dfaac-89a6-2034-6098-dd8b2ee51dcd(id:4) cni-7e94f0cc-9ee8-6a46-178a-55c73ce58f2e(id:2) cni-7619c818-5b66-5d45-91c1-1c516f559291(id:1) cni-3004ec2c-9ac2-2928-b556-82c7fb37a4d8(id:0)
注意 cni- 前綴;這意味著命名空間是由 CNI 插件創(chuàng)建的。
當(dāng)你創(chuàng)建一個(gè) Pod,Pod 被分配給一個(gè)節(jié)點(diǎn)后,CNI 將:
分配 IP 地址。
將容器連接到網(wǎng)絡(luò)。
如果 Pod 包含多個(gè)容器,那么這些容器都將被放在同一個(gè)命名空間中。
當(dāng)創(chuàng)建 Pod 時(shí),容器運(yùn)行時(shí)會(huì)給容器創(chuàng)建一個(gè)網(wǎng)絡(luò)命名空間。
? 2. 然后 CNI 負(fù)責(zé)給 Pod 分配一個(gè) IP 地址。
? 3. 最后 CNI 將容器連接到網(wǎng)絡(luò)的其余部分。
那么,當(dāng)你列出節(jié)點(diǎn)上的容器的命名空間會(huì)發(fā)生什么呢?
你可以通過(guò) SSH 連接到 Kubernetes 節(jié)點(diǎn)并查看命名空間:
$lsns-tnet NSTYPENPROCSPIDUSERNETNSIDNSFSCOMMAND 4026531992net1711rootunassigned/run/docker/netns/default/sbin/initnoembednorestore 4026532286net24808655350/run/docker/netns/56c020051c3b/pause 4026532414net55489655351/run/docker/netns/7db647b9b187/pause
lsns 是一個(gè)用于列出主機(jī)上所有可用命名空間的命令。
請(qǐng)記住,Linux 中有多種命名空間類型。
Nginx 容器在哪里?
那些 pause 容器是什么?
在 Pod 中,pause 容器創(chuàng)建了網(wǎng)絡(luò)命名空間
先列出節(jié)點(diǎn)上的所有命名空間,看看能否找到 Nginx 容器:
$lsns NSTYPENPROCSPIDUSERCOMMAND #truncatedoutput 4026532414net5548965535/pause 4026532513mnt15599rootsleep1d 4026532514uts15599rootsleep1d 4026532515pid15599rootsleep1d 4026532516mnt35777rootnginx:masterprocessnginx-gdaemonoff; 4026532517uts35777rootnginx:masterprocessnginx-gdaemonoff; 4026532518pid35777rootnginx:masterprocessnginx-gdaemonoff;
Nginx 容器在掛載 (mnt)、Unix time-sharing (uts) 和 PID (pid) 命名空間中,但不在網(wǎng)絡(luò)命名空間 (net) 中。
不幸的是,lsns 只顯示每個(gè)進(jìn)程最小的 PID,但你可以根據(jù)這個(gè)進(jìn)程 ID 進(jìn)一步過(guò)濾。
使用以下命令,在所有命名空間中檢索 Nginx 容器:
$sudolsns-p5777 NSTYPENPROCSPIDUSERCOMMAND 4026531835cgroup1781root/sbin/initnoembednorestore 4026531837user1781root/sbin/initnoembednorestore 4026532411ipc5548965535/pause 4026532414net5548965535/pause 4026532516mnt35777rootnginx:masterprocessnginx-gdaemonoff; 4026532517uts35777rootnginx:masterprocessnginx-gdaemonoff; 4026532518pid35777rootnginx:masterprocessnginx-gdaemonoff;
pause 進(jìn)程再次出現(xiàn),它劫持了網(wǎng)絡(luò)命名空間。
這是怎么回事?
集群中的每個(gè) Pod 都有一個(gè)額外的隱藏容器在后臺(tái)運(yùn)行,稱為 pause 容器。
列出在節(jié)點(diǎn)上運(yùn)行的容器并獲取 pause 容器:
$dockerps|greppause fa9666c1d9c6k8s.gcr.io/pause:3.4.1"/pause"k8s_POD_kube-dns-599484b884-sv2js… 44218e010aebk8s.gcr.io/pause:3.4.1"/pause"k8s_POD_blackbox-exporter-55c457d… 5fb4b5942c66k8s.gcr.io/pause:3.4.1"/pause"k8s_POD_kube-dns-599484b884-cq99x… 8007db79dcf2k8s.gcr.io/pause:3.4.1"/pause"k8s_POD_konnectivity-agent-84f87c…
可以看到,節(jié)點(diǎn)上的每一個(gè) Pod 都會(huì)有一個(gè)對(duì)應(yīng)的 pause 容器。
這個(gè) pause 容器負(fù)責(zé)創(chuàng)建和維持網(wǎng)絡(luò)命名空間。
底層容器運(yùn)行時(shí)會(huì)完成網(wǎng)絡(luò)命名空間的創(chuàng)建,通常是由 containerd 或 CRI-O 完成。
在部署 Pod 和創(chuàng)建容器之前,由運(yùn)行時(shí)創(chuàng)建網(wǎng)絡(luò)命名空間。
容器運(yùn)行時(shí)會(huì)自動(dòng)完成這些,不需要手工執(zhí)行 ip netns 創(chuàng)建命名空間。
話題回到 pause 容器。
它包含非常少的代碼,并且在部署后立即進(jìn)入睡眠狀態(tài)。
但是,它是必不可少的,并且在 Kubernetes 生態(tài)系統(tǒng)中起著至關(guān)重要的作用。
創(chuàng)建 Pod 時(shí),容器運(yùn)行時(shí)會(huì)創(chuàng)建一個(gè)帶有睡眠容器的網(wǎng)絡(luò)命名空間。
? 2. Pod 中的其他容器都會(huì)加入由 pause 容器創(chuàng)建的網(wǎng)絡(luò)名稱空間。
? 3. 此時(shí),CNI 分配 IP 地址并將容器連接到網(wǎng)絡(luò)。
一個(gè)進(jìn)入睡眠狀態(tài)的容器有什么用?
為了理解它的用途,讓我們想象一個(gè) Pod 有兩個(gè)容器,就像前面的例子一樣,但沒(méi)有 pause 容器。
一旦容器啟動(dòng),CNI 將會(huì):
使 busybox 容器加入之前的網(wǎng)絡(luò)命名空間。
分配 IP 地址。
將容器連接到網(wǎng)絡(luò)。
如果 Nginx 崩潰了怎么辦?
CNI 將不得不再次執(zhí)行所有步驟,并且兩個(gè)容器的網(wǎng)絡(luò)都將中斷。
由于睡眠容器不太可能有任何錯(cuò)誤,因此創(chuàng)建網(wǎng)絡(luò)命名空間通常是一種更安全、更健壯的選擇。
如果 Pod 中的一個(gè)容器崩潰了,剩下的仍然可以回復(fù)其他網(wǎng)絡(luò)請(qǐng)求。
分配一個(gè) IP 地址給 Pod
前面我提到 Pod 和兩個(gè)容器將具有同一個(gè) IP 地址。
那是怎樣配置的呢?
在 Pod 網(wǎng)絡(luò)命名空間內(nèi),創(chuàng)建了一個(gè)接口,并分配了一個(gè) IP 地址。
讓我們驗(yàn)證一下。
首先,找到 Pod 的 IP 地址:
$ kubectlgetPodmulti-container-Pod-ojsonpath={.status.PodIP} 10.244.4.40
接下來(lái),找到相關(guān)的網(wǎng)絡(luò)命名空間。
由于網(wǎng)絡(luò)命名空間是從物理接口創(chuàng)建的,需要先訪問(wèn)集群節(jié)點(diǎn)。
如果你運(yùn)行的是 minikube,使用 minikube ssh 訪問(wèn)節(jié)點(diǎn)。如果在云廠中運(yùn)行,那么應(yīng)該有某種方法可以通過(guò) SSH 訪問(wèn)節(jié)點(diǎn)。
進(jìn)入后,找到最新創(chuàng)建的命名網(wǎng)絡(luò)命名空間:
$ls-lt/var/run/netns total0 -r--r--r--1rootroot0Sep2513:34cni-0f226515-e28b-df13-9f16-dd79456825ac -r--r--r--1rootroot0Sep2409:39cni-4e4dfaac-89a6-2034-6098-dd8b2ee51dcd -r--r--r--1rootroot0Sep2409:39cni-7e94f0cc-9ee8-6a46-178a-55c73ce58f2e -r--r--r--1rootroot0Sep2409:39cni-7619c818-5b66-5d45-91c1-1c516f559291 -r--r--r--1rootroot0Sep2409:39cni-3004ec2c-9ac2-2928-b556-82c7fb37a4d8
在示例中,就是 cni-0f226515-e28b-df13-9f16-dd79456825ac。然后,可以在該命名空間內(nèi)運(yùn)行 exec 命令:
$ipnetnsexeccni-0f226515-e28b-df13-9f16-dd79456825acipa #outputtruncated 3:eth0@if12:mtu1450qdiscnoqueuestateUPgroupdefault link/ether16f856:77brdffffff:fflink-netnsid0 inet10.244.4.40/32brd10.244.4.40scopeglobaleth0 valid_lftforeverpreferred_lftforever inet6fe80:f8ff5677/64scopelink valid_lftforeverpreferred_lftforever
這個(gè) IP 就是 Pod 的 IP 地址!通過(guò)查找 @if12 中的 12 找到網(wǎng)絡(luò)接口
$iplink|grep-A1^12 12:vethweplb3f36a0@if16:mtu1376qdiscnoqueuemasterweavestateUPmodeDEFAULTgroupdefault link/ether7273d9:f6brdffffff:fflink-netnsid1
你還可以驗(yàn)證 Nginx 容器是否監(jiān)聽(tīng)了來(lái)自該命名空間內(nèi)的 HTTP 流量:
$ipnetnsexeccni-0f226515-e28b-df13-9f16-dd79456825acnetstat-lnp ActiveInternetconnections(onlyservers) ProtoRecv-QSend-QLocalAddressForeignAddressStatePID/Programname tcp000.0.0.0:800.0.0.0:*LISTEN692698/nginx:master tcp600:::80:::*LISTEN692698/nginx:master
如果你無(wú)法通過(guò) SSH 訪問(wèn)集群中的工作節(jié)點(diǎn),你可以使用 kubectl exec 獲取到 busybox 容器的 shell 并直接在內(nèi)部使用 ip 和 netstat 命令。
剛剛我們介紹了容器之間的通信,再來(lái)看看如何建立 Pod 到 Pod 的通信吧。
查看集群中 Pod 到 Pod 的流量
Pod 到 Pod 的通信有兩種可能的情況:
Pod 流量的目的地是同一節(jié)點(diǎn)上的 Pod。
Pod 流量的目的地是在不同節(jié)點(diǎn)上的 Pod。
整個(gè)工作流依賴于虛擬接口對(duì)和網(wǎng)橋,下面先來(lái)了解一下這部分的內(nèi)容。
為了讓一個(gè) Pod 與其他 Pod 通信,它必須先訪問(wèn)節(jié)點(diǎn)的根命名空間。
通過(guò)虛擬以太網(wǎng)對(duì)來(lái)實(shí)現(xiàn) Pod 和根命名空間的連接。
這些虛擬接口設(shè)備(veth 中的 v)連接并充當(dāng)兩個(gè)命名空間之間的隧道。
使用此 veth 設(shè)備,你將一端連接到 Pod 的命名空間,另一端連接到根命名空間。
CNI 可以幫你執(zhí)行這些操作,但你也可以手動(dòng)執(zhí)行:
$iplinkaddveth1netnsPod-namespacetypevethpeerveth2netnsroot
現(xiàn)在 Pod 的命名空間有一個(gè)可以訪問(wèn)根命名空間的 隧道。
節(jié)點(diǎn)上,新建的每一個(gè) Pod 都會(huì)設(shè)置這樣的 veth 對(duì)。
一個(gè)是,創(chuàng)建接口對(duì);另一個(gè)是為以太網(wǎng)設(shè)備分配地址并配置默認(rèn)路由。
下面看看如何在 Pod 的命名空間中設(shè)置 veth1 接口:
$ipnetnsexeccni-0f226515-e28b-df13-9f16-dd79456825acipaddradd10.244.4.40/24devveth1 $ipnetnsexeccni-0f226515-e28b-df13-9f16-dd79456825aciplinksetveth1up $ipnetnsexeccni-0f226515-e28b-df13-9f16-dd79456825aciprouteadddefaultvia10.244.4.40
在節(jié)點(diǎn)上,讓我們創(chuàng)建另一個(gè) veth2 對(duì):
$ipaddradd169.254.132.141/16devveth2 $iplinksetveth2up
可以像前面一樣檢查現(xiàn)有的 veth 對(duì)。
在 Pod 的命名空間中,檢索 eth0 接口的后綴。
$ipnetnsexeccni-0f226515-e28b-df13-9f16-dd79456825aciplinkshowtypeveth 3:eth0@if12:mtu1450qdiscnoqueuestateUPmodeDEFAULTgroupdefault link/ether16f856:77brdffffff:fflink-netnsid0
在這種情況下,可以使用命令 grep -A1 ^12 查找(或滾動(dòng)到目標(biāo)所在處):
$iplinkshowtypeveth #outputtruncated 12:cali97e50e215bd@if3:mtu1450qdiscnoqueuestateUPmodeDEFAULTgroupdefault link/ethereeeeee:eebrdffffff:fflink-netnscni-0f226515-e28b-df13-9f16-dd79456825ac
也可以使用 ip -n cni-0f226515-e28b-df13-9f16-dd79456825ac link show type veth.命令
注意 3: eth0@if12和12: cali97e50e215bd@if3 接口上的符號(hào)。
從 Pod 命名空間,該 eth0 接口連接到根命名空間的 12 號(hào)接口,因此是 @if12.
在 veth 對(duì)的另一端,根命名空間連接到 Pod 命名空間的 3 號(hào)接口。
接下來(lái)是連接 veth 對(duì)兩端的橋接器。
Pod 網(wǎng)絡(luò)命名空間連接到以太網(wǎng)橋
網(wǎng)橋會(huì)匯聚位于根命名空間中的每一個(gè)虛擬接口。這個(gè)網(wǎng)橋允許虛擬 pair 之間的流量,也允許穿過(guò)公共根命名空間的流量。
補(bǔ)充一下相關(guān)原理。
以太網(wǎng)橋位于 OSI 網(wǎng)絡(luò)模型 的第 2 層。
你可以將網(wǎng)橋視為接受來(lái)自不同命名空間和接口的連接的虛擬交換機(jī)。
以太網(wǎng)橋可以連接節(jié)點(diǎn)上的多個(gè)可用網(wǎng)絡(luò)。
因此,可以使用網(wǎng)橋連接兩個(gè)接口,即 Pod 命名空間的 veth 連接到同一節(jié)點(diǎn)上另一個(gè) Pod 的 veth。
接下來(lái),繼續(xù)看網(wǎng)橋和 veth 對(duì)的用途。
跟蹤在同一節(jié)點(diǎn)上 Pod 到 Pod 的流量
假設(shè)同一個(gè)節(jié)點(diǎn)上有兩個(gè) Pod,Pod-A 向 Pod-B 發(fā)送消息。
由于訪問(wèn)目標(biāo)不在同一個(gè)命名空間,Pod-A 將數(shù)據(jù)包發(fā)送到其默認(rèn)接口 eth0。這個(gè)接口與 veth 對(duì)的一端綁定,作為隧道。這樣,數(shù)據(jù)包會(huì)被轉(zhuǎn)發(fā)到節(jié)點(diǎn)上的根命名空間。
? 2. 以太網(wǎng)網(wǎng)橋作為一個(gè)虛擬交換機(jī),需要目標(biāo) Pod-B 的 MAC 地址才能工作。
? 3. ARP 協(xié)議會(huì)解決這個(gè)問(wèn)題。當(dāng)幀到達(dá)網(wǎng)橋時(shí),會(huì)向所有連接的設(shè)備發(fā)送 ARP 廣播。網(wǎng)橋廣播詢問(wèn)持有 Pod-B 的 IP 地址
? 4. 此時(shí)會(huì)收到一個(gè)帶有 Pod-B IP 的 MAC 地址應(yīng)答,這條消息會(huì)被存儲(chǔ)在橋接 ARP 緩存(查找表)中。
? 5. IP 地址和 MAC 地址的映射關(guān)系存儲(chǔ)之后,網(wǎng)橋就在表中查找,并將數(shù)據(jù)包轉(zhuǎn)發(fā)到正確的端點(diǎn)。數(shù)據(jù)包到達(dá)根命名空間內(nèi) Pod-B 的 veth 之后,很快又到達(dá) Pod-B 命名空間內(nèi)的 eth0 接口。
至此,Pod-A 和 Pod-B 之間的通信就成功了。
跟蹤不同節(jié)點(diǎn)上的 Pod 到 Pod 通信
對(duì)于跨節(jié)點(diǎn) Pod 之間的通信,會(huì)經(jīng)過(guò)額外的通信跳躍。
前幾個(gè)步驟保持不變,直到數(shù)據(jù)包到達(dá)根命名空間并需要發(fā)送到 Pod-B。
? 2. 當(dāng)目的 IP 不在本地網(wǎng)絡(luò)中時(shí),報(bào)文被轉(zhuǎn)發(fā)到節(jié)點(diǎn)的默認(rèn)網(wǎng)關(guān)。節(jié)點(diǎn)的出口網(wǎng)關(guān)或默認(rèn)網(wǎng)關(guān),通常位于節(jié)點(diǎn)與網(wǎng)絡(luò)相連的物理接口 eth0 上。
此時(shí) 不會(huì)發(fā)生 ARP 解析,因?yàn)樵?IP 和目標(biāo) IP 不在同一個(gè)網(wǎng)段中。
網(wǎng)段的檢查是使用按位運(yùn)算完成的。
當(dāng)目的 IP 不在當(dāng)前網(wǎng)絡(luò)段時(shí),數(shù)據(jù)包被轉(zhuǎn)發(fā)到節(jié)點(diǎn)的默認(rèn)網(wǎng)關(guān)。
按位運(yùn)算的工作原理
在確定數(shù)據(jù)包的轉(zhuǎn)發(fā)位置時(shí),源節(jié)點(diǎn)必須執(zhí)行位運(yùn)算
這也稱為與操作。
復(fù)習(xí)一下,按位與運(yùn)算的規(guī)則:
0AND0=0 0AND1=0 1AND0=0 1AND1=1
除了 1 與 1 以外的都是 false。
如果源節(jié)點(diǎn)的 IP 為 192.168.1.1,子網(wǎng)掩碼為 /24,目標(biāo) IP 為 172.16.1.1/16,則按位與運(yùn)算將得知它們位于不同的網(wǎng)段上。
這意味著目標(biāo) IP 與數(shù)據(jù)包的源不在同一個(gè)網(wǎng)絡(luò)上,數(shù)據(jù)包將通過(guò)默認(rèn)網(wǎng)關(guān)轉(zhuǎn)發(fā)。
數(shù)學(xué)時(shí)間。
我們必須從二進(jìn)制的 32 位地址開(kāi)始進(jìn)行 AND 操作。
先找出源 IP 網(wǎng)絡(luò)和目標(biāo) IP 網(wǎng)段。
Type | Binary | Converted |
---|---|---|
Src. IP Address | 11000000.10101000.00000001.00000001 | 192.168.1.1 |
Src. Subnet Mask | 11111111.11111111.11111111.00000000 | 255.255.255.0(/24) |
Src. Network | 11000000.10101000.00000001.00000000 | 192.168.1.0 |
Dst. IP Address | 10101100.00010000.00000001.00000001 | 172.16.1.1 |
Dst. Subnet Mask | 11111111.11111111.00000000.00000000 | 255.255.0.0(/16) |
Dst. Network | 10101100.00010000.00000000.00000000 | 172.16.0.0 |
按位運(yùn)算之后,需要將目標(biāo) IP 與數(shù)據(jù)包源節(jié)點(diǎn)的子網(wǎng)進(jìn)行比較。
Type | Binary | Converted |
---|---|---|
Dst. IP Address | 10101100.00010000.00000001.00000001 | 172.16.1.1 |
Src. Subnet Mask | 11111111.11111111.11111111.00000000 | 255.255.255.0(/24) |
Network Result | 10101100.00010000.00000001.00000000 | 172.16.1.0 |
運(yùn)算的結(jié)果是 172.16.1.0,不等于 192.168.1.0(源節(jié)點(diǎn)的網(wǎng)絡(luò))。說(shuō)明源 IP 地址和目標(biāo) IP 地址不在同一個(gè)網(wǎng)絡(luò)上。
如果目標(biāo) IP 是 192.168.1.2,即與發(fā)送 IP 在同一子網(wǎng)中,則 AND 操作將得到節(jié)點(diǎn)的本地網(wǎng)絡(luò)。
Type | Binary | Converted |
---|---|---|
Dst. IP Address | 11000000.10101000.00000001.00000010 | 192.168.1.2 |
Src. Subnet Mask | 11111111.11111111.11111111.00000000 | 255.255.255.0(/24) |
Network | 11000000.10101000.00000001.00000000 | 192.168.1.0 |
進(jìn)行逐位比較后,ARP 通過(guò)查找表查找默認(rèn)網(wǎng)關(guān)的 MAC 地址。
如果有條目,將立即轉(zhuǎn)發(fā)數(shù)據(jù)包。
否則,先進(jìn)行廣播以找到網(wǎng)關(guān)的 MAC 地址。
現(xiàn)在,數(shù)據(jù)包路由到另一個(gè)節(jié)點(diǎn)的默認(rèn)接口,我們稱為 Node-B。
以相反的順序。現(xiàn)在,數(shù)據(jù)包位于 Node-B 的根命名空間,并到達(dá)網(wǎng)橋,這里會(huì)進(jìn)行 ARP 解析。
路由系統(tǒng)將返回與 Pod-B 相連的接口的 MAC 地址。
? 4. 網(wǎng)橋通過(guò) Pod-B 的 veth 設(shè)備轉(zhuǎn)發(fā)幀,并到達(dá) Pod-B 的命名空間。
至此,你應(yīng)該已經(jīng)熟悉了 Pod 之間的流量是如何流轉(zhuǎn)的。下面,讓我們花點(diǎn)時(shí)間來(lái)看看 CNI 如何管理上訴內(nèi)容。
容器網(wǎng)絡(luò)接口 - CNI
容器網(wǎng)絡(luò)接口(CNI)主要關(guān)注的是當(dāng)前節(jié)點(diǎn)中的網(wǎng)絡(luò)。
可以將 CNI 看作為解決 Kubernetes 網(wǎng)絡(luò)需求,而遵循的一組規(guī)則。
有這些 CNI 實(shí)現(xiàn)可供使用:
Calico
Cillium
Flannel
Weave Net
其他網(wǎng)絡(luò)插件
他們都遵循相同的 CNI 標(biāo)準(zhǔn)。
如果沒(méi)有 CNI,你需要人工完成如下操作:
創(chuàng)建接口。
創(chuàng)建 veth 對(duì)。
設(shè)置網(wǎng)絡(luò)命名空間。
設(shè)置靜態(tài)路由。
配置以太網(wǎng)橋。
分配 IP 地址。
創(chuàng)建 NAT 規(guī)則。
還有其他大量事情。
這還不包括,在刪除或重啟 Pod 時(shí),需要進(jìn)行類似的全部操作。
CNI 必須支持四種不同的操作:
ADD - 向網(wǎng)絡(luò)添加一個(gè)容器。
DEL - 從網(wǎng)絡(luò)中刪除一個(gè)容器。
CHECK - 如果容器的網(wǎng)絡(luò)出現(xiàn)問(wèn)題,則返回錯(cuò)誤。
VERSION - 顯示插件的版本。
我們一起看下,CNI 是如何工作的。
當(dāng) Pod 被分配到特定節(jié)點(diǎn)時(shí),Kubelet 自身不會(huì)初始化網(wǎng)絡(luò)。
相反,Kubelet 將這個(gè)任務(wù)交給 CNI。
但是,Kubelet 以 JSON 格式指定配置并發(fā)送至 CNI 插件。
你可以進(jìn)入節(jié)點(diǎn)上的 /etc/cni/net.d 文件夾,使用以下命令查看當(dāng)前的 CNI 配置文件:
$cat10-calico.conflist { "name":"k8s-Pod-network", "cniVersion":"0.3.1", "plugins":[ { "type":"calico", "datastore_type":"kubernetes", "mtu":0, "nodename_file_optional":false, "log_level":"Info", "log_file_path":"/var/log/calico/cni/cni.log", "ipam":{"type":"calico-ipam","assign_ipv4":"true","assign_ipv6":"false"}, "container_settings":{ "allow_ip_forwarding":false }, "policy":{ "type":"k8s" }, "kubernetes":{ "k8s_api_root":"https://10.96.0.1:443", "kubeconfig":"/etc/cni/net.d/calico-kubeconfig" } }, { "type":"bandwidth", "capabilities":{"bandwidth":true} }, {"type":"portmap","snat":true,"capabilities":{"portMappings":true}} ] }
每個(gè) CNI 插件都會(huì)使用不同類型的網(wǎng)絡(luò)配置。
例如,Calico 使用基于 BGP 的三層網(wǎng)絡(luò)連接 Pod
Cilium 從三層到七層使用的是基于 eBPF 的 overlay 網(wǎng)絡(luò)
與 Calico 一樣,Cilium 也支持通過(guò)配置網(wǎng)絡(luò)策略來(lái)限制流量。
那么你應(yīng)該使用哪一個(gè)呢?主要有兩類 CNI。
在第一類中,使用基本網(wǎng)絡(luò)設(shè)置(也稱為平面網(wǎng)絡(luò)),從集群的 IP 池為 Pod 分配 IP 地址的 CNI。
這種方式可能很快耗盡 IP 地址,而成為負(fù)擔(dān)。
相反,另一類是使用 overlay 網(wǎng)絡(luò)。
簡(jiǎn)單來(lái)說(shuō),overlay 網(wǎng)絡(luò)是主(底層)網(wǎng)絡(luò)之上的重建網(wǎng)絡(luò)。
overlay 網(wǎng)絡(luò)通過(guò)封裝來(lái)自底層網(wǎng)絡(luò)的數(shù)據(jù)包工作,這些數(shù)據(jù)包被發(fā)送到另一個(gè)節(jié)點(diǎn)上的 Pod。
overlay 網(wǎng)絡(luò)的一種流行技術(shù)是 VXLAN,它可以在 L3 網(wǎng)絡(luò)上建立 L2 域的隧道。
那么哪個(gè)更好呢?
沒(méi)有單一的答案,這取決于你的需求。
你是否正在構(gòu)建具有數(shù)萬(wàn)個(gè)節(jié)點(diǎn)的大型集群?
也許 overlay 網(wǎng)絡(luò)更好。
你是否在意更簡(jiǎn)單的配置和審查網(wǎng)絡(luò)流量,而不會(huì)愿意在復(fù)雜網(wǎng)絡(luò)中丟失這種能力?
扁平網(wǎng)絡(luò)更適合你。
現(xiàn)在我們討論完了 CNI,接著讓我們來(lái)看看 Pod 到服務(wù)的通信是如何連接的。
檢查 Pod 到 Service 的流量
由于 Pod 在 Kubernetes 中是動(dòng)態(tài)的,分配給 Pod 的 IP 地址不是靜態(tài)的。
Pod 的 IP 是短暫的,每次創(chuàng)建或刪除 Pod 時(shí)都會(huì)發(fā)生變化。
Kubernetes 中的 Service 解決了這個(gè)問(wèn)題,為連接一組 Pod 提供了可靠的機(jī)制。
默認(rèn)情況下,在 Kubernetes 中創(chuàng)建 Service 時(shí),被分配一個(gè)虛擬 IP。
在 Service 中,可以使用選擇器將 Service 與目標(biāo) Pod 相關(guān)聯(lián)。
當(dāng)刪除或添加一個(gè) Pod 時(shí)會(huì)發(fā)生什么呢?
Service 的虛擬 IP 保持靜態(tài)不變。
但流量可以再無(wú)需干預(yù)的情況下,到達(dá)新創(chuàng)建的 Pod。
換句話說(shuō),Kubernetes 中的 Service 類似于負(fù)載均衡器。
但它們是如何工作的?
使用 Netfilter 和 Iptables 攔截和重寫流量
Kubernetes 中的 Service 是基于 Linux 內(nèi)核中的兩個(gè)組件構(gòu)建的:
網(wǎng)絡(luò)過(guò)濾器
iptables
Netfilter是一個(gè)可以配置數(shù)據(jù)包過(guò)濾、創(chuàng)建NAT、端口轉(zhuǎn)發(fā)規(guī)則以及管理網(wǎng)絡(luò)中流量的框架
此外,它可以屏蔽和禁止未經(jīng)同意的訪問(wèn)。
另一方面,iptables 是一個(gè)用戶態(tài)程序,可以用來(lái)配置 Linux 內(nèi)核防火墻的 IP 數(shù)據(jù)包過(guò)濾規(guī)則。
iptables 是作為不同的 Netfilter 模塊實(shí)現(xiàn)的。
可以使用 iptables CLI 即時(shí)修改過(guò)濾規(guī)則,并將它們插入 netfilters 掛載點(diǎn)。
過(guò)濾器配置在不同的表中,其中包含用于處理網(wǎng)絡(luò)流量數(shù)據(jù)包的鏈。
不同的協(xié)議使用不同的內(nèi)核模塊和程序。
當(dāng)提到 iptables 時(shí),通常指的是 IPv4。對(duì)于 IPv6 ,終端工具是 ip6tables。
iptables 有五種鏈,每一種鏈都直接映射到 Netfilter 的鉤子上。
從 iptables 的角度來(lái)看,它們是:
PRE_ROUTING
INPUT
FORWARD
OUTPUT
POST_ROUTING
它們對(duì)應(yīng)地映射到 Netfilter 鉤子:
NF_IP_PRE_ROUTING
NF_IP_LOCAL_IN
NF_IP_FORWARD
NF_IP_LOCAL_OUT
NF_IP_POST_ROUTING
當(dāng)一個(gè)數(shù)據(jù)包到達(dá)時(shí),根據(jù)它所處的階段,將 “觸發(fā)” 一個(gè) Netfilter 鉤子。這個(gè)鉤子會(huì)執(zhí)行特定的 iptables 過(guò)濾規(guī)則。
哎呀!看起來(lái)很復(fù)雜!
不過(guò)沒(méi)什么好擔(dān)心的。
這就是我們使用 Kubernetes 的原因,以上所有內(nèi)容都是通過(guò)使用 Service 抽象出來(lái)的,并且一個(gè)簡(jiǎn)單的 YAML 定義可以自動(dòng)設(shè)置這些規(guī)則。
如果你有興趣查看 iptables 規(guī)則,可以連接到節(jié)點(diǎn)并運(yùn)行:
$iptables-save
你還可以使用這個(gè)工具來(lái)可視化節(jié)點(diǎn)上的 iptables 鏈。
這是來(lái)自 GKE 節(jié)點(diǎn)上的可視化 iptables 鏈的示例圖:
注意,這里可能配置了幾百條規(guī)則,想想一下自己動(dòng)手怎么配置!
至此,我們已經(jīng)了解了,相同節(jié)點(diǎn)上的 Pod 和不同節(jié)點(diǎn)上 Pod 之間是如何通信的。
在 Pod 與 Service 的通信中,鏈路的前半部分是一樣的。
當(dāng)請(qǐng)求從 Pod-A 走向 Pod-B 時(shí),由于 Pod-B 在 Service 的 “后面”,在傳輸?shù)倪^(guò)程中,會(huì)有一些不一樣。
原始的請(qǐng)求,在 Pod-A 命名空間的 eth0 接口發(fā)出。
接著,請(qǐng)求通過(guò) veth到達(dá)根名稱空間的網(wǎng)橋。
一旦到達(dá)網(wǎng)橋,數(shù)據(jù)包就會(huì)立即通過(guò)默認(rèn)網(wǎng)關(guān)轉(zhuǎn)發(fā)。
與 Pod-to-Pod 部分一樣,主機(jī)進(jìn)行按位比較。由于服務(wù)的虛擬 IP 不是節(jié)點(diǎn) CIDR 的一部分,因此數(shù)據(jù)包將立即通過(guò)默認(rèn)網(wǎng)關(guān)轉(zhuǎn)發(fā)。
如果默認(rèn)網(wǎng)關(guān)的 MAC 地址尚未出現(xiàn)在查找表中,則會(huì)進(jìn)行 ARP 解析找出默認(rèn)網(wǎng)關(guān)的 MAC 地址。
現(xiàn)在神奇的事情發(fā)生了。
在數(shù)據(jù)包通過(guò)節(jié)點(diǎn)的路由之前,Netfilter 的 NF_IP_PRE_ROUTING 掛鉤被觸發(fā),并執(zhí)行 iptables 規(guī)則。這個(gè)規(guī)則會(huì)修改 Pod-A 數(shù)據(jù)包的目標(biāo) IP 地址 DNAT。
前面服務(wù)的虛擬 IP 地址被重寫為 Pod-B 的 IP 地址。
接下來(lái),數(shù)據(jù)包路由過(guò)程與 Pod 到 Pod 的通信一樣。
數(shù)據(jù)包重寫后,通信是 Pod 到 Pod。
然而,在所有這些通信中,使用了一個(gè)第三方的功能。
此功能稱為 conntrack 或鏈路跟蹤。
當(dāng) Pod-B 發(fā)回響應(yīng)時(shí),conntrack 會(huì)將數(shù)據(jù)包與鏈路相關(guān)聯(lián),并跟蹤其來(lái)源。
NAT 嚴(yán)重依賴于 conntrack。
如果沒(méi)有鏈路跟蹤,將不知道將包含響應(yīng)的數(shù)據(jù)包發(fā)回何處。
使用 conntrack 時(shí),數(shù)據(jù)包的返回路徑很容易設(shè)置為相同的源或目標(biāo) NAT 更改。
通信的另一部分與現(xiàn)在的鏈路相反。
Pod-B 接收并處理了請(qǐng)求,現(xiàn)在將數(shù)據(jù)發(fā)送回 Pod-A。
現(xiàn)在會(huì)發(fā)生什么呢?
檢查來(lái)自服務(wù)的響應(yīng)
Pod-B 發(fā)送響應(yīng),將其 IP 地址設(shè)置為源地址,并將 Pod-A 的 IP 地址設(shè)置為目標(biāo)地址。
當(dāng)數(shù)據(jù)包到達(dá) Pod-A 所在節(jié)點(diǎn)的接口時(shí),會(huì)發(fā)生另一個(gè) NAT。
這時(shí),conntrack 開(kāi)始工作,修改源 IP 地址,iptables 規(guī)則執(zhí)行 SNAT,并將 Pod-B 的源 IP 地址修改為原始服務(wù)的虛擬 IP。
對(duì)于 Pod-A 來(lái)說(shuō),響應(yīng)是來(lái)自于 Service 而不是 Pod-B。
其余的都是一樣的。一旦 SNAT 完成,數(shù)據(jù)包就會(huì)到達(dá)根命名空間中的網(wǎng)橋,并通過(guò) veth 對(duì)轉(zhuǎn)發(fā)到 Pod-A。
總 結(jié)
讓我們一起回顧下本文相關(guān)要點(diǎn)
容器如何在本地或 Pod 內(nèi)通信。
在相同節(jié)點(diǎn)和不同節(jié)點(diǎn)上的 Pod 如何通信。
Pod-to-Service - Pod 如何將流量發(fā)送到 Kubernetes 中服務(wù)后面的 Pod 時(shí)。
什么是命名空間、veth、iptables、chains、conntrack、Netfilter、CNI、overlay 網(wǎng)絡(luò),以及 Kubernetes 網(wǎng)絡(luò)工具箱中所需的一切。
審核編輯:湯梓紅
-
Linux
+關(guān)注
關(guān)注
87文章
11511瀏覽量
213837 -
容器
+關(guān)注
關(guān)注
0文章
511瀏覽量
22458 -
網(wǎng)絡(luò)流量
+關(guān)注
關(guān)注
0文章
61瀏覽量
10710 -
kubernetes
+關(guān)注
關(guān)注
0文章
245瀏覽量
9070
原文標(biāo)題:跟蹤 Kubernetes 的網(wǎng)絡(luò)流量路徑
文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
運(yùn)營(yíng)商將能夠應(yīng)對(duì)網(wǎng)絡(luò)流量的增長(zhǎng)
分布式網(wǎng)絡(luò)流量監(jiān)測(cè)
網(wǎng)絡(luò)流量測(cè)量的研究與實(shí)現(xiàn)
淺談基于PCA的網(wǎng)絡(luò)流量分析
網(wǎng)絡(luò)流量監(jiān)測(cè)和管理平臺(tái)的設(shè)計(jì)與實(shí)現(xiàn)
基于分組采樣組播網(wǎng)絡(luò)流量預(yù)測(cè)研究
基于FPGA的網(wǎng)絡(luò)流量計(jì)設(shè)計(jì)與實(shí)現(xiàn)
基于多組博弈的新型網(wǎng)絡(luò)流量控制模型
基于混沌理論和神經(jīng)網(wǎng)絡(luò)的網(wǎng)絡(luò)流量預(yù)測(cè)
基于FPGA的網(wǎng)絡(luò)流量計(jì)設(shè)計(jì)與實(shí)現(xiàn)
基于覆蓋網(wǎng)絡(luò)的多路徑流量均衡研究

基于KPCA優(yōu)化ESN的網(wǎng)絡(luò)流量預(yù)測(cè)方法_田中大
基于 ossim 的網(wǎng)絡(luò)流量監(jiān)控系統(tǒng)的分析

IP網(wǎng)絡(luò)流量矩陣估計(jì)方法

評(píng)論