kubernetes網絡通信
- 容器間的通信 pod內的容器通信(lo)
- Pod之間的通信 pod IP <-----> pod IP
- Pod與Service之間的通信 podIP <-----> ClusterIP
- Service與集群外部的通信 ClusterIP <-----> 集群外部
CNI插件:
- flannel
- calico
- canel
- kube-route
解決方案:
- 虛擬網橋
- 多路復用 MacVLAN
- 硬件交換 SR-IOV
Flannel
Flannel本身是一個框架,真正提供網絡功能是他的后端實現。目前支持三種后端實現:
- VXLAN
- host-gw
- UDP
UDP
UDP模式是Flannel早期支持的一種方式,卻也是性能最差的一種方式。所以現在基本都棄用了。但是這種模式是最直接最容易理解的一種方式。
Node1的容器訪問Node2的容器,Node1容器訪問的目的地址不是在容器的路由規則里,所以會走默認路由到達宿主機的docker0 網橋上,從而出現在宿主機上,這個時候這個IP的下一步取決於這台宿主機的路由規則,這時候這條會使用flannel0的設備上,flannel是一個TUN設備,在Linux中,TUN設備是一種工作在三層的虛擬網絡設備,TUN設備的功能是在操作系統內核和應用程序之間傳遞IP包。
當操作系統將一個IP包發送給flannel0設備后,flannel0 就會把IP包交給創建這個設備的應用程序,也就是flannel進行。這就是從內核態(Linux操作系統)到用戶態(Flannel進程)的流動方向。
反之,如果flannel進程向flannel0設備發送一個IP包,那么這個IP包就會出現在宿主機網絡棧,然后根據宿主機路由表進行下一步處理,這是用戶態向內核態流動方向。
所以,當IP包從容器經過docker0出現在宿主機上,然后又根據宿主機路由表進入flannel0設備后,宿主機上的flannel進程就會收到這個IP包。然后,flannel看到了這個IP包的目的地址,就可以把他發送到Node2宿主機上。
flannel是如何知道目的IP地址對應的容器是運行在Node2上
源於flannel里的一個重要概念子網(Subnet)。事實上,在由Flannel管理的容器網絡里,一台宿主機上的所有容器都屬於該宿主機被分配的一個“子網”,所以flannel會根據目的IP地址找到對應的子網,從而在etcd里找到對應的宿主機。只要node1 和node2 互通,flannel進程一定會到達node2。
UDP模式的缺點
相比兩台宿主機之間的直接通信,基於flannel UDP模式的容器通信網絡多個一個額外的步驟,那就是flannel的處理過程,這個過程由於使用到了flannel0這個TUN設備,僅僅是發送IP包的時候,就需要三次用戶態和內核態的數據拷貝。如圖:
- 用戶態容器進程發起IP包進過docker0網橋進入內核態
- IP包根據路由表進入TUN(flannel0)設備,從而回到用戶態的flanneld進程
- flanneld 進行UDP封包之后重新進入內核態,將UDP包通過宿主機的eth0發出去
VXLAN
VXLAN全稱虛擬可擴展局域網,是Linux內核本身就支持的一種網絡虛擬化技術。所以說,VXLAN可以完全在內核態實現上述封裝和解封工作,從而通過前面相似的“隧道機制”,構建出覆蓋網絡。
VXLAN覆蓋網絡的設計思想:在現有的三層網絡之上,“覆蓋”一層虛擬的,由內核VXLAN模塊負責維護的二層網絡,使得連接在這個VXLAN二層網絡上的“主機”(可以是虛擬機也可以是容器)之間,可以像一個局域網里那樣自由通信,當然,實際上,這些“主機”可能分布在不同的宿主機上,甚至可以分布在不同的物理機房里。
而為了能夠在二層網絡上打通“隧道”,VXLAN會在宿主機上設置一個特殊的網絡設備作為“隧道”的兩端。這個設備叫VTEP,既:虛擬隧道端點。而這個VTEP設備的作用,其實跟前面的flanneld進程非常相似。只不過,它進行封裝和解封裝的對象是二層數據幀。而且這個工作的執行流程全部是在內核里完成的(因為VXLAN本身就是內核模塊)。如圖:
從圖里看每個宿主機都有一個flannel1的設備,就是VXLAN所需的VTEP設備,它既有IP地址也有MAC地址。現在我們是container1 訪問 container2,相對UDP比較來看,當container1發出請求后,這個目的的地址是10.244.1.3的IP包,會先出現在docker0網橋,然后被路由到本機flanner1設備上處理,也就是說,來到了“隧道”的出口。既目的宿主機的VTEP設備。而這個設備信息正是每台宿主機上的flanneld進程負責維護的。
當所有Node啟動后,我們可以在Node1 上可以看到多個flannel1 網卡的路由信息,是因為flanneld啟動后創建的。
$ route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface ... 10.244.1.0 10.244.1.0 255.255.255.0 UG 0 0 0 flannel.1 ....
從上圖看到10.244.1.0就是Node2的VTEP設備(就是flannel1 設備)的IP地址,而這些VTEP設備之間就需要想辦法組成一個虛擬的二層網絡,既:通過二層數據幀進行通信,而Node1上的VTEP設備收到原始IP包后,就要想辦法把原始IP包加一個目的MAC地址,封裝成二層數據幀,然后發送給目的VTEP設備。這里需要解決一個問題目的VTEP設備的MAC地址是什么?
根據路由表信息我們知道了目的VTEP設備的IP地址,而根據三層IP地址查詢二層MAC地址正是ARP表的功能。而這里用ARP表的記錄,也就是flanneld進程在Node2節點啟動時,自動添加到Node1上的。如下:
$ ip neigh show dev flannel.1 10.244.1.0 lladdr b2:ba:aa:a5:10:1a PERMANENT
有了這個MAC地址linux內核就可以開始二層封包了,上面提到的MAC地址,對宿主機的二層網絡沒有任何意義,所以上述封裝的數據幀不能在宿主機的二層網絡里傳輸,為了方便概述,我們把上述數據幀稱為內部數據幀。所以Linux內核還要把內部數據幀進一步封裝成宿主機網絡的一個普通數據幀,好讓他載着內部數據幀,通過eth0網卡進行傳輸。這次封裝我們稱為外部數據幀,為了實現這個搭便車的機制,Linux內核在封裝內部數據幀前面,加上特殊的VXLAN頭,用來表示這個乘客實際上是VXLAN使用的數據幀。而這個VXLAN頭里有一個重要的標志VNI,它是識別某個數據幀是不是應該歸屬自己處理的標志。而flannel中,VNI的值是1,這也是為什么宿主機的VTEP設備都叫做flannel1的原因。這個時候linux內核會把這數據幀封裝一個UDP包里發出去。雖然node1的flannel1知道node2的flannel2的MAC地址,但是不知道node2MAC的地址,也就是UDP該發往那台主機,實際上flannel1還要扮演一個網橋的角色,在二層網絡進行UDP轉發,而在Linux內核里面,網橋設備進行轉發的依據來自FDB的轉發數據庫。這個flannel網橋對應的FDB信息,就是flannel進程維護的,他的內容如下:
$ bridge fdb show flannel.1 | grep b2:ba:aa:a5:10:1a b2:ba:aa:a5:10:1a dev flannel.1 dst 172.16.138.41 self permanent
我們可以看到發往的IP地址是172.16.138.41的主機,顯然這台主機就是 Node2,UDP要轉發的目的也找到了。接下來就是宿主機網絡封包的過程了。
Flannel 在kubernetes上的應用
其實上述過程也是kubernetes的主要處理方法,只不過kubernetes是通過一個叫CNI接口維護一個單獨網橋來代替docker0,這個網橋CNI網橋,默認叫做cni0。如圖:
過程也是和VXLAN是一樣的。需要注意的是,CNI網橋只是接管所有CNI插件負責的,既只是kubernetes創建的容器。
kubernetes之所以要設置這樣一個與docker0網橋功能一樣的CNI網橋,主要原因有兩點:
- kubernetes項目並沒有使用Docker的網絡模型,所以它不希望,也不具備配置docker0的能力。
- 還與kubernetes如何配置Pod,也就是infra容器Network Namespace相關。
我們在部署kubernetes的時候,有一個步驟是安裝kubernetes-cni包,他的目的就是宿主機上安裝CNI插件所需要的基礎可執行文件。
ls /opt/cni/bin/ bridge dhcp flannel host-local ipvlan loopback macvlan portmap ptp sample tuning vlan
CNI的基礎可執行文件,按照功能分三類:
- Main插件 他是用來創建具體網絡設備的二進制文件。例如:birdge(網橋設備)、ipvlan、lookback(lo 設備)、ptp(Veth Pair設備)、macvlan、vlan
- IPAM插件 他是負責分配IP地址的二進制文件。例如:dhcp,這個文件會向dhcp服務器發起請求;host-local,則會使用預先配置的IP地址來進行分配。
- CNI社區維護的內置CNI插件。例如 flannel,就是專門為Flannel項目提供的CNI插件。
從二進制文件來看,如果實現一個kubernetes用的容器網絡方案,其實需要兩部分工作,以Flannel下項目為例。
- 首先實現這個網絡本身,其實就是flanneld進程里主要邏輯,例如:創建和配置flannel.1設備,配置宿主機路由、配置ARP和FDB表。
- 實現該網絡方案對應的CNI插件,就是配置infra容器里的網絡棧,並把他連接在CNI網橋上。
接下來進行第一步,在宿主機上安裝flannel。而這個過程中,flanneld啟動后會在每台宿主機上生成他對應的CNI配置文件(就是configmap),從而告訴kubernetes,這個集群要使用Flannel作為容器網絡方案。
CNI配置文件內容如下:
cat /etc/cni/net.d/10-flannel.conflist { "name": "cbr0", "plugins": [ { "type": "flannel", "delegate": { "hairpinMode": true, "isDefaultGateway": true } }, { "type": "portmap", "capabilities": { "portMappings": true } } ] }
需要注意的是Kubernetes 目前不支持多個CNI混用,如果在/etc/cni/net.d/里放置多個CNI配置文件的話,只會加載字母排序第一個配置文件。但另一方面,CNI允許你在一個CNI配置文件里,通過plugins字段,定義多個插件協作。假如我們定義了flannel和protmap這兩個插件,這個時候dockershim會把這個CNI配置文件加載起來,並把列表第一個插件,就是flannel插件設置為默認插件,然后執行過程中flannel和portmap會按照定義順序被調用,從而一次完成“配置容器網絡”和“配置端口映射”。
CNI插件工作原理
當kubelet組件需要創建pod時,他一個創建的一定是infra容器,所以這一步,dockershim 就會先調用Docker API創建並啟動infra容器,緊着執行一個叫做SetUpPod的方法,這個方法的作用是:為CNI插件准備構建參數,然后調用CNI插件為infra容器配置網絡。這里要調用CNI插件是/opt/cni/bin/flannel;而調用它所需的參數,分兩部分:
第一部分,是由dockershim設置的一組CNI環境變量。其中,最重要的環境變量參數叫做:CNI_COMMAND。他的取值只有兩種ADD和DEL。這個ADD和DEL操作,就是CNI插件唯一實現的兩種方法。
- ADD 把容器添加CNI網絡里
- DEL 把容器從CNI網絡里移除
在網橋類型的CNI里,這兩個操作說明把容器以“Veth Pair”方式插在CNI網橋上,或者從網橋上拔掉。
第二部分,是dockershim從CNI配置文件里加載到的默認插件的配置信息。這個配置信息在CNI中被叫作Network Configuration,dockershim會把Network Configuration 通過JSON數據的方式,通過標准輸入的方式傳遞給Flannel CNI插件。
將Flannel插件的后端改成VXLAN+DriectRouting
以下操作不適合在生產環境操作,kubernetes的網絡環境需要根據未來的業務環境選擇。所以要集群配置前做好規划。
VXLAN+DriectRouting是首先采用直接路由的方式,如果不可以就使用VXLAN的方式。
Flannel的默認配置是VXLAN,但是我們可以配置多個插件協作,接下來我們就使用VXLAN+DriectRouting這兩個插件協作。
下載Flannel配置文件
wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/k8s-manifests/kube-flannel-legacy.yml
修改kube-flannel-legacy.yml,在net-conf.json里的增加一個type。
$ vim kube-flannel-legacy.yml ....... net-conf.json: | { "Network": "10.244.0.0/16", "Backend": { "Type": "vxlan", "Type": "DriectRouting" } } .....
刪除flannel插件,重新生成配置文件。
$ kubectl delete ds kube-flannel-ds -n kube-system
$ kubectl apply -f kube-flannel-legacy.yml
這樣就修改完成了,切忌生成不可以直接操作。