歡迎光臨
每天分享高質量文章

深入淺出Kubernetes網路:跨節點網路通訊之Flannel

曾記得有一位哲人說過:“在雲端計算當中,計算最基礎,儲存最重要,網路最複雜”,而PaaS雲平臺Kubernetes的出現也使得網路的應用場景變得更加複雜多變。本文試圖從Kubernetes當中容器跨節點網路通訊方案Flannel的實際應用場景出發,帶領讀者梳理Kubernetes當中容器跨節點網路通訊的實現過程以及背後的實現原理。
Kubernetes網路模型

 

透過上一篇文章《深入淺出Kubernetes網路:容器網路初探》我們知道了Docker透過VEth虛擬網路裝置以及Linux bridge實現了同一臺主機上的容器網路通訊,再透過iptables進行NAT實現了容器和外部網路的通訊。
這對於簡單部署的應用已經能夠滿足網路通訊的需求了,但是Kubernetes作為一個容器編排平臺,要處理的是生產環境當中高併發大規模的彈性擴充套件問題,首先要解決的就是整個叢集當中所有節點上的容器網路通訊問題。
為瞭解決Kubernetes當中網路通訊的問題,Kubernetes作為一個容器編排平臺提出了Kubernetes網路模型,但是並沒有自己去實現,具體網路通訊方案透過網路外掛來實現。
其實Kubernetes網路模型當中總共只作了三點要求:
  1. 執行在一個節點當中的Pod能在不經過NAT的情況下跟叢集中所有的Pod進行通訊

  2. 節點當中的客戶端(system daemon、kubelet)能跟該節點當中的所有Pod進行通訊

  3. 以host network樣式執行在一個節點上的Pod能跟叢集中所有的Pod進行通訊

從Kubernetes的網路模型我們可以看出來,在Kubernetes當中希望做到的是每一個Pod都有一個在叢集當中獨一無二的IP,並且可以透過這個IP直接跟叢集當中的其他Pod以及節點自身的網路進行通訊,一句話概括就是Kubernetes當中希望網路是扁平化的。
針對Kubernetes網路模型也湧現出了許多的實現方案,例如Calico、Flannel、Weave等等,雖然實現原理各有千秋,但都圍繞著同一個問題即如何實現Kubernetes當中的扁平網路進行展開。Kubernetes只需要負責編排排程相關的事情,修橋鋪路的事情交給相應的網路外掛即可。
Flannel簡介

 

Flannel專案為CoreOS團隊對Kubernetes網路設計實現的一種三層網路通訊方案,安裝部署方式可以參考官方示例檔案:https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml,故關於Flannel的安裝部署部分這裡暫時不進行贅述,有興趣的同學可以參考官方檔案進行部署測試。
為了便於理解和說明,以下內容將用一個1(master)+2(work node)的Kubernetes叢集進行舉例說明。 
在安裝部署完成之後應該能看到在各個節點上透過DaemonSet的方式運行了一個Flannel的Pod。
  1. [root@10-10-88-192 ~]# kubectl get daemonset -n kube-system -l app=flannel
  2. NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
  3. kube-flannel-ds 3 3 3 3 3 beta.kubernetes.io/arch=amd64 135d
  4. [root@10-10-88-192 ~]#
  5. [root@10-10-88-192 ~]#
  6. [root@10-10-88-192 ~]# kubectl get pod -n kube-system -o wide -l app=flannel
  7. NAME READY STATUS RESTARTS AGE IP NODE
  8. kube-flannel-ds-npcxv 1/1 Running 0 2h 172.16.130.164 10-10-88-170
  9. kube-flannel-ds-rv8wv 1/1 Running 0 2h 172.16.130.244 10-10-88-192
  10. kube-flannel-ds-t5zlv 1/1 Running 0 2h 172.16.130.140 10-10-88-195
  11. [root@10-10-88-192 ~]#
