Kubernetes 網絡模型的核心要求之一是每個 Pod 都擁有自己的 IP 位址并可以使用該 IP 位址進行通信。很多人剛開始使用 Kubernetes 時,還不清楚如何為每個 Pod 配置設定 IP 位址。他們了解各種元件如何獨立工作,但不清楚這些元件如何組合在一起使用。例如,他們了解什麼是 CNI 插件,但是不知道它們是如何被調用的。本文就介紹了各種網絡元件在 Kubernetes 叢集中是如何互動的,以及如何幫助每個 Pod 都擷取 IP 位址。
在 Kubernetes 中有多種網絡設定方法,以及 container runtime 的各種選項。這篇文章将使用 Flannel 作為 network provider,并使用 Containered 作為 container runtime。
背景概念
容器網絡
同一主機上的容器
在同一主機上運作的容器通過 IP 位址互相通信的方法之一是使用 Linux Bridge,即在 Kubernetes(和 Docker)世界中,建立 veth(虛拟以太網)裝置。該 veth 裝置的一端連接配接在容器網絡命名空間,另一端連接配接到主機網絡上的 Linux Bridge。同一主機上的所有容器都将這 veth pair 的一端連接配接到 Linux Bridge,它們可以通過 Bridge 使用 IP 位址互相通信。Linux Bridge 也被配置設定了一個 IP 位址,它充當從目的地到不同節點的 Pod 流出流量的網關。
不同主機上的容器
在不同主機上運作的容器可以通過其 IP 位址互相通信的方式之一是使用資料包封裝(packet encapsulation)。Flannel 通過 vxlan 使用該功能,vxlan 将原始資料包封裝在 UDP 資料包中并将其發送到目的地。
在 Kubernetes 叢集中,Flannel 會在每個節點上建立一個 vxlan 裝置和一些路由表。每個發往不同主機上的容器的資料包都會通過 vxlan 裝置,并封裝在 UDP 資料包中。在目标位置,它會提取封裝的資料包,然後将資料包路由到目的地 Pod。
注意:這隻是配置容器之間網絡的方法之一。
CRI
CRI(容器運作時接口)是一個插件接口,允許 kubelet 使用不同的 container runtimes。各種 container runtimes 都實作了 CRI API,這使使用者可以在 Kubernetes 安裝中使用他們想要的 container runtimes。
CNI
CNI(容器網絡接口)項目包含一個為 Linux 容器提供基于通用插件網絡解決方案的規則。它由各種插件組成,這些插件在配置 Pod 網絡時執行不同的功能。CNI 插件是遵循 CNI 規範的可執行檔案。
為節點子網配置設定 Pod IP 位址
如果要求所有 Pod 具有 IP 位址,那麼就要確定整個叢集中的所有 Pod 的 IP 位址是唯一的。這可以通過為每個節點配置設定一個唯一的子網來實作,即從子網中為 Pod 配置設定節點 IP 位址。
節點 IPAM 控制器
當 nodeipam 傳遞給 kube-controller-manager 的 --controllers 指令行标志時,它将為每個節點配置設定來自叢集 CIDR(叢集網絡的 IP 範圍)的專用子網(podCIDR)。由于這些 podCIDR 是不相交的子網,是以它可以為每個 Pod 配置設定唯一的 IP 位址。
當 Kubernetes 節點首次在叢集上注冊時,會被配置設定一個 podCIDR。要更改配置設定給叢集中節點的 podCIDR,需要先登出節點,然後使用應用于 Kubernetes 控制平面的任何配置更改來重新注冊節點。podCIDR 可以使用以下指令列出節點的名稱:
Kubelet、Container Runtime 和 CNI 插件互動
當在節點上排程 Pod 時,一啟動 Pod 就會發生很多事情。這裡我們僅關注與 Pod 配置網絡有關的動态。一旦在節點上排程了 Pod,将配置網絡并啟動應用程式容器。
參考:容器式 cri 插件架構
Container Runtime 與 CNI 插件的互動
每個 network provider 都有一個 CNI 插件,container runtime 會調用該插件,在 Pod 啟動時配置網絡。使用容器化作為 container runtime,容器化 CRI 插件将調用 CNI 插件。每個 network provider 都在每個 Kubernetes 節點上安裝了一個代理,以配置 Pod 網絡。安裝 network provider agent 後,它會随 CNI 一起配置或者在節點上建立,CRI 插件會使用它來确定要調用哪個 CNI 插件。
CNI 配置檔案的位置是可配置的,預設值為 /etc/cni/net.d/<config-file>。叢集管理者需要在每個節點上傳遞 CNI 插件。CNI 插件的位置也是可配置的,預設值為 /opt/cni/bin。
如果使用 containerd 作為 container runtime,則可以在 containerd config 部分下 [plugins."io.containerd.grpc.v1.cri".cni] 指定 CNI 配置和 CNI 插件的路徑。
本文中我們将 Flannel 作為 network provider,這裡簡單介紹一下 Flannel 的設定。Flanneld 是 Flannel 守護程式,通常 install-cni 作為帶有初始化容器的守護程式安裝在 Kubernetes 叢集上。install-cni 容器建立 CNI 配置檔案在每個節點上 /etc/cni/net.d/10-flannel.conflist。Flanneld 建立一個 vxlan 裝置,從 apiserver 擷取網絡中繼資料,并監控 Pod 上的更新。建立 Pod 時,它将在整個叢集中為所有 Pod 配置設定路由,這些路由允許 Pod 通過 IP 位址互相連接配接。
Containerd CRI 插件和 CNI 插件之間的互動可以如下所示:
如上所述,kubelet 調用 Containered CRI 插件建立容器,再調用 CNI 插件為容器配置網絡。Network provider CNI 插件調用其他基本 CNI 插件來配置網絡。CNI 插件之間的互動如下所述。
CNI 插件之間的互動
有多種 CNI 插件可幫助配置主機上容器之間的網絡,本文主要讨論以下 3 個插件。
Flannel CNI 插件
當使用 Flannel 作為 network provider 時,Containered CRI 插件使用 CNI 配置檔案,調用 Flannel CNI 插件:/etc/cni/net.d/10-flannel.conflist。
Fannel CNI 插件與 Flanneld 結合使用,當 Flanneld 啟動時,它将從 apiserver 中擷取 podCIDR 和其他與網絡相關的詳細資訊,并将它們存儲在檔案中/run/flannel/subnet.env。
Flannel CNI 插件使用 /run/flannel/subnet.env 的資訊來配置和調用 Bridge CNI 插件。
Bridge CNI 插件
Flannel CNI 插件使用以下配置調用 Bridge CNI 插件:
當 Bridge CNI 插件第一次調用時,它會建立一個 Linux Bridge "name": "cni0" 在配置檔案中,然後為每個 Pod 建立 veth pair,其一端在容器的網絡命名空間中,另一端連接配接到主機網絡上的 Linux Bridge。使用 Bridge CNI 插件,主機上的所有容器都連接配接到主機網絡上的 Linux Bridge。
配置完 veth pair 後,Bridge 插件将調用主機本地 IPAM CNI 插件。我們可以在 CNI config 中配置要使用的 IPAM 插件,CRI 插件用于調用 Flannel CNI插件。
主機本地 IPAM CNI 插件
Bridge CNI 插件使用以下配置調用主機本地 IPAM CNI 插件:
主機本地 IPAM(IP 位址管理)插件從中傳回容器的 IP 位址,subnet将配置設定的 IP 本地存儲在主機下dataDir指定的目錄中
/var/lib/cni/networks/<network-name=cni0>/<ip>
/var/lib/cni/networks/<network-name=cni0>/<ip>檔案包含 IP 配置設定到的容器 ID。
調用時,主機本地 IPAM 插件傳回以下有效負載:
總結
Kube-controller-manager 為每個節點配置設定一個 podCIDR。從 podCIDR 中的子網值為節點上的 Pod 配置設定了 IP 位址。由于所有節點上的 podCIDR 是不相交的子網,是以它允許為每個 pod 配置設定唯一的IP位址。
Kubernetes 叢集管理者可配置和安裝 kubelet、container runtime、network provider,并在每個節點上分發 CNI 插件。Network provider agent 啟動時,将生成 CNI 配置。在節點上排程 Pod 後,kubelet 會調用 CRI 插件來建立 Pod。在容器情況下,容器的 CRI 插件調用 CNI 配置中指定的 CNI 插件來配置 Pod 網絡。所有這些都會影響 Pod 擷取 IP位址。