Flannel的三種工作模式


在 Docker 的默認配置下,一台宿主機上的 docker0 網橋,和其他宿主機上的 docker0 網橋,沒有任何關聯,它們互相之間也沒辦法連通。所以,連接在這些網橋上的容器,自然也沒辦法進行通信了。不過,萬變不離其宗。如果我們通過軟件的方式,創建一個整個集群“公用”的網橋,然后把集群里的所有容器都連接到這個網橋上,不就可以相互通信了嗎?這樣一來,我們整個集群里的容器網絡就會類似於下圖所示的樣子

  

可以看到,構建這種容器網絡的核心在於:我們需要在已有的宿主機網絡上,再通過軟件構建一個覆蓋在已有宿主機網絡之上的、可以把所有容器連通在一起的虛擬網絡。所以,這種技術就被稱為:Overlay Network(覆蓋網絡)。

而這個 Overlay Network 本身,可以由每台宿主機上的一個“特殊網橋”共同組成。甚至,每台宿主機上,都不需要有一個這種特殊的網橋,而僅僅通過某種方式配置宿主機的路由表,就能夠把數據包轉發到正確的宿主機上。

為了解決容器“跨主通信”的問題,社區里出現了很多的容器網絡方案,要理解容器“跨主通信”的原理,就一定要先從 Flannel 這個項目說起。Flannel 項目是 CoreOS 公司主推的容器網絡方案。事實上,Flannel 項目本身只是一個框架,真正為我們提供容器網絡功能的,是 Flannel 的后端實現。目前,Flannel 支持三種后端實現,分別是:UDP、VXLAN、host-gw。

1、UDP

UDP是 Flannel 項目最早支持的一種方式,卻也是性能最差的一種方式,這個模式目前已經被棄用。不過,Flannel 之所以最先選擇 UDP 模式,就是因為這種模式是最直接、也是最容易理解的容器跨主網絡實現。

在由 Flannel 管理的容器網絡里,Flannel會為每一台宿主機分配一個“子網”,一台宿主機上的所有容器,都屬於這個“子網”,即都屬於同一個網段。

這些子網與宿主機的對應關系,正是保存在 Etcd 當中,可通過以下命令查看:

$ etcdctl ls /coreos.com/network/subnets
/coreos.com/network/subnets/100.96.1.0-24
/coreos.com/network/subnets/100.96.2.0-24
/coreos.com/network/subnets/100.96.3.0-24 

子網對應的宿主機的 IP 地址,可通過以下命令查看:

$ etcdctl get /coreos.com/network/subnets/100.96.2.0-24
{"PublicIP":"10.168.0.3"}

而Flannel的工作模式,可通過以下命令查看:

$ etcdctl get /coreos.com/network/config 
{"Network":"172.10.0.0/16", "SubnetLen": 24, "Backend": {"Type": "host-gw"}}

Flannel 會在每個宿主機上啟動一個flanneld進程,flanneld進程會創建一個叫做flannel0的設備,並添加一系列的路由規則,用來指定數據包的下一跳。這個 flannel0 設備是一個TUN 設備(Tunnel 設備),TUN 設備是一種工作在三層的虛擬網絡設備,其功能非常簡單,即:在操作系統內核和用戶應用程序之間傳遞 IP 包。而添加的路由規則則包含了流入flannel0 設備和流出flannel0 設備的路由。

所以,當 IP 包從容器經過 docker0 出現在宿主機后,就會根據路由表進入 flannel0 設備,然后,宿主機上的 flanneld 進程就會收到這個 IP 包,然后,flanneld 就會根據這個 IP 包的目的地址,匹配到對應的子網,從 Etcd 中找到這個子網對應的宿主機的 IP 地址,然后,flanneld就會把這個IP包直接封裝在一個 UDP 包里,通過物理網絡發送給目的宿主機。這時候,容器間的通信就轉變為兩個節點間的通信,只要節點是互通的,那容器就可以互通。

當然,這個請求得以完成的原因是,每台宿主機上的 flanneld進程,都監聽着一個 8285 端口,所以源宿主機的 flanneld 只要把 UDP 包發往目的宿主機的 8285 端口即可,目的宿主機的flanneld 就可以從這個 UDP 包里解析出封裝在里面的源容器發來的原 IP 包。