每一個Flannel的Pod當中都運行了一個flanneld行程,且flanneld的配置檔案以ConfigMap的形式掛載到容器內的/etc/kube-flannel/目錄供flanneld使用。
  1. [root@10-10-88-192 ~]# kubectl get cm -n kube-system -l app=flannel
  2. NAME DATA AGE
  3. kube-flannel-cfg 2 137d
  4. [root@10-10-88-192 ~]#
Flannel Backend

 

Flannel透過在每一個節點上啟動一個叫flanneld的行程,負責每一個節點上的子網劃分,並將相關的配置資訊如各個節點的子網網段、外部IP等儲存到etcd當中,而具體的網路包轉發交給具體的Backend來實現。
flanneld可以在啟動的時候透過配置檔案來指定不同的Backend來進行網路通訊,目前比較成熟的Backend有VXLAN、host-gw以及UDP三種方式,也已經有諸如AWS,GCE and AliVPC這些還在實驗階段的Backend。VXLAN是目前官方最推崇的一種Backend實現方式,host-gw一般用於對網路效能要求比較高的場景,但需要基礎架構本身的支援,UDP則一般用於Debug和一些比較老的不支援VXLAN的Linux核心。
這裡只展開講講最成熟也是最通用的三種Backend網路通訊實現流程:
  • UDP

  • VXLAN

  • host-gw

 

UDP
由於UDP樣式相對容易理解,故這裡先採用UDP這種Backend樣式進行舉例說明然後再對其他Backend樣式進行展開講解。
採用UDP樣式時需要在flanneld的配置檔案當中指定Backend type為UDP,可以透過直接修改flanneld的ConfigMap的方式實現,配置修改完成之後如下:
  1. [root@10-10-88-192 ~]# kubectl get cm -n kube-system -o yaml kube-flannel-cfg
  2. apiVersion: v1
  3. data:
  4. cni-conf.json: |
  5. {
  6. "name": "cbr0",
  7. "type": "flannel",
  8. "delegate": {
  9. "isDefaultGateway": true
  10. }
  11. }
  12. net-conf.json: |
  13. {
  14. "Network": "10.244.0.0/16",
  15. "Backend": {
  16. "Type": "udp"
  17. }
  18. }
  19. kind: ConfigMap
  20. metadata:
  21. creationTimestamp: 2018-10-30T08:34:01Z
  22. labels:
  23. app: flannel
  24. tier: node
  25. name: kube-flannel-cfg
  26. namespace: kube-system
  27. resourceVersion: "33718154"
  28. selfLink: /api/v1/namespaces/kube-system/configmaps/kube-flannel-cfg
  29. uid: 8d981eff-dc1e-11e8-8103-fa900126bc00
  30. [root@10-10-88-192 ~]#
關鍵欄位為Backend當中的Type欄位,採用UDP樣式時Backend Port預設為8285,即flanneld的監聽埠。
flanneld的ConfigMap更新完成之後delete flannel pod進行配置更新:
  1. [root@10-10-88-192 ~]# kubectl delete pod -n kube-system -l app=flannel
  2. pod "kube-flannel-ds-npcxv" deleted
  3. pod "kube-flannel-ds-rv8wv" deleted
  4. pod "kube-flannel-ds-t5zlv" deleted
  5. [root@10-10-88-192 ~]#
