天天看點

Kubernetes 網絡插件工作原理Kubernetes 網絡插件工作原理

Kubernetes 網絡插件工作原理

容器的網絡解決方案有很多種,每支援一種網絡實作就進行一次适配顯然是不現實的,而 CNI 就是為了相容多種網絡方案而發明的。CNI 是 Container Network Interface 的縮寫,是一個标準的通用的接口,用于連接配接容器管理系統和網絡插件。

簡單來說,容器 runtime 為容器提供 network namespace,網絡插件負責将 network interface 插入該 network namespace 中并且在主控端做一些必要的配置,最後對 namespace 中的 interface 進行 IP 和路由的配置。

是以網絡插件的主要工作就在于為容器提供網絡環境,包括為 pod 設定 ip 位址、配置路由保證叢集内網絡的通暢。目前比較流行的網絡插件是 Flannel 和 Calico。

Flannel

Flannel 主要提供的是叢集内的 Overlay 網絡,支援三種後端實作,分别是:UDP 模式、VXLAN 模式、host-gw 模式。

UDP 模式

UDP 模式,是 Flannel 項目最早支援的一種方式,卻也是性能最差的一種方式。這種模式提供的是一個三層的 Overlay 網絡,即:它首先對發出端的 IP 包進行 UDP 封裝,然後在接收端進行解封裝拿到原始的 IP 包,進而把這個 IP 包轉發給目标容器。工作原理如下圖所示。

Kubernetes 網絡插件工作原理Kubernetes 網絡插件工作原理

node1 上的 pod1 請求 node2 上的 pod2 時,流量的走向如下:

  1. pod1 裡的程序發起請求,發出 IP 包;
  2. IP 包根據 pod1 裡的 veth 裝置對,進入到 cni0 網橋;
  3. 由于 IP 包的目的 ip 不在 node1 上,根據 flannel 在節點上建立出來的路由規則,進入到 flannel0 中;
  4. 此時 flanneld 程序會收到這個包,flanneld 判斷該包應該在哪台 node 上,然後将其封裝在一個 UDP 包中;
  5. 最後通過 node1 上的網關,發送給 node2;

flannel0 是一個 TUN 裝置(Tunnel 裝置)。在 Linux 中,TUN 裝置是一種工作在三層(Network Layer)的虛拟網絡裝置。TUN 裝置的功能:在作業系統核心和使用者應用程式之間傳遞 IP 包。

可以看到,這種模式性能差的原因在于,整個包的 UDP 封裝過程是 flanneld 程式做的,也就是使用者态,而這就帶來了一次核心态向使用者态的轉換,以及一次使用者态向核心态的轉換。在上下文切換和使用者态操作的代價其實是比較高的,而 UDP 模式因為封包拆包帶來了額外的性能消耗。

VXLAN 模式

VXLAN,即 Virtual Extensible LAN(虛拟可擴充區域網路),是 Linux 核心本身就支援的一種網絡虛似化技術。通過利用 Linux 核心的這種特性,也可以實作在核心态的封裝和解封裝的能力,進而建構出覆寫網絡。其工作原理如下圖所示:

Kubernetes 網絡插件工作原理Kubernetes 網絡插件工作原理

VXLAN 模式的 flannel 會在節點上建立一個叫 flannel.1 的 VTEP (VXLAN Tunnel End Point,虛拟隧道端點) 裝置,跟 UDP 模式一樣,該裝置将二層資料幀封裝在 UDP 包裡,再轉發出去,而與 UDP 模式不一樣的是,整個封裝的過程是在核心态完成的。

  1. 由于 IP 包的目的 ip 不在 node1 上,根據 flannel 在節點上建立出來的路由規則,進入到 flannel.1 中;
  2. flannel.1 将原始 IP 包加上一個目的 MAC 位址,封裝成一個二層資料幀;然後核心将資料幀封裝進一個 UDP 包裡;

抓包驗證

在 node1 上部署一個 nginx pod1,node2 上部署一個 nginx pod2。然後在 pod1 的容器中 curl pod2 容器的 80 端口。

叢集網絡環境如下:

node1 網卡 ens33:192.168.50.10
pod1 veth:10.244.0.7
node1 cni0:10.244.0.1/24
node1 flannel.1:10.244.0.0/32

node2 網卡 ens33:192.168.50.11
pod2 veth:10.244.1.9
node2 cni0:10.244.1.1/24
node2 flannel.1:10.244.1.0/32           

node1 上的路由資訊如下:

