你一直在Kubernetes集群中運(yùn)行一系列服務(wù)并已從中獲益,或者你正打算這么做。盡管有一系列工具能幫助你建立并管理集群,你仍困惑于集群底層是如何工作的,以及出現(xiàn)問(wèn)題該如何處理。我曾經(jīng)就是這樣的。
誠(chéng)然Kubernetes對(duì)初學(xué)者來(lái)說(shuō)已足夠易用,但我們?nèi)匀徊坏貌怀姓J(rèn),它的底層實(shí)現(xiàn)異常復(fù)雜。Kubernetes由許多部件組成,如果你想對(duì)失敗場(chǎng)景做好應(yīng)對(duì)準(zhǔn)備,那么你必須知道各部件是如何協(xié)調(diào)工作的。其中一個(gè)最復(fù)雜,甚至可以說(shuō)是最關(guān)鍵的部件就是網(wǎng)絡(luò)。因此我著手精確理解Kubernetes網(wǎng)絡(luò)是如何工作的。我閱讀了許多文章,看了很多演講,甚至瀏覽了代碼庫(kù)。以下就是我的所得。
Kubernetes網(wǎng)絡(luò)模型核心點(diǎn)是,Kubernetes網(wǎng)絡(luò)有一個(gè)重要的基本設(shè)計(jì)原則:每個(gè)Pod擁有唯一的IP。這個(gè)Pod IP被該P(yáng)od內(nèi)的所有容器共享,并且其它所有Pod都可以路由到該P(yáng)od。你可曾注意到,你的Kubernetes節(jié)點(diǎn)上運(yùn)行著一些"pause"容器?
它們被稱(chēng)作“沙盒容器(sandbox containers)",其唯一任務(wù)是保留并持有一個(gè)網(wǎng)絡(luò)命名空間(netns),該命名空間被Pod內(nèi)所有容器共享。
通過(guò)這種方式,即使一個(gè)容器死掉,新的容器創(chuàng)建出來(lái)代替這個(gè)容器,Pod IP也不會(huì)改變。這種IP-per-pod模型的巨大優(yōu)勢(shì)是,Pod和底層主機(jī)不會(huì)有IP或者端口沖突。我們不用擔(dān)心應(yīng)用使用了什么端口。這點(diǎn)滿(mǎn)足后,Kubernetes唯一的要求是,這些Pod IP可被其它所有Pod訪問(wèn),不管那些Pod在哪個(gè)節(jié)點(diǎn)。
節(jié)點(diǎn)內(nèi)通信第一步是確保同一節(jié)點(diǎn)上的Pod可以相互通信,然后可以擴(kuò)展到跨節(jié)點(diǎn)通信、internet上的通信,等等。
Kubernetes Node(root network namespace)在每個(gè)Kubernetes節(jié)點(diǎn)(本場(chǎng)景指的是Linux機(jī)器)上,都有一個(gè)根(root)命名空間(root是作為基準(zhǔn),而不是超級(jí)用戶(hù))--root netns。最主要的網(wǎng)絡(luò)接口 eth0 就是在這個(gè)root netns下。
Kubernetes Node(pod network namespace)類(lèi)似的,每個(gè)Pod都有其自身的netns,通過(guò)一個(gè)虛擬的以太網(wǎng)對(duì)連接到root netns。這基本上就是一個(gè)管道對(duì),一端在root netns內(nèi),另一端在Pod的nens內(nèi)。我們把Pod端的網(wǎng)絡(luò)接口叫 eth0,這樣Pod就不需要知道底層主機(jī),它認(rèn)為它擁有自己的根網(wǎng)絡(luò)設(shè)備。另一端命名成比如 vethxxx。你可以用ifconfig 或者 ip a 命令列出你的節(jié)點(diǎn)上的所有這些接口。
Kubernetes Node(linux network bridge)節(jié)點(diǎn)上的所有Pod都會(huì)完成這個(gè)過(guò)程。這些Pod要相互通信,就要用到linux的以太網(wǎng)橋 cbr0 了。Docker使用了類(lèi)似的網(wǎng)橋,稱(chēng)為docker0。你可以用 brctl show 命令列出所有網(wǎng)橋。
Kubernetes Node(same node pod-to-pod communication)假設(shè)一個(gè)網(wǎng)絡(luò)數(shù)據(jù)包要由pod1到pod2。
它由pod1中netns的eth0網(wǎng)口離開(kāi),通過(guò)vethxxx進(jìn)入root netns。
然后被傳到cbr0,cbr0使用ARP請(qǐng)求,說(shuō)“誰(shuí)擁有這個(gè)IP”,從而發(fā)現(xiàn)目標(biāo)地址。
vethyyy說(shuō)它有這個(gè)IP,因此網(wǎng)橋就知道了往哪里轉(zhuǎn)發(fā)這個(gè)包。
數(shù)據(jù)包到達(dá)vethyyy,跨過(guò)管道對(duì),到達(dá)pod2的netns。這就是同一節(jié)點(diǎn)內(nèi)容器間通信的流程。
當(dāng)然也可以用其它方式,但是無(wú)疑這是最簡(jiǎn)單的方式,同時(shí)也是Docker采用的方式。節(jié)點(diǎn)間通信正如我前面提到,Pod也需要跨節(jié)點(diǎn)可達(dá)。Kubernetes不關(guān)心如何實(shí)現(xiàn)。我們可以使用L2(ARP跨節(jié)點(diǎn)),L3(IP路由跨節(jié)點(diǎn),就像云提供商的路由表),Overlay網(wǎng)絡(luò),或者甚至信鴿。無(wú)所謂,只要流量能到達(dá)另一個(gè)節(jié)點(diǎn)的期望Pod就好。每個(gè)節(jié)點(diǎn)都為Pod IPs分配了唯一的CIDR塊(一段IP地址范圍),因此每個(gè)Pod都擁有唯一的IP,不會(huì)和其它節(jié)點(diǎn)上的Pod沖突。大多數(shù)情況下,特別是在云環(huán)境上,云提供商的路由表就能確保數(shù)據(jù)包到達(dá)正確的目的地。我們?cè)诿總€(gè)節(jié)點(diǎn)上建立正確的路由也能達(dá)到同樣的目的。許多其它的網(wǎng)絡(luò)插件通過(guò)自己的方式達(dá)到這個(gè)目的。
這里我們有兩個(gè)節(jié)點(diǎn),與之前看到的類(lèi)似。每個(gè)節(jié)點(diǎn)有不同的網(wǎng)絡(luò)命名空間、網(wǎng)絡(luò)接口以及網(wǎng)橋。
Kubernetes Nodes with route table(cross node pod-to-pod communication)假設(shè)一個(gè)數(shù)據(jù)包要從pod1到達(dá)pod4(在不同的節(jié)點(diǎn)上)。它由pod1中netns的eth0網(wǎng)口離開(kāi),通過(guò)vethxxx進(jìn)入root netns。然后被傳到cbr0,cbr0通過(guò)發(fā)送ARP請(qǐng)求來(lái)找到目標(biāo)地址。
本節(jié)點(diǎn)上沒(méi)有Pod擁有pod4的IP地址,因此數(shù)據(jù)包由cbr0 傳到 主網(wǎng)絡(luò)接口 eth0。數(shù)據(jù)包的源地址為pod1,目標(biāo)地址為pod4,它以這種方式離開(kāi)node1進(jìn)入電纜。路由表有每個(gè)節(jié)點(diǎn)的CIDR塊的路由設(shè)定,它把數(shù)據(jù)包路由到CIDR塊包含pod4的IP的節(jié)點(diǎn)。因此數(shù)據(jù)包到達(dá)了node2的主網(wǎng)絡(luò)接口eth0。現(xiàn)在即使pod4不是eth0的IP,數(shù)據(jù)包也仍然能轉(zhuǎn)發(fā)到cbr0,因?yàn)楣?jié)點(diǎn)配置了IP forwarding enabled。節(jié)點(diǎn)的路由表尋找任意能匹配pod4 IP的路由。它發(fā)現(xiàn)了 cbr0 是這個(gè)節(jié)點(diǎn)的CIDR塊的目標(biāo)地址。你可以用route -n命令列出該節(jié)點(diǎn)的路由表,它會(huì)顯示cbr0的路由,類(lèi)型如下:
網(wǎng)橋接收了數(shù)據(jù)包,發(fā)送ARP請(qǐng)求,發(fā)現(xiàn)目標(biāo)IP屬于vethyyy。數(shù)據(jù)包跨過(guò)管道對(duì)到達(dá)pod4。這就是Kubernetes網(wǎng)絡(luò)的基礎(chǔ)。下次你碰到問(wèn)題,務(wù)必先檢查這些網(wǎng)橋和路由表。前面我們漫談了Kubernetes的網(wǎng)絡(luò)模型。
我們觀察了數(shù)據(jù)包是如何在同一節(jié)點(diǎn)上的pod 間和跨節(jié)點(diǎn)的 pod 間流動(dòng)的。我們也注意到了Linux網(wǎng)橋和路由表在這個(gè)過(guò)程中所扮演的角色。現(xiàn)在我們將進(jìn)一步闡述這些概念,并闡述Overlay網(wǎng)絡(luò)是如何工作的。我們也將理解Kubernetes千變?nèi)f化的Pod是如何從運(yùn)行的應(yīng)用中抽象出來(lái),并在幕后處理的。
Overlay 網(wǎng)絡(luò)Overlay網(wǎng)絡(luò)不是默認(rèn)必須的,但是它們?cè)谔囟▓?chǎng)景下非常有用。比如當(dāng)我們沒(méi)有足夠的IP空間,或者網(wǎng)絡(luò)無(wú)法處理額外路由,抑或當(dāng)我們需要Overlay提供的某些額外管理特性。一個(gè)常見(jiàn)的場(chǎng)景是當(dāng)云提供商的路由表能處理的路由數(shù)是有限制時(shí)。例如,AWS路由表最多支持50條路由才不至于影響網(wǎng)絡(luò)性能。因此如果我們有超過(guò)50個(gè)Kubernetes節(jié)點(diǎn),AWS路由表將不夠。這種情況下,使用Overlay網(wǎng)絡(luò)將幫到我們。
本質(zhì)上來(lái)說(shuō),Overlay就是在跨節(jié)點(diǎn)的本地網(wǎng)絡(luò)上的包中再封裝一層包。你可能不想使用Overlay網(wǎng)絡(luò),因?yàn)樗鼤?huì)帶來(lái)由封裝和解封所有報(bào)文引起的時(shí)延和復(fù)雜度開(kāi)銷(xiāo)。通常這是不必要的,因此我們應(yīng)當(dāng)在知道為什么我們需要它時(shí)才使用它。
為了理解Overlay網(wǎng)絡(luò)中流量的流向,我們拿Flannel做例子,它是CoreOS 的一個(gè)開(kāi)源項(xiàng)目。
Kubernetes Node with route table(cross node pod-to-pop Traffic flow with flannel overlay network)這里我們注意到它和之前我們看到的設(shè)施是一樣的,只是在root netns中新增了一個(gè)虛擬的以太網(wǎng)設(shè)備,稱(chēng)為flannel0。它是虛擬擴(kuò)展網(wǎng)絡(luò)Virtual Extensible LAN(VXLAN)的一種實(shí)現(xiàn),但是在Linux上,它只是另一個(gè)網(wǎng)絡(luò)接口。從pod1到pod4(在不同節(jié)點(diǎn))的數(shù)據(jù)包的流向類(lèi)似如下:
1、它由pod1中netns的eth0網(wǎng)口離開(kāi),通過(guò)vethxxx進(jìn)入root netns。
2、然后被傳到cbr0,cbr0通過(guò)發(fā)送ARP請(qǐng)求來(lái)找到目標(biāo)地址。
3
a、由于本節(jié)點(diǎn)上沒(méi)有Pod擁有pod4的IP地址,因此網(wǎng)橋把數(shù)據(jù)包發(fā)送給了flannel0,因?yàn)楣?jié)點(diǎn)的路由表上flannel0被配成了Pod網(wǎng)段的目標(biāo)地址。
b、flanneld daemon和Kubernetes apiserver或者底層的etcd通信,它知道所有的Pod IP,并且知道它們?cè)谀膫€(gè)節(jié)點(diǎn)上。因此Flannel創(chuàng)建了Pod IP和Node IP之間的映射(在用戶(hù)空間)。flannel0取到這個(gè)包,并在其上再用一個(gè)UDP包封裝起來(lái),該UDP包頭部的源和目的IP分別被改成了對(duì)應(yīng)節(jié)點(diǎn)的IP,然后發(fā)送這個(gè)新包到特定的VXLAN端口(通常是8472)。
Packet-in-packet encapsulation(notice the packet is encapsulated from 3c to 6b in previous diagram)盡管這個(gè)映射發(fā)生在用戶(hù)空間,真實(shí)的封裝以及數(shù)據(jù)的流動(dòng)發(fā)生在內(nèi)核空間,因此仍然是很快的。
c、封裝后的包通過(guò)eth0發(fā)送出去,因?yàn)樗婕傲斯?jié)點(diǎn)間的路由流量。
4、包帶著節(jié)點(diǎn)IP信息作為源和目的地址離開(kāi)本節(jié)點(diǎn)。
5、云提供商的路由表已經(jīng)知道了如何在節(jié)點(diǎn)間發(fā)送報(bào)文,因此該報(bào)文被發(fā)送到目標(biāo)地址node2。
6
a、包到達(dá)node2的eth0網(wǎng)卡,由于目標(biāo)端口是特定的VXLAN端口,內(nèi)核將報(bào)文發(fā)送給了 flannel0。
b、flannel0解封報(bào)文,并將其發(fā)送到 root 命名空間下。從這里開(kāi)始,報(bào)文的路徑和我們之前在Part 1 中看到的非Overlay網(wǎng)絡(luò)就是一致的了。
c、由于IP forwarding開(kāi)啟著,內(nèi)核按照路由表將報(bào)文轉(zhuǎn)發(fā)給了cbr0。
7、網(wǎng)橋獲取到了包,發(fā)送ARP請(qǐng)求,發(fā)現(xiàn)目標(biāo)IP屬于vethyyy。
8、包跨過(guò)管道對(duì)到達(dá)pod4。這就是Kubernetes中Overlay網(wǎng)絡(luò)的工作方式,雖然不同的實(shí)現(xiàn)還是會(huì)有細(xì)微的差別。有個(gè)常見(jiàn)的誤區(qū)是,當(dāng)我們使用Kubernetes,我們就不得不使用Overlay網(wǎng)路。事實(shí)是,這完全依賴(lài)于特定場(chǎng)景。因此請(qǐng)確保在確實(shí)需要的場(chǎng)景下才使用。在前一部分我們學(xué)習(xí)了Kubernetes網(wǎng)絡(luò)的基礎(chǔ)知識(shí)。現(xiàn)在我們知道了Overlay網(wǎng)絡(luò)是如何工作的。在下一部分,我們將看到Pod創(chuàng)建和刪除過(guò)程中網(wǎng)絡(luò)變化是如何發(fā)生的,以及出站和進(jìn)站流量是如何流動(dòng)的。
總體而言我對(duì)網(wǎng)絡(luò)概念仍然是個(gè)新手,因此我非常期待能得到大家的反饋,特別是某些不清晰或者錯(cuò)誤的地方。