當採用UDP樣式時,flanneld行程在啟動時會透過開啟/dev/net/tun的方式生成一個TUN裝置,TUN裝置可以簡單理解為Linux當中提供的一種核心網路與使用者空間(應用程式)通訊的一種機制,即應用可以透過直接讀寫tun裝置的方式收發RAW IP包。
flanneld行程啟動後透過ip a命令可以發現節點當中已經多了一個叫flannel0的網路介面:
  1. [root@10-10-88-192 ~]# ip a
  2. 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
  3. link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  4. inet 127.0.0.1/8 scope host lo
  5. valid_lft forever preferred_lft forever
  6. inet6 ::1/128 scope host
  7. valid_lft forever preferred_lft forever
  8. 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
  9. link/ether fa:90:01:26:bc:00 brd ff:ff:ff:ff:ff:ff
  10. inet 10.10.88.192/24 brd 10.10.88.255 scope global eth0
  11. valid_lft forever preferred_lft forever
  12. inet6 fe80::f890:1ff:fe26:bc00/64 scope link
  13. valid_lft forever preferred_lft forever
  14. 3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
  15. link/ether fa:86:b8:79:70:01 brd ff:ff:ff:ff:ff:ff
  16. inet 172.16.130.244/24 brd 172.16.130.255 scope global eth1
  17. valid_lft forever preferred_lft forever
  18. inet6 fe80::f886:b8ff:fe79:7001/64 scope link
  19. valid_lft forever preferred_lft forever
  20. 4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN
  21. link/ether 02:42:ae:dd:19:83 brd ff:ff:ff:ff:ff:ff
  22. inet 172.17.0.1/16 scope global docker0
  23. valid_lft forever preferred_lft forever
  24. 5: flannel0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1472 qdisc pfifo_fast state UNKNOWN qlen 500
  25. link/none
  26. inet 10.244.0.0/16 scope global flannel0
  27. valid_lft forever preferred_lft forever
  28. inet6 fe80::969a:a8eb:e4da:308b/64 scope link flags 800
  29. valid_lft forever preferred_lft forever
  30. 6: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1472 qdisc noqueue state UP qlen 1000
  31. link/ether 0a:58:0a:f4:00:01 brd ff:ff:ff:ff:ff:ff
  32. inet 10.244.0.1/24 scope global cni0
  33. valid_lft forever preferred_lft forever
  34. inet6 fe80::3428:a4ff:fe6c:bb77/64 scope link
  35. valid_lft forever preferred_lft forever
細心的同學就會發現此時flannel0這個網路介面上的MTU為1472,相比Kubernetes叢集網路介面eth1小了28個位元組,為什麼呢?
透過可以ip -d link show flannel0可以看到這是一個tun裝置:
  1. [root@10-10-88-192 ~]# ip -d link show flannel0
  2. 5: flannel0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1472 qdisc pfifo_fast state UNKNOWN mode DEFAULT qlen 500
  3. link/none promiscuity 0
  4. tun
  5. [root@10-10-88-192 ~]#
透過netstat -ulnp命令可以看到此時flanneld行程監聽在8285埠:
  1. [root@10-10-88-192 ~]# netstat -ulnp | grep flanneld
  2. udp 0 0 172.16.130.140:8285 0.0.0.0:* 2373/flanneld
  3. [root@10-10-88-192 ~]#

 

容器跨節點通訊實現流程:
假設在節點A上有容器A(10.244.1.96),在節點B上有容器B(10.244.2.194),此時容器A向容器傳送一個ICMP請求報文(ping),我們來逐步分析一下ICMP報文是如何從容器A到達容器B的。 
1、容器A當中發出ICMP請求報文,透過IP封裝後形式為:10.244.1.96 -> 10.244.2.194,此時透過容器A內的路由表匹配到應該將IP包傳送到閘道器10.244.1.1(cni0網橋)。
完整的幀格式為: 
2、此時到達cni0的IP包目的地IP 10.244.2.194匹配到節點A上第一條路由規則(10.244.0.0),核心將RAW IP包傳送給flannel0介面。
3、flannel0為tun裝置,傳送給flannel0介面的RAW IP包(無MAC資訊)將被flanneld行程接收到,flanneld行程接收到RAW IP包後在原有的基礎上進行UDP封包,UDP封包的形式為:172.16.130.140:src port -> 172.16.130.164:8285。
這裡有一個問題就是flanneld怎麼知道10.244.2.194這個容器到底是在哪個節點上呢?
flanneld在啟動時會將該節點的網路資訊透過api-server儲存到etcd當中,故在傳送報文時可以透過查詢etcd得到10.244.2.194這個容器的IP屬於host B,且host B的IP為172.16.130.164。
RAW IP包示例:
 
