天天看點

源碼解析:從 kubelet、容器運作時看 CNI 的使用

源碼解析:從 kubelet、容器運作時看 CNI 的使用

這是 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 的使用

源碼解析:從 kubelet、容器運作時看 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 容器,并保證容器狀态正常。

  1. 容器運作時首先初始化容器對象,産生必要的參數

    CNI_CONTAINERID

  2. 會建立 pod 網絡命名空間,産生必要的參數

    CNI_NETNS

  3. 然後調用 CNI 的接口來對 pod 的網絡空間進行配置,比如建立網絡接口、配置設定 IP 位址、建立 veth、設定路由等等一系列的操作。這些操作正是由具體的網絡插件實作完成,不同插件之間的實作存在差異。了解了規範之後之後,網絡的配置就不難了,其中 2 和 3 可能執行多次:
    1. 讀取網絡配置
    2. 查找二進制檔案
    3. 執行二進制檔案
    4. 向容器運作時回報結果
  4. 最後便是建立 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 也是系列的文章之一。

關注"雲原生指北"微信公衆号 (轉載本站文章請注明作者和出處亂世浮生,請勿用于任何商業用途)

繼續閱讀