➜  ~ ip route
default via 192.168.50.1 dev ens33
10.244.0.0/24 dev cni0 proto kernel scope link src 10.244.0.1
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.50.0/24 dev ens33 proto kernel scope link src 192.168.50.10 metric 100           

node1 的網卡 ens33 的抓包情況:

Kubernetes 網絡插件工作原理Kubernetes 網絡插件工作原理

隻能看到源 ip 為 node1 ip、目的 ip 為 node2 ip 的 UDP 包。由于 flannel.1 進行了一層 UDP 封包,這裡我們在 Wireshark 中設定一下将 UDP 包解析為 VxLAN 格式(端口為 8472),設定過程為 Analyze->Decode As:

Kubernetes 網絡插件工作原理Kubernetes 網絡插件工作原理

然後再來看一下 node1 網卡上收到的包:

Kubernetes 網絡插件工作原理Kubernetes 網絡插件工作原理

可以看到源 ip 為 pod1 ip、目的 ip 為 pod2 ip,并且該 IP 包被封裝在 UDP 包中。

host-gw 模式

最後一種 host-gw 模式是一種純三層網絡方案。其工作原理為将每個 Flannel 子網的“下一跳”設定成了該子網對應的主控端的 IP 位址,這台主機會充當這條容器通信路徑裡的“網關”。這樣 IP 包就能通過二層網絡達到目的主機,而正是因為這一點,host-gw 模式要求叢集主控端之間的網絡是二層連通的,如下圖所示。

Kubernetes 網絡插件工作原理Kubernetes 網絡插件工作原理

主控端上的路由資訊是 flanneld 設定的,因為 flannel 子網和主機的資訊儲存在 etcd 中,是以 flanneld 隻需要 watch 這些資料的變化,實時更新路由表即可。在這種模式下,容器通信的過程就免除了額外的封包和解包帶來的性能損耗。

  1. pod1 裡的程序發起請求,發出 IP 包,從網絡層進傳入連結路層封裝成幀;
  2. 根據主機上的路由規則,資料幀從 Node 1 通過主控端的二層網絡到達 Node 2 上;

Calico

Calico 沒有使用 CNI 的網橋模式,而是将節點當成邊界路由器,組成了一個全連通的網絡,通過 BGP 協定交換路由。是以,Calico 的 CNI 插件還需要在主控端上為每個容器的 Veth Pair 裝置配置一條路由規則,用于接收傳入的 IP 包。

Calico 的元件:

  1. CNI 插件:Calico 與 Kubernetes 對接的部分
  2. Felix:負責在主控端上插入路由規則(即:寫入 Linux 核心的 FIB 轉發資訊庫),以及維護 Calico 所需的網絡裝置等工作。
  3. BIRD (BGP Route Reflector):是 BGP 的用戶端,專門負責在叢集裡分發路由規則資訊。

三個元件都是通過一個 DaemonSet 安裝的。CNI 插件是通過 initContainer 安裝的;而 Felix 和 BIRD 是同一個 pod 的兩個 container。

工作原理

Calico 采用的 BGP,就是在大規模網絡中實作節點路由資訊共享的一種協定。全稱是 Border Gateway Protocol,即:邊界網關協定。它是一個 Linux 核心原生就支援的、專門用在大規模資料中心裡維護不同的 “自治系統” 之間路由資訊的、無中心的路由協定。

由于沒有使用 CNI 的網橋,Calico 的 CNI 插件需要為每個容器設定一個 Veth Pair 裝置,然後把其中的一端放置在主控端上,還需要在主控端上為每個容器的 Veth Pair 裝置配置一條路由規則,用于接收傳入的 IP 包。如下圖所示:

Kubernetes 網絡插件工作原理Kubernetes 網絡插件工作原理

可以使用 calicoctl 檢視 node1 的節點連接配接情況:

~ calicoctl get no
NAME
node1
node2
node3
~ calicoctl node status
Calico process is running.

IPv4 BGP status
+---------------+-------------------+-------+------------+-------------+
| PEER ADDRESS  |     PEER TYPE     | STATE |   SINCE    |    INFO     |
+---------------+-------------------+-------+------------+-------------+
| 192.168.50.11 | node-to-node mesh | up    | 2020-09-28 | Established |
| 192.168.50.12 | node-to-node mesh | up    | 2020-09-28 | Established |
+---------------+-------------------+-------+------------+-------------+

IPv6 BGP status
No IPv6 peers found.           

可以看到整個 calico 叢集上有 3 個節點,node1 和另外兩個節點處于連接配接狀态,模式為 “Node-to-Node Mesh”。再看下 node1 上的路由資訊如下:

~ ip route
default via 192.168.50.1 dev ens33 proto static metric 100
10.244.104.0/26 via 192.168.50.11 dev ens33 proto bird
10.244.135.0/26 via 192.168.50.12 dev ens33 proto bird
10.244.166.128 dev cali717821d73f3 scope link
blackhole 10.244.166.128/26 proto bird
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.50.0/24 dev ens33 proto kernel scope link src 192.168.50.10 metric 100           

其中,第 2 條的路由規則表明 10.244.104.0/26 網段的資料包通過 bird 協定由 ens33 裝置發往網關 192.168.50.11。這也就定義了目的 ip 為 node2 上 pod 請求的走向。第 3 條路由規則與之類似。

與上面一樣,從 node1 上的 pod1 發送一個 http 請求到 node2 上的 pod2。

node1 網卡 ens33:192.168.50.10
pod1 ip:10.244.166.128

node2 網卡 ens33:192.168.50.11
pod2 ip:10.244.104.1           
Kubernetes 網絡插件工作原理Kubernetes 網絡插件工作原理

IPIP 模式

IPIP 模式為了解決兩個 node 不在一個子網的問題。隻要将名為 calico-node 的 daemonset 的環境變量 CALICO_IPV4POOL_IPIP 設定為 "Always" 即可。如下:

- name: CALICO_IPV4POOL_IPIP
              value: "Off"           

IPIP 模式的 calico 使用了 tunl0 裝置,這是一個 IP 隧道裝置。IP 包進入 tunl0 後,核心會将原始 IP 包直接封裝在主控端的 IP 包中;封裝後的 IP 包的目的位址為下一跳位址,即 node2 的 IP 位址。由于主控端之間已經使用路由器配置了三層轉發,是以這個 IP 包在離開 node 1 之後,就可以經過路由器,最終發送到 node 2 上。如下圖所示。

Kubernetes 網絡插件工作原理Kubernetes 網絡插件工作原理

由于 IPIP 模式的 Calico 額外多出了封包和拆包的過程,叢集的網絡性能受到了影響,是以在叢集的二層網絡通的情況下,建議不要使用 IPIP 模式。

看下 node1 上的路由資訊:

~ ip route
default via 192.168.50.1 dev ens33 proto static metric 100
10.244.104.0/26 via 192.168.50.11 dev tunl0 proto bird onlink
10.244.135.0/26 via 192.168.50.12 dev tunl0 proto bird onlink
blackhole 10.244.166.128/26 proto bird
10.244.166.129 dev calif3c799362a5 scope link
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.50.0/24 dev ens33 proto kernel scope link src 192.168.50.10 metric 100           

可以看到,與之前不一樣的是,目的 IP 為 node2 上的 Pod 的資料包是經由 tunl0 發送到網關 192.168.50.11。

從 node1 上的 pod1 發送一個 http 請求到 node2 上的 pod2。

node1 網卡 ens33:192.168.50.10
node1 tunl0:10.244.166.128/32
pod1 ip:10.244.166.129

node2 網卡 ens33:192.168.50.11
pod2 ip:10.244.104.2
node2 tunl0:10.244.104.0/32           

tunl0 裝置的抓包情況:

Kubernetes 網絡插件工作原理Kubernetes 網絡插件工作原理

node1 網卡 ens33 的抓包情況:

Kubernetes 網絡插件工作原理Kubernetes 網絡插件工作原理

可以看到,IP 包在 tunl0 裝置中被封裝進了另一個 IP 包,其目的 IP 為 node2 的 IP,源 IP 為 node1 的 IP。

總結

Kubernetes 的叢集網絡插件實作方案有很多種,本文主要分析了社群比較常見的兩種 Flannel 和 Calico 的工作原理,針對叢集内不同節點的 pod 間通信的場景,抓包分析了網絡包的走向。

Flannel 主要提供了 Overlay 的網絡方案,UDP 模式由于其封包拆包的過程涉及了多次上下文的切換,導緻性能很差,逐漸被社群抛棄;VXLAN 模式的封包拆包過程均在核心态,性能要比 UDP 好很多,也是最經常使用的模式;host-gw 模式不涉及封包拆包,是以性能相對較高,但要求節點間二層互通。

Calico 主要采用了 BGP 協定交換路由,沒有采用 cni0 網橋,當二層網絡不通的時候,可以采用 IPIP 模式,但由于涉及到封包拆包的過程,性能相對較弱,與 Flannel 的 VXLAN 模式相當。

繼續閱讀