4、flanneld將封裝好的UDP報文經eth1發出,從這裡可以看出網路包在透過eth1發出前先是加上了UDP頭(8個位元組),再然後加上了IP頭(20個位元組)進行封裝,這也是為什麼flannel0的MTU要比eth1的MTU小28個位元組的原因(防止封裝後的乙太網幀超過eth1的MTU而在經過eth1時被丟棄)。
此時完整的乙太網幀格式為:
 
5、網路包經節點A和節點B之間的網路連線到達host B。
6、host B收到UDP報文後經Linux核心透過UDP埠號8285將包交給正在監聽的應用flanneld。
7、執行在host B當中的flanneld將UDP包解包後得到RAW IP包:10.244.1.96 -> 10.244.2.194。
8、解封後的RAW IP包匹配到host B上的路由規則(10.244.2.0),核心將RAW IP包傳送到cni0。
此時的完整的乙太網幀格式為: 
9、cni0將IP包轉發給連線在cni0網橋上的container B,而flanneld在整個過程中主要主要負責兩個工作:
  • UDP封包解包

  • 節點上的路由表的動態更新

從上面虛線部分就可以看到container A和container B雖然在物理網路上並沒有直接相連,但在邏輯上就好像是處於同一個三層網路當中,這種基於底下的物理網路裝置透過Flannel等軟體定義網路技術實現的網路我們稱之為Overlay網路。
那麼上面透過UDP這種Backend實現的網路傳輸過程有沒有問題呢?最明顯的問題就是,網路資料包先是透過tun裝置從核心當中複製到使用者態的應用,然後再由使用者態的應用複製到核心,僅一次網路傳輸就進行了兩次使用者態和核心態的切換,顯然這種效率是不會很高的。那麼有沒有高效一點的辦法呢?當然,最簡單的方式就是把封包解包這些事情都交給核心去乾好了,事實上Linux核心本身也提供了比較成熟的網路封包解包(隧道傳輸)實現方案VXLAN,下麵我們就來看看透過內核的VXLAN跟flanneld自己透過UDP封裝網路包在實現上有什麼差別。
VXLAN
 
VXLAN簡介
VXLAN全稱Virtual Extensible LAN,是一種虛擬化隧道通訊技術,主要是為了突破VLAN的最多4096個子網的數量限制,以滿足大規模雲端計算資料中心的需求。VLAN技術的缺陷是VLAN Header預留的長度只有12 bit,故最多隻能支援2的12次方即4096個子網的劃分,無法滿足雲端計算場景下主機數量日益增長的需求。當前VXLAN的報文Header內有24 bit,可以支援2的24次方個子網,並透過VNI(Virtual Network Identifier)來區分不同的子網,相當於VLAN當中的VLAN ID。
不同於其他隧道協議,VXLAN是一個一對多的網路,並不僅僅是一對一的隧道協議。一個VXLAN裝置能透過像網橋一樣的學習方式學習到其他對端的IP地址,也可以直接配置靜態轉發表。
VXLAN包格式: 
從VXLAN的包格式就可以看到原本的二層乙太網幀被放在VXLAN包頭裡進行封裝,VXLAN實際實現的是一個二層網路的隧道,透過VXLAN讓處於同一個VXLAN網路(VNI相同則為同一個VXLAN網路)當中的機器看似處在同一個二層網路當中(邏輯上處於同一個二層網路),而網路包轉發的方式也類似二層網路當中的交換機(這樣雖然不是很準確,但更便於理解)。
當採用VXLAN樣式時,flanneld在啟動時會透過Netlink機制與Linux核心通訊,建立一個VTEP(Virtual Tunnel Access End Point)裝置flannel.1 (命名規則為flannel.[VNI],VNI預設為1),類似於交換機當中的一個網口。
可以透過ip -d link檢視VTEP裝置flannel.1的配置資訊,從以下輸出可以看到,VTEP的local IP為172.16.130.244,destination port為8472。
  1. [root@10-10-88-192 ~]# ip -d link show flannel.1
  2. 5: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT
  3. link/ether a2:5e:b0:43:09:a7 brd ff:ff:ff:ff:ff:ff promiscuity 0
  4. vxlan id 1 local 172.16.130.244 dev eth1 srcport 0 0 dstport 8472 nolearning ageing 300 addrgenmode eui64
  5. [root@10-10-88-192 ~]#
在UDP樣式下由flanneld行程進行網路包的封包和解包的工作,而在VXLAN樣式下解封包的事情交由核心處理,那麼此時FlannnelD的作用是什麼呢?帶著這個疑問我們先來簡單看一下VXLAN Backend是如何工作的。
 
VXLAN Backend工作原理
Flannel當中對VXLAN Backend的實現經過了幾個版本的改進之後目前最新版本的flanneld當中的處理流程為:
當flanneld啟動時將建立VTEP裝置(預設為flannel.1,若已經建立則跳過),並將VTEP裝置的相關資訊上報到etcd當中,而當在Flannel網路中有新的節點發現時,各個節點上的flanneld將依次執行以下流程:
在節點當中建立一條該節點所屬網段的路由表,主要是能讓Pod當中的流量路由到flannel.1介面。
透過route -n可以檢視到節點當中已經有兩條flannel.1介面的路由:
  1. [root@10-10-88-192 ~]# route -n
  2. Kernel IP routing table
  3. Destination Gateway Genmask Flags Metric Ref Use Iface
  4. 0.0.0.0 10.10.88.254 0.0.0.0 UG 0 0 0 eth0
  5. 10.10.88.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
  6. 10.244.0.0 0.0.0.0 255.255.255.0 U 0 0 0 cni0
  7. 10.244.1.0 10.244.1.0 255.255.255.0 UG 0 0 0 flannel.1
  8. 10.244.2.0 10.244.2.0 255.255.255.0 UG 0 0 0 flannel.1
  9. 169.254.0.0 0.0.0.0 255.255.0.0 U 1002 0 0 eth0
  10. 169.254.0.0 0.0.0.0 255.255.0.0 U 1003 0 0 eth1
  11. 172.16.130.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1
  12. 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
  13. [root@10-10-88-192 ~]#
在節點當中新增一條該節點的IP以及VTEP裝置的靜態ARP快取。
可透過arp -n命令檢視到master節點當中已經快取了另外兩個節點以及VTEP的ARP資訊(已刪除無關ARP快取資訊)。
  1. [root@10-10-88-192 ~]# arp -n
  2. Address HWtype HWaddress Flags Mask Iface
  3. 10.244.2.0 ether 42:7f:69:c7:cd:37 CM flannel.1
  4. 10.244.1.0 ether 7a:2c:d0:7f:48:3f CM flannel.1
  5. 172.16.130.140 ether fa:89:cf:03:e3:01 C eth1
  6. 172.16.130.164 ether fa:88:2a:44:2b:01 C eth1
在節點當中新增一條該節點的轉發表。
透過bridge命令檢視節點上的VXLAN轉發表(FDB entry),MAC為對端VTEP裝置即flannel.1的MAC,IP為VTEP對應的對外IP(可透過flanneld的啟動引數–iface=eth1指定,若不指定則按預設閘道器查詢網路介面對應的IP),可以看到已經有兩條轉發表。
  1. [root@10-10-88-192 ~]# bridge fdb show dev flannel.1
  2. 42:7f:69:c7:cd:37 dst 172.16.130.164 self permanent
  3. 7a:2c:d0:7f:48:3f dst 172.16.130.140 self permanent
  4. [root@10-10-88-192 ~]#
 