接下來,flanneld 會直接把這個 IP 包發送給它所管理的 TUN 設備,即 flannel0 設備,Linux內核會根據路由規則,把這個 IP 包轉發給 docker0 網橋,docker0 網橋會扮演二層交換機的角色,將數據包發送給正確的端口,進而通過 Veth Pair 設備進入到目的容器里。

以上,就是基於 Flannel UDP 模式的跨主通信的基本原理了,如下圖所示:

  

可以看到,UDP 模式提供的其實是一個三層的 Overlay 網絡,即:它首先對發出端的 IP 包進行 UDP 封裝,然后在接收端進行解封裝拿到原始的 IP 包,進而把這個 IP 包轉發給目標容器。

實際上,相比於兩台宿主機之間的直接通信,基於 Flannel UDP 模式的容器通信多了一個額外的步驟,即 flanneld 的處理過程。而這個過程,由於使用到了 flannel0 這個 TUN 設備,僅在發出 IP 包的過程中,就需要經過三次用戶態與內核態之間的數據拷貝,如下所示:  

  

UDP 小結:

工作在三層的overlay網絡;

三次用戶態與內核態之間的數據拷貝;

UDP 封裝和解封裝的過程,也都是由flanneld進程在用戶態完成的;

這些上下文切換和用戶態操作的代價其實是比較高的,這也正是造成 Flannel UDP 模式性能不好的主要原因。

我們在進行系統級編程的時候,有一個非常重要的優化原則,就是要減少用戶態到內核態的切換次數,並且把核心的處理邏輯都放在內核態進行。這也是為什么,Flannel 后來支持的VXLAN 模式,逐漸成為了主流的容器網絡方案的原因。

2、VXLAN

VXLAN即Virtual Extensible LAN(虛擬可擴展局域網),是 Linux 內核本身就支持的一種網絡虛似化技術。VXLAN 的覆蓋網絡(Overlay Network)的設計思想是:在現有的三層網絡之上,“覆蓋”一層虛擬的、由內核 VXLAN 模塊負責維護的二層網絡,使得連接在這個 VXLAN 二層網絡上的“主機”(虛擬機或者容器都可以)之間,可以像在同一個局域網(LAN)里那樣自由通信。

VXLAN 會在宿主機上設置一個特殊的網絡設備 VTEP,即:VXLAN Tunnel End Point(虛擬隧道端點),而 VTEP 設備的作用,是對二層數據幀進行封裝和解封裝,且這個工作的執行流程,全部是在內核里完成的。Flannel也會在各節點上添加相應的路由規則,用來指定數據包的下一跳,並維護一個ARP表,存儲各節點的VTEP設備的MAC地址,也會維護一個FDP轉發數據庫,存儲各VTEP設備所在的宿主機ip。

上述基於 VTEP 設備進行“隧道”通信的流程,如下圖所示:

  

可以看到,圖中每台宿主機上名叫 flannel.1 的設備,就是 VXLAN 所需的 VTEP 設備,它既有 IP 地址,也有 MAC 地址。

整體通信流程如下: 每台設備上已經有了VTEP設備和相應的路由規則,源容器發出請求后,數據包包含了源容器ip和目的容器ip,這個原始數據包首先會通過虛擬網卡對出現在docker0網橋上,然后根據路由規則到達VTEP設備,且要發往的網關地址為目的VTEP設備的ip,然后VTEP會從ARP表里獲取到對應的MAC地址,並進行二層封包,得到一個二層數據幀,我們把它稱為“內部數據幀”。

接下來,Linux 內核會在“內部數據幀”前面,加上一個特殊的 VXLAN 頭,用來表示這實際上是一個 VXLAN 要使用的數據幀,而這個 VXLAN 頭里有一個重要的標志叫作 VNI,它是 VTEP 設備識別某個數據幀是不是應該歸自己處理的重要標識,而在 Flannel 中,VNI 的默認值是 1。

然后,Linux 內核會從FDB數據庫里讀取到對應的目的宿主機ip,把“內部數據幀”和目的宿主機ip封裝進一個 UDP 包里,我們把它稱為“外部數據幀”,這個“外部數據幀”里包含了源節點ip和目的節點ip,然后Linux內核會在這個“外部數據幀”前面加上目的宿主機的MAC地址,這樣,封包工作就宣告完成了。

