kubernetes之flannel


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包的時候,就需要三次用戶態和內核態的數據拷貝。如圖:

 

  1.  用戶態容器進程發起IP包進過docker0網橋進入內核態
  2. IP包根據路由表進入TUN(flannel0)設備,從而回到用戶態的flanneld進程
  3. 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 

這樣就修改完成了,切忌生成不可以直接操作。

 


免責聲明!

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



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