VXLAN Backend配置
跟UDP Backend一樣,將Flannel Backend修改為VXLAN只需要將Flannel ConfigMap當中的Backend type欄位修改為VXLAN即可。由於VXLAN型別相對UDP複雜並且有較好的靈活性,這裡簡單說一下VXLAN當中的幾個配置選項:
  • VNI(Number):VXLAN Identifier,預設為1

  • Port(Number):用於傳送VXLAN UDP報文的埠,預設為8472

  • DirectRouting(Boolean):當兩臺主機處於同一個網段當中時,啟用後將採用直接路由的方式進行跨節點網路通訊(此時工作樣式跟後面要講的host-gw Backend一樣),只有當兩臺主機處於不同的網段當中時才會採用VXLAN進行封包,預設為關閉狀態。

 

修改完成後的ConfigMap如下:
  1. [root@10-10-88-192 ~]# kubectl get cm -o yaml -n kube-system kube-flannel-cfg
  2. apiVersion: v1
  3. data:
  4. cni-conf.json: |
  5. {
  6. "name": "cbr0",
  7. "type": "flannel",
  8. "delegate": {
  9. "isDefaultGateway": true
  10. }
  11. }
  12. net-conf.json: |
  13. {
  14. "Network": "10.244.0.0/16",
  15. "Backend": {
  16. "Type": "vxlan"
  17. }
  18. }
  19. kind: ConfigMap
  20. metadata:
  21. creationTimestamp: 2018-10-30T08:34:01Z
  22. labels:
  23. app: flannel
  24. tier: node
  25. name: kube-flannel-cfg
  26. namespace: kube-system
  27. resourceVersion: "33872104"
  28. selfLink: /api/v1/namespaces/kube-system/configmaps/kube-flannel-cfg
  29. uid: 8d981eff-dc1e-11e8-8103-fa900126bc00
  30. [root@10-10-88-192 ~]#
同樣在更新配置後delete pod使配置生效,並可以透過Flannel的日誌檢視到Backend已經更新為VXLAN樣式:
  1. [root@10-10-88-192 ~]# kubectl logs -f -n kube-system kube-flannel-ds-7bjfm
  2. I0318 03:24:02.148654 1 main.go:487] Using interface with name eth1 and address 172.16.130.244
  3. I0318 03:24:02.148754 1 main.go:504] Defaulting external address to interface address (172.16.130.244)
  4. I0318 03:24:02.207525 1 kube.go:130] Waiting 10m0s for node controller to sync
  5. I0318 03:24:02.207596 1 kube.go:283] Starting kube subnet manager
  6. I0318 03:24:03.207695 1 kube.go:137] Node controller sync successful
  7. I0318 03:24:03.207729 1 main.go:234] Created subnet manager: Kubernetes Subnet Manager - 10-10-88-192
  8. I0318 03:24:03.207735 1 main.go:237] Installing signal handlers
  9. I0318 03:24:03.207812 1 main.go:352] Found network config - Backend type: vxlan
  10. I0318 03:24:03.227332 1 vxlan.go:119] VXLAN config: VNI=1 Port=0 GBP=false DirectRouting=false
  11. I0318 03:24:03.587362 1 main.go:299] Wrote subnet file to /run/flannel/subnet.env
  12. I0318 03:24:03.587379 1 main.go:303] Running backend.
  13. I0318 03:24:03.587390 1 main.go:321] Waiting for all goroutines to exit
  14. I0318 03:24:03.587418 1 vxlan_network.go:56] watching for new subnet leases
同樣可以透過netstat -ulnp命令檢視VXLAN監聽的埠:
  1. [root@10-10-88-192 ~]# netstat -ulnp | grep 8472
  2. udp 0 0 0.0.0.0:8472 0.0.0.0:* -
  3. [root@10-10-88-192 ~]#