所以,這個普通UDP包的數據部分並不是單純的數據,而是一個包含了容器ip的數據幀,這個數據幀里又包含了容器真正傳輸的數據。

接下來,源宿主機上的 flannel.1 設備就可以把這個數據幀從源宿主機的 eth0 網卡發出去,數據幀會經過宿主機網絡來到目的宿主機的 eth0 網卡。這時候,目的宿主機的內核網絡棧會發現這個數據幀里有 VXLAN Header,並且 VNI=1。所以 Linux 內核會對它進行拆包,拿到里面的內部數據幀,然后根據 VNI 的值,把它交給 flannel.1 設備。而 flannel.1 設備則會進一步拆包,取出“原始 IP 包”,然后根據路由規則,將它交給docker0網橋,經過虛擬網卡對就進入到了目的容器里。

VXLAN 小結:

工作在二層的overlay網絡;

只有一次用戶態到內核態之間的數據拷貝;

UDP 封裝和解封裝的過程,是由flannel.1設備在內核態完成;

相較UDP模式而言,性能更好。

UDP 和 VXLAN 模式對比:

都是在現有的物理網絡之上,通過軟件創建一層虛擬的覆蓋網絡(Overlay Network),把不同宿主機上創建的虛擬設備連通,從而達到容器跨主機通信的目的。在每一個節點上啟動flanneld進程,分配子網,創建虛擬設備,添加路由規則,維護相關表等。

模式

OSI

虛擬設備

拷貝次數

封裝/解封裝

維護表

內核原生支持

UDP

三層

flannel0(TUN)

三次

flanneld在用戶態完成

etcd

route

VXLAN

二層

flannel.1(VTEP)

一次

flannel.1在內核態完成

etcd、route

ARP、FDB

所以,VXLAN相較UDP模式,性能更好。

上面介紹的工作流程,也正是 Kubernetes 對容器網絡的主要處理方法。只不過,Kubernetes 是通過一個叫作 CNI 的接口,維護了一個單獨的網橋來代替 docker0。這個網橋的名字就叫作:CNI 網橋,它在宿主機上的設備名稱默認是:cni0。

以 Flannel 的 VXLAN 模式為例,在 Kubernetes 環境里,它的工作方式跟上面描述的沒有任何不同。只不過,docker0 網橋被替換成了 CNI 網橋而已,如下所示:

  

 

需要注意的是,CNI 網橋只是接管所有 CNI 插件負責的、即 Kubernetes 創建的容器(Pod)。而此時,如果你用 docker run 單獨啟動一個容器,那么 Docker 項目還是會把這個容器連接到 docker0 網橋上。

3、host-gw

host-gw的工作原理非常簡單,就是將每個 Flannel 子網的“下一跳”,設置成了該子網對應的宿主機的 IP 地址。也就是說,這台“主機”(Host)會充當這條容器通信路徑里的“網關”(Gateway)。這也正是“host-gw”的含義。當然,Flannel 子網和主機的信息,都是保存在 Etcd 當中的。flanneld 只需要 WACTH 這些數據的變化,然后實時更新路由表即可。如下圖所示:

  

flanneld會在宿主機上創建相應的路由規則,設置到某一目的網段的的數據包,應該經過本機的 eth0 設備發出去,且下一跳地址是目的主機ip。

所以,當 IP 包從網絡層進入鏈路層封裝成幀的時候,eth0 設備就會使用下一跳地址對應的 MAC 地址,作為該數據幀的目的 MAC 地址,這樣,這個數據幀就會從 源宿主機通過宿主機的二層網絡順利到達目的宿主機上。目的宿主機拿到數據包后,根據路由表進入cni0網橋,進而進入到目的容器中。

在這種模式下,容器通信的過程就免除了額外的封包和解包帶來的性能損耗。根據實際的測試,host-gw 的性能損失大約在 10% 左右,而其他所有基於 VXLAN“隧道”機制的網絡方案,性能損失都在 20%~30% 左右。

host-gw 小結:

純三層的網絡方案;

無需創建虛擬設備,只需要添加相應路由規則即可;

免除了額外的虛擬設備的封包和解封包過程;

需要集群宿主機之間二層連通;

相較UDP和VXLAN,性能最好;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM