1. 程式人生 > >kubernetes之flannel

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外掛。