但跟UDP樣式下檢視flanneld監聽的埠的區別為,最後一欄顯示的不是行程的ID和名稱,而是一個破折號“-”,這說明UDP的8472埠不是由使用者態的行程在監聽的,也證實了VXLAN模組工作在核心態樣式下。 
此時容器跨節點網路通訊實現流程為:
  1. 同UDP Backend樣式,容器A當中的IP包透過容器A內的路由表被髮送到cni0

  2. 到達cni0當中的IP包透過匹配host A當中的路由表發現通往10.244.2.194的IP包應該交給flannel.1介面

  3. flannel.1作為一個VTEP裝置,收到報文後將按照VTEP的配置進行封包,首先透過etcd得知10.244.2.194屬於節點B,並得到節點B的IP,透過節點A當中的轉發表得到節點B對應的VTEP的MAC,根據flannel.1裝置建立時的設定的引數(VNI、local IP、Port)進行VXLAN封包

  4. 透過host A跟host B之間的網路連線,VXLAN包到達host B的eth1介面

  5. 透過埠8472,VXLAN包被轉發給VTEP裝置flannel.1進行解包

  6. 解封裝後的IP包匹配host B當中的路由表(10.244.2.0),核心將IP包轉發給cni0

  7. cni0將IP包轉發給連線在cni0上的容器B

這麼一看是不是覺得相比UDP樣式單單從步驟上就少了很多步?VXLAN樣式相比UDP樣式高效也就不足為奇了。
host-gw
host-gw即Host Gateway,從名字中就可以想到這種方式是透過把主機當作閘道器來實現跨節點網路通訊的。那麼具體如何實現跨節點通訊呢?
同理UDP樣式和VXLAN樣式,首先將Backend中的type改為host-gw,這裡就不再贅述,只講一下網路通訊的實現流程。
採用host-gw樣式後flanneld的唯一作用就是負責主機上路由表的動態更新, 想一下這樣會不會有什麼問題?
使用host-gw Backend的Flannel網路的網路包傳輸過程如下圖所示: 
  1. 同UDP、VXLAN樣式一致,透過容器A的路由表IP包到達cni0

  2. 到達cni0的IP包匹配到host A當中的路由規則(10.244.2.0),並且閘道器為172.16.130.164,即host B,所以核心將IP包傳送給host B(172.16.130.164)

  3. IP包透過物理網路到達host B的eth1

  4. 到達host B eth1的IP包匹配到host B當中的路由表(10.244.2.0),IP包被轉發給cni0

  5. cni0將IP包轉發給連線在cni0上的容器B

host-gw樣式其中一個侷限性就是,由於是透過節點上的路由表來實現各個節點之間的跨節點網路通訊,那麼就得保證兩個節點是可以直接路由過去的。按照核心當中的路由規則,閘道器必須在跟主機當中至少一個IP處於同一網段,故造成的結果就是採用host-gw這種Backend方式時則叢集中所有的節點必須處於同一個網路當中,這對於叢集規模比較大時需要對節點進行網段劃分的話會存在一定的侷限性。另外一個則是隨著叢集當中節點規模的增大,flanneld需要維護主機上成千上萬條路由表的動態更新也是一個不小的壓力。
後記

 

本文主要講述了Flannel當中UDP、VXLAN、host-gw三種Backend的跨節點網路通訊實現流程,並作了簡單的分析對比,跨節點網路通訊的流程從UDP到VXLAN再到host-gw依次變得簡單,效率也逐步提升。這一切還是離不開Linux的設計哲學:Less Is More。
在Kubernetes當中往往很多問題都是出在網路層面,如何在出現問題時能快速地定位到問題原因需要我們對Kubernetes的網路模型有一個熟悉的認知。很多事物的出現都有他的歷史原因,永遠沒有適合各種場景的最好的方案,只有某些應用場景最合適的方案,網路選型還是得根據自身情況進行決定。

贊(0)

分享創造快樂