k8s網絡主題系列:
簡介
Flannel是CoreOS團隊針對Kubernetes設計的一個網絡規划服務,簡單來說,它的功能是讓集群中的不同節點主機創建的Docker容器都具有全集群唯一的虛擬IP地址。
在默認的Docker配置中,每個節點上的Docker服務會分別負責所在節點容器的IP分配。這樣導致的一個問題是,不同節點上容器可能獲得相同的內外IP地址。並使這些容器之間能夠之間通過IP地址相互找到,也就是相互ping通。
Flannel的設計目的就是為集群中的所有節點重新規划IP地址的使用規則,從而使得不同節點上的容器能夠獲得“同屬一個內網”且”不重復的”IP地址,並讓屬於不同節點上的容器能夠直接通過內網IP通信。
Flannel實質上是一種“覆蓋網絡(overlaynetwork)”,也就是將TCP數據包裝在另一種網絡包里面進行路由轉發和通信,目前已經支持udp、vxlan、host-gw、aws-vpc、gce和alloc路由等數據轉發方式,默認的節點間數據通信方式是UDP轉發。
簡單總結Flannel的特點
1.使集群中的不同Node主機創建的Docker容器都具有全集群唯一的虛擬IP地址。
2.建立一個覆蓋網絡(overlay network),通過這個覆蓋網絡,將數據包原封不動的傳遞到目標容器。覆蓋網絡是建立在另一個網絡之上並由其基礎設施支持的虛擬網絡。覆蓋網絡通過將一個分組封裝在另一個分組內來將網絡服務與底層基礎設施分離。在將封裝的數據包轉發到端點后,將其解封裝。
3.創建一個新的虛擬網卡flannel0接收docker網橋的數據,通過維護路由表,對接收到的數據進行封包和轉發(vxlan)。
4.etcd保證了所有node上flanned所看到的配置是一致的。同時每個node上的flanned監聽etcd上的數據變化,實時感知集群中node的變化。
Flannel對網絡要求提出的解決辦法
互相不沖突的ip
1.flannel利用Kubernetes API或者etcd用於存儲整個集群的網絡配置,根據配置記錄集群使用的網段。
2.flannel在每個主機中運行flanneld作為agent,它會為所在主機從集群的網絡地址空間中,獲取一個小的網段subnet,本主機內所有容器的IP地址都將從中分配。
如測試環境中ip分配:
1.master節點
2.node1
3.node2
在flannel network中,每個pod都會被分配唯一的ip地址,且每個K8s node的subnet各不重疊,沒有交集。
Pod之間互相訪問
- flanneld將本主機獲取的subnet以及用於主機間通信的Public IP通過etcd存儲起來,需要時發送給相應模塊。
- flannel利用各種backend mechanism,例如udp,vxlan等等,跨主機轉發容器間的網絡流量,完成容器間的跨主機通信。
Flannel架構原理
Flannel架構圖(根據個人理解畫的圖,和官網的圖有一些小差距):
各個組件的解釋:
Cni0:網橋設備,每創建一個pod都會創建一對 veth pair。其中一端是pod中的eth0,另一端是Cni0網橋中的端口(網卡)。Pod中從網卡eth0發出的流量都會發送到Cni0網橋設備的端口(網卡)上。
Cni0 設備獲得的ip地址是該節點分配到的網段的第一個地址。
Flannel.1: overlay網絡的設備,用來進行 vxlan 報文的處理(封包和解包)。不同node之間的pod數據流量都從overlay設備以隧道的形式發送到對端。
Flanneld:flannel在每個主機中運行flanneld作為agent,它會為所在主機從集群的網絡地址空間中,獲取一個小的網段subnet,本主機內所有容器的IP地址都將從中分配。同時Flanneld監聽K8s集群數據庫,為flannel.1設備提供封裝數據時必要的mac,ip等網絡數據信息。
不同node上的pod的通信流程:
- pod中產生數據,根據pod的路由信息,將數據發送到Cni0
- Cni0 根據節點的路由表,將數據發送到隧道設備flannel.1
- Flannel.1查看數據包的目的ip,從flanneld獲得對端隧道設備的必要信息,封裝數據包。
- Flannel.1將數據包發送到對端設備。對端節點的網卡接收到數據包,發現數據包為overlay數據包,解開外層封裝,並發送內層封裝到flannel.1設備。
- Flannel.1設備查看數據包,根據路由表匹配,將數據發送給Cni0設備。
- Cni0匹配路由表,發送數據給網橋上對應的端口。
通信流程
1.Pod1中的容器到cni0
Pod1與Pod3能夠互相ping通
Ping包的dst ip 為192.20.1.43,根據路由匹配到最后一條路由表項,去往192.20.0.0/12的包都轉發給192.20.0.1。
192.20.0.1為cni0的ip地址。
2.cni0到flannel1.1
當icmp包達到cni0之后,cni0發現dst為192.20.1.43,cni根據主機路由表來查找匹配項。
根據最小匹配原則,匹配到圖上的一條路由表項。去往192.20.1.0/24 網段的包,發送192.20.1.0網關,網關設備是flannel.1。
3.Flannel.1
flannel.1為vxlan設備,當數據包來到flannel.1時,需要將數據包封裝起來。此時的dst ip 為192.20.1.43,src ip為192.20.0.51。數據包繼續封裝需要知道192.20.1.43 ip地址對應的mac地址。此時,flannel.1不會發送arp請求去獲得192.20.1.42的mac地址,而是由Linux kernel將一個“L3 Miss”事件請求發送的用戶空間的flanned程序。Flanned程序收到內核的請求事件之后,從etcd查找能夠匹配該地址的子網的flannel.1設備的mac地址,即發往的pod所在host中flannel.1設備的mac地址。Flannel在為Node節點分配ip網段時記錄了所有的網段和mac等信息,所以能夠知道。交互流程如下圖所示:
flanned將查詢到的信息放入master node host的arp cache表中:
到這里,vxlan的內層數據包就完成了封裝。格式是這樣的:
簡單總結這個流程:
- 數據包到達flannel.1,通過查找路由表,知道數據包要通過flannel.1發往192.20.1.0
- 通過arp cache表,知道了目的ip192.20.1.0的mac地址。
kernel需要查看node上的fdb(forwarding database)以獲得內層封包中目的vtep設備所在的node地址。因為已經從arp table中查到目的設備mac地址為52:77:71:e6:4f:58,同時在fdb中存在該mac地址對應的node節點的IP地址。如果fdb中沒有這個信息,那么kernel會向用戶空間的flanned程序發起”L2 MISS”事件。flanneld收到該事件后,會查詢etcd,獲取該vtep設備對應的node的”Public IP“,並將信息注冊到fdb中。
當內核獲得了發往機器的ip地址后,arp得到mac地址,之后就能完成vxlan的外層封裝。
4.對端flannel.1
Node節點的eth0網卡接收到vxlan設備包,kernal將識別出這是一個vxlan包,將包拆開之后轉給節點上的flannel.1設備。這樣數據包就從發送節點到達目的節點,flannel.1設備將接收到一個如下的數據包:
目的地址為192.20.1.43,flannel.1查找自己的路由表,根據路由表完成轉發。
根據最下匹配原則,flannel.1將去往192.20.1.0/24的流量轉發到cni0上去。
5.cnio到Pod
cni0是一個網橋設備。當cni0拿到數據包之后,通過veth pair,將數據包發送給pod。查看Node節點中的網橋。
在node節點上通過arp解析可以開出,192.20.1.43的mac地址為 66:57:8e:3d:00:85
該地址為pod的網卡eth0的地址。
同時通過veth pair的配對關系可以看出,pod中的eth0是veth pair的一端,另一端在node節點行上,對應的網卡是vethd356ffc1@if3。所以,在cni0網橋上掛載的pod的veth pair為vethd356ffc1,即:
eth0@if50和vethd356ffc1@if3組成的一對veth,pair。其效果相當於將pod中的eth0直接插在到cni0上。所以簡單總結cni0轉發流量的原理:
- 首先通過arp查找出ip地址對應的mac地址
- 將流量轉發給mac地址所在eth0網的對應的veth pair端口
- veth pair端口接收到流量,直接將流量注入到pod的eth0網卡上。
不同后端的封裝
Flannel可以指定不同的轉發后端網絡,常用的有hostgw,udp,vxlan等。
Hostgw
hostgw是最簡單的backend,它的原理非常簡單,直接添加路由,將目的主機當做網關,直接路由原始封包。
例如,我們從etcd中監聽到一個EventAdded事件subnet為10.1.15.0/24被分配給主機Public IP 192.168.0.100,hostgw要做的工作就是在本主機上添加一條目的地址為10.1.15.0/24,網關地址為192.168.0.100,輸出設備為上文中選擇的集群間交互的網卡即可。
優點:簡單,直接,效率高
缺點:要求所有的pod都在一個子網中,如果跨網段就無法通信。
UDP
如何應對Pod不在一個子網里的場景呢?將Pod的網絡包作為一個應用層的數據包,使用UDP封裝之后在集群里傳輸。即overlay。
上圖來自flannel官方,其中右邊Packer的封裝格式就是使用udp完成overlay的格式
當容器10.1.15.2/24要和容器10.1.20.2/24通信時,
1.因為該封包的目的地不在本主機subnet內,因此封包會首先通過網橋轉發到主機中。
2.在主機上經過路由匹配,進入網卡flannel.1。(需要注意的是flannel.1是一個tun設備,它是一種工作在三層的虛擬網絡設備,而flanneld是一個proxy,它會監聽flannel.1並轉發流量。)
3.當封包進入flannel.1時,flanneld就可以從flanne.1中將封包讀出,由於flanne.1是三層設備,所以讀出的封包僅僅包含IP層的報頭及其負載。
4.最后flanneld會將獲取的封包作為負載數據,通過udp socket發往目的主機。
5.在目的主機的flanneld會監聽Public IP所在的設備,從中讀取udp封包的負載,並將其放入flannel.1設備內。
6.容器網絡封包到達目的主機,之后就可以通過網橋轉發到目的容器了。
優點:Pod能夠跨網段訪問
缺點:隔離性不夠,udp不能隔離兩個網段。
Vxlan
vxlan和上文提到的udp backend的封包結構是非常類似的,不同之處是多了一個vxlan header,以及原始報文中多了個二層的報頭。
當初始化集群里,vxlan網絡的初始化工作:
主機B加入flannel網絡時,它會將自己的三個信息寫入etcd中,分別是:subnet 10.1.16.0/24、Public IP 192.168.0.101、vtep設備flannel.1的mac地址 MAC B。之后,主機A會得到EventAdded事件,並從中獲取上文中B添加至etcd的各種信息。這個時候,它會在本機上添加三條信息:
1) 路由信息:所有通往目的地址10.1.16.0/24的封包都通過vtep設備flannel.1設備發出,發往的網關地址為10.1.16.0,即主機B中的flannel.1設備。
2) fdb信息:MAC地址為MAC B的封包,都將通過vxlan發往目的地址192.168.0.101,即主機B
3)arp信息:網關地址10.1.16.0的地址為MAC B
事實上,flannel只使用了vxlan的部分功能,由於VNI被固定為1,本質上工作方式和udp backend是類似的,區別無非是將udp的proxy換成了內核中的vxlan處理模塊。而原始負載由三層擴展到了二層,但是這對三層網絡方案flannel是沒有意義的,這么做也僅僅只是為了適配vxlan的模型。vxlan詳細的原理參見文后的參考文獻,其中的分析更為具體,也更易理解。
總的來說,flannel更像是經典的橋接模式的擴展。我們知道,在橋接模式中,每台主機的容器都將使用一個默認的網段,容器與容器之間,主機與容器之間都能互相通信。要是,我們能手動配置每台主機的網段,使它們互不沖突。接着再想點辦法,將目的地址為非本機容器的流量送到相應主機:如果集群的主機都在一個子網內,就搞一條路由轉發過去;若是不在一個子網內,就搞一條隧道轉發過去。這樣以來,容器的跨網絡通信問題就解決了。而flannel做的,其實就是將這些工作自動化了而已。
存在問題
1.不支持pod之間的網絡隔離。Flannel設計思想是將所有的pod都放在一個大的二層網絡中,所以pod之間沒有隔離策略。
2.設備復雜,效率不高。Flannel模型下有三種設備,數量經過多種設備的封裝、解析,勢必會造成傳輸效率的下降。
對於flannel網絡介紹的文章也很多,其中有一個點有明顯的分歧,就是對於flanned的作用。分歧點在於:使用UDP作為后端網絡時,flanned會將flanne.1設備的流量經過自己的處理發送給對端的flanned。但是在分析vxlan作為后端網絡時明顯不是這么做的,在vxlan中flanned作用是獲取必要的mac地址,ip地址信息,沒有直接處理數據流。這里要存疑,如果有讀者能告知,歡迎留言。