作者 | 聲東 阿里雲售后技術專家
導讀:阿里雲 K8S 集群網絡目前有兩種方案:一種是 flannel 方案;另外一種是基於 calico 和彈性網卡 eni 的 terway 方案。Terway 和 flannel 類似,不同的地方在於 terway 支持 Pod 彈性網卡,以及 NetworkPolicy 功能。本文中,作者基於當前的 1.12.6 版本,以 flannel 為例,深入分析阿里雲 K8S 集群網絡的實現方法。
鳥瞰
總體上來說,阿里雲 K8S 集群網絡配置完成之后,如下圖所示:包括集群 CIDR、VPC 路由表、節點網絡、節點的 podCIDR、節點上的虛擬網橋 cni0、連接 Pod 和網橋的 veth 等部分。

類似的圖大家可能在很多文章中都看過,但因為其中相關配置過於復雜,比較難理解。這里我們可以看下這些配置背后的邏輯。
基本上我們可以把這些配置分三種情況來理解:集群配置,節點配置以及 Pod 配置。與這三種情況對應的,其實是對集群網絡 IP 段的三次划分:首先是集群 CIDR,接着是為每個節點分配 podCIDR(即集群 CIDR 的子網段),最后在 podCIDR 里為每個 Pod 分配自己的 IP。

集群網絡搭建
初始階段
集群的創建,基於雲資源 VPC 和 ECS,在創建完 VPC 和 ECS 之后,我們基本上可以得到如下圖的資源配置。我們得到一個 VPC,這個 VPC 的網段是 192.168.0.0/16,我們得到若干 ECS,他們從 VPC 網段里分配到 IP 地址。

集群階段
在以上出初始資源的基礎上,我們利用集群創建控制台得到集群 CIDR。這個值會以參數的形式傳給集群節點 provision 腳本,並被腳本傳給集群節點配置工具 kubeadm。kubeadm 最后把這個參數寫入集群控制器靜態 Pod 的 yaml 文件 kube-controller-manager.yaml。

集群控制器有了這個參數,在節點 kubelet 注冊節點到集群的時候,集群控制器會為每個注冊節點,划分一個子網出來,即為每個節點分配 podCIDR。如上圖,Node B 的子網是 172.16.8.1/25,而 Node A 的子網是 172.16.0.128/25。這個配置會記錄到集群 node 的 podCIDR 數據項里。
節點階段
經過以上集群階段,K8S 有了集群 CIDR,以及為每個節點划分的 podCIDR。在此基礎上,集群會下發 flanneld 到每個階段上,進一步搭建節點上,可以給 Pod 使用的網絡框架。這里主要有兩個操作:
- 第一個是集群通過 Cloud Controller Manager 給 VPC 配置路由表項。路由表項對每個節點有一條,每一條的意思是,如果 VPC 路由收到的目的地址是某一個節點 podCIDR 的 IP 地址,那么路由會把這個網絡包轉發到對應的 ECS 上;
- 第二個是創建虛擬網橋 cni0 以及與 cni0 相關的路由。這些配置的作用是,從階段外部進來的網絡包,如果目的 IP 是 podCIDR,則會被節點轉發到 cni0 虛擬局域網里。
注意:實際實現上,cni0 的創建,是在第一個使用 Pod 網絡的 Pod 被調度到節點上的時候,由下一節中 flannal cni 創建的,但是從邏輯上來說,cni0 屬於節點網絡,不屬於 Pod 網絡,所以在此描述。

Pod 階段
在前邊的三個階段,集群實際上已經為 Pod 之間搭建了網絡通信的干道。這個時候,如果集群把一個 Pod 調度到節點上,kubelet 會通過 flannel cni 為這個 Pod 本身創建網絡命名空間和 veth 設備,然后,把其中一個 veth 設備加入到 cni0 虛擬網橋里,並為 Pod 內的 veth 設備配置 IP 地址。這樣 Pod 就和網絡通信的干道連接在了一起。
這里需要強調的是,前一節的 flanneld 和這一節的 flannel cni 完全是兩個組件。flanneld 是一個 daemonset 下發到每個節點的 pod,它的作用是搭建網絡(干道),而 flannel cni 是節點創建的時候,通過 kubernetes-cni 這個 rpm 包安裝的 cni 插件,其被 kubelet 調用,用來為具體的 pod 創建網絡(分枝)。理解這兩者的區別,有助於我們理解 flanneld 和 flannel cni 相關的配置文件的用途。比如 /run/flannel/subnet.env,是 flanneld 創建的,為 flannel cni 提供輸入的一個環境變量文件;又比如 /etc/cni/net.d/10-flannel.conf,也是 flanneld pod(准確的說,是 pod 里的腳本 install-cni)從 pod 里拷貝到節點目錄,給 flannel cni 使用的子網配置文件。

通信
以上完成 Pod 網絡環境搭建。基於以上的網絡環境,Pod 可以完成四種通信:本地通信;同節點 Pod 通信;跨節點 Pod 通信;以及 Pod 和 Pod 網絡之外的實體通信。

其中本地通信,說的是 Pod 內部,不同容器之間的通信。因為 Pod 內網容器之間共享一個網絡協議棧,所以他們之間的通信,可以通過 loopback 設備完成。
同節點 Pod 之間的通信,是 cni0 虛擬網橋內部的通信,這相當於一個二層局域網內部設備通信。
跨節點 Pod 通信略微復雜一點,但也很直觀,發送端數據包,通過 cni0 網橋的網關,流轉到節點上,然后經過節點 eth0 發送給 VPC 路由。這里不會經過任何封包操作。當 VPC 路由收到數據包時,它通過查詢路由表,確認數據包目的地,並把數據包發送給對應的 ECS 節點。而進去節點之后,因為 flanneld 在節點上創建了 cni0 的路由,所以數據包會被發送到目的地的 cni0 局域網,再到目的地 Pod。
最后一種情況,Pod 與非 Pod 網絡的實體通信,需要經過節點上 iptables 規則做 SNAT,而此規則就是 flanneld 依據命令行 --ip-masq 選項做的配置。
總結
以上是阿里雲 K8S 集群網絡的搭建和通信原理。我們主要通過網絡搭建和通信兩個角度去分析 K8S 集群網絡。其中網絡搭建包括初始階段、集群階段、節點階段以及 Pod 階段,這么分類有助於我們理解這些復雜的配置。而理解了各個配置,集群通信原理就比較容易理解了。
“ 阿里巴巴雲原生微信公眾號(ID:Alicloudnative)關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術公眾號。”
