天天看點

Kubernetes學習筆記之Calico CNI Plugin源碼解析(一)

1

Overview

    之前在Kubernetes學習筆記之kube-proxy service實作原理學習到calico會在worker節點上為pod建立路由route和虛拟網卡virtual interface,并為pod配置設定pod ip,以及為worker節點配置設定pod cidr網段。

   我們生産k8s網絡插件使用calico cni,在安裝時會安裝兩個插件:calico和calico-ipam,官網安裝文檔 Install the plugin(https://docs.projectcalico.org/getting-started/kubernetes/hardway/install-cni-plugin#install-the-plugin)也說到了這一點,而這兩個插件代碼在 calico.go(https://github.com/projectcalico/cni-plugin/blob/release-v3.17/cmd/calico/calico.go) ,代碼會編譯出兩個二進制檔案:calico和calico-ipam。calico插件主要用來建立route和virtual interface,而calico-ipam插件主要用來配置設定pod ip和為worker節點配置設定pod cidr。

    重要問題是,calico是如何做到的?

2

Sandbox container

 kubelet程序在開始啟動時,會調用容器運作時SyncPod(https://github.com/kubernetes/kubernetes/blob/release-1.17/pkg/kubelet/kubelet.go#L1692) 來建立pod内相關容器,

主要做了幾件事情 L657-L856(https://github.com/kubernetes/kubernetes/blob/release-1.17/pkg/kubelet/kuberuntime/kuberuntime_manager.go#L657-L856):

建立sandbox container,這裡會調用cni插件建立network等步驟,同時考慮了邊界條件,建立失敗會kill sandbox container等等。

建立ephemeral containers、init containers和普通的containers。

   這裡隻關注建立sandbox container過程,隻有這一步會建立pod network,這個sandbox container建立好後,其餘container都會和其共享同一個network namespace,是以一個pod内各個容器看到的網絡協定棧是同一個,ip位址都是相同的,通過port來區分各個容器。具體建立過程,會調用容器運作時服務建立容器,這裡會先準備好pod的相關配置資料,建立network namespace時也需要這些配置資料 L36-L138(https://github.com/kubernetes/kubernetes/blob/release-1.17/pkg/kubelet/kuberuntime/kuberuntime_sandbox.go#L36-L138) :

   k8s使用cri(container runtime interface)來抽象出标準接口,目前docker還不支援cri接口,是以kubelet做了個适配子產品dockershim,代碼在pkg/kubelet/dockershim。上面代碼中的runtimeService對象就是dockerService對象,是以可以看下dockerService.RunPodSandbox()代碼實作 L76-L197(https://github.com/kubernetes/kubernetes/blob/release-1.17/pkg/kubelet/dockershim/docker_sandbox.go#L76-L197):

   由于我們網絡插件都是cni(container network interface),代碼 ds.network.SetUpPod繼續追下去發現實際調用的是 cniNetworkPlugin.SetUpPod(),代碼在 pkg/kubelet/dockershim/network/cni/cni.go(https://github.com/kubernetes/kubernetes/blob/release-1.17/pkg/kubelet/dockershim/network/cni/cni.go#L300-L321) :

 addToNetwork()函數會調用cni标準庫裡的 AddNetworkList(https://github.com/containernetworking/cni/blob/master/libcni/api.go#L400-L440)函數。CNI是容器網絡标準接口Container Network Interface,這個代碼倉庫提供了CNI标準接口的相關實作,所有K8s網絡插件都必須實作該CNI代碼倉庫中的接口,K8s網絡插件如何實作規範可見 SPEC.md(https://github.com/containernetworking/cni/blob/master/SPEC.md) ,我們也可實作遵循該标準規範實作一個簡單的網絡插件。是以kubelet、cni和calico的三者關系就是:kubelet調用cni标準規範代碼包,cni調用calico插件二進制檔案。cni代碼包中的AddNetworkList相關代碼如下 AddNetworkList(https://github.com/containernetworking/cni/blob/master/libcni/api.go#L400-L440):

   以上pluginPath就是calico二進制檔案路徑,這裡calico二進制檔案路徑參數是在啟動kubelet時通過參數 --cni-bin-dir 傳進來的,可見官網kubelet command-line-tools-reference(https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/) ,并且啟動參數 --cni-conf-dir 包含cni配置檔案路徑,該路徑包含cni配置檔案内容類似如下:

   cni相關代碼是個标準骨架,核心還是需要調用第三方網絡插件來實作為sandbox建立網絡資源。cni也提供了一些示例plugins,代碼倉庫見 containernetworking/plugins(https://github.com/containernetworking/plugins),并配有文檔說明見 plugins docs(https://www.cni.dev/plugins/) ,比如可以參考學習官網提供的 static IP address management plugin(https://www.cni.dev/plugins/ipam/static/) 。

3

總結

   總之,kubelet在建立sandbox container時候,會先調用cni插件指令,如 calico ADD 指令并通過環境變量傳遞相關指令參數,來給sandbox container建立network相關資源對象,比如calico會建立route和virtual interface,以及為pod配置設定ip位址,和從叢集網段cluster cidr中為目前worker節點配置設定pod cidr網段,并且會把這些資料寫入到calico datastore資料庫裡。是以,關鍵問題,還是得看calico插件代碼是如何做的。

https://docs.projectcalico.org/networking/use-specific-ip

https://mp.weixin.qq.com/s/lyfeZh6VWWjXuLY8fl3ciw

https://www.yuque.com/baxiaoshi/tyado3/lvfa0b

https://github.com/containernetworking/cni

https://github.com/projectcalico/cni-plugin

Kubernetes學習筆記之Calico CNI Plugin源碼解析(一)

繼續閱讀