這是 Kubernetes 網絡學習的第三篇筆記。
- 深入探索 Kubernetes 網絡模型和網絡通信
- 認識一下容器網絡接口 CNI(本篇)
- 源碼分析:從 kubelet、容器運作時看 CNI 的使用
- 從 Flannel 學習 Kubernetes VXLAN 網絡
- Cilium CNI 與 eBPF
- ...
在上一篇中,通過對 CNI 規範的解讀了解了網絡配置的操作和相關的流程。在網絡的幾個操作中除了
CNI_COMMAND
外,有另外三個參數幾乎每次都要提供
CNI_CONTAINERID
、
CNI_IFNAME
和
CNI_NETNS
,這些參數無外乎都來自容器運作時。這篇将結合 Kubernetes 和 Containerd 源碼,來分析一下 CNI 的使用。
Kubernetes 的源碼來自分支
release-1.24
,Containerd 的來自分支
release/1.6
。
CNI 的使用
建立 Pod
在之前做過的 kubelet 源碼分析 中曾提到
Kubelet#syncLoop()
會持續監控來自 檔案、apiserver、http 的變更,來更新 pod 的狀态。寫那篇文章的時候,分析到這裡就結束了。因為這之後的工作就交給容器運作時來完成 sandbox 和各種容器的建立和運作,見
kubeGenericRuntimeManager#SyncPod()
。
kubelet
封裝 sandbox 和容器建立、運作請求,調用容器運作時的接口,将具體工作交由容器運作時來完成來完成(容器運作時接口 Container Runtime Interface,簡稱 CRI,找時間再進行研究)。
參考源碼
-
pkg/kubelet/kubelet.go:1985
-
pkg/kubelet/kuberuntime/kuberuntime_manager.go:711
Sandbox 容器
記得在 系列的第一篇 中,當我們在節點上檢視命名空間時,網絡命名空間的程序是
/pause
。
lsns -t net
NS TYPE NPROCS PID USER NETNSID NSFS COMMAND
4026531992 net 126 1 root unassigned /lib/systemd/systemd --system --deserialize 31
4026532247 net 1 83224 uuidd unassigned /usr/sbin/uuidd --socket-activation
4026532317 net 4 129820 65535 0 /run/netns/cni-607c5530-b6d8-ba57-420e-a467d7b10c56 /pause
Kubernetes 在建立 pod 時,會先一個 sandbox 容器(使用
pause
鏡像,啟動時執行
/pause
進入休眠狀态)。我們知道 Kubernetes 的 pod 中是允許多容器的,由這個 sandbox 容器來建立和維持網絡命名空間,pod 的其他容器會加入到該命名空間中。因為 pause 鏡像足夠簡單,不會出錯導緻網絡管理空間在出錯時被删除。sandbox 容器發揮着至關重要的作用,它在 PID 程序空間的程序樹中作為 PID 為 1 的程序,其他容器程序都将其作為父程序。當其他容器的程序成為孤兒程序時,可以得到清理。
建立 Sandbox 容器
CRI 的
RuntimeServiceServer
定義了運作時對外提供的服務接口,除了管理 sandbox、容器相關的操作外,還有 streaming 相關的操作,即常用的
exec
、
attach
、
portforward
。streaming 相關的内容,可以參考之前的一篇 《源碼解析 kubectl port-forward 工作原理》。
讓我們來看容器相關的部分。
Containerd 的
criService
實作了
RuntimeServiceServer
的接口。建立 sandbox 容器的請求通過 CRI 的 UDS(Unix domain socket) 接口
/runtime.v1.RuntimeService/RunPodSandbox
,進入到
criService
的處理流程中。在
criService#RunPodSandbox()
,負責建立和運作 sandbox 容器,并保證容器狀态正常。
- 容器運作時首先初始化容器對象,産生必要的參數
CNI_CONTAINERID
- 會建立 pod 網絡命名空間,産生必要的參數
CNI_NETNS
- 然後調用 CNI 的接口來對 pod 的網絡空間進行配置,比如建立網絡接口、配置設定 IP 位址、建立 veth、設定路由等等一系列的操作。這些操作正是由具體的網絡插件實作完成,不同插件之間的實作存在差異。了解了規範之後之後,網絡的配置就不難了,其中 2 和 3 可能執行多次:
- 讀取網絡配置
- 查找二進制檔案
- 執行二進制檔案
- 向容器運作時回報結果
- 最後便是建立 sandbox 容器,這個過程與作業系統的類型相關,會調用對應作業系統的方法來完成容器的建立。
從零開始學習容器,推薦閱讀 Ivan Velichko 的 《Learning Containers From The Bottom Up》
參考源碼:
-
pkg/cri/server/sandbox_run.go:61
-
pkg/cri/server/sandbox_run.go:422
建立其他容器
接下來就是建立 pod 内的其他容器:臨時(
ephemeral
)、初始化(
init
)和普通容器,建立這些容器的時候,會将 sandbox 容器的。會加入到 sandox 的網絡命名空間中。這裡不展開,詳細邏輯可參考 containerd 的
containerStore#Create()
。
參考源碼
- kubernetes:
pkg/kubelet/kuberuntime/kuberuntime_manager.go:913
- containerd:
pkg/cri/server/container_create.go:51
總結
接着上篇 CNI 的規範介紹,這次又介紹了 CNI 的使用,以及如何與容器運作時的互動、Pod 的建立流程。
不同的 CNI 插件,實作了不一樣的網絡功能。下篇,将以 Flannel 為例來了解下 CNI 的實作,以及 Kubernetes VXLAN 網絡。
為什麼介紹 flannel?因為我常用的開發環境之一 k3s 預設就用的 flannel 網絡。另一個開發環境是 k8e ,k8e 預設用的是 Cilium,cilium 的 cni 也是系列的文章之一。
關注"雲原生指北"微信公衆号 (轉載本站文章請注明作者和出處亂世浮生,請勿用于任何商業用途)