kubernetes-kubelet進程源碼分析(一)

kubelet是運行在Minion節點上的重要守護進程,是工作在一線的重要工人,它纔是負責實例化和啓動一個具體Pod的幕後主導,並且掌管着本節點上的Pod和容器的全生命週期過程,定時向master彙報工作情況。此外kubelet進程也是一個server進程,它默認監聽10250端口,接收並執行遠程(master)發來的指令。

進程啓動過程

kubelet進程的入口類源碼位置如下:

cmd/kubelet/kubelet.go

入口main函數的邏輯如下:

我們已經很多次遇見這樣的代碼風格了。

我們先看看kubelet這個結構體所包含的屬性吧,這些屬性可分爲以下幾組。

1.基本配置

kubeconfig:kubelet默認配置文件路徑

address、Port、ReadOnlyPort、CadvisorPort、HealthPort、HealthzBindAddress:爲kubelet綁定監聽的地址,包括自身server的地址、cadvisor綁定的地址,以及自身健康檢查服務的綁定地址等。

RootDirectory、CertDirectory:kubelet默認的工作目錄(/var/lib/kubelet),用於存放配置及VM卷等數據,CertDirectory用於存放證書目錄。

2.管理Pod和容器相關的參數

PodInfraContainerImage:Pod的infra容器的鏡像名稱,谷歌被屏蔽的時候可以換成自己的私有倉庫的鏡像名。

CgroupRoot:可選項,創建Pod的時候所使用的頂層的cgroup名字(Root Cgroup)。

ContainerRuntime、DockerDaemonContainer、SystemContainer:這三個參數分別表示選擇什麼容器技術(docker或RKT)、Docker Daemon容器的模擬購置及可選的系統資源容器名稱,用來將所有非kernel的、不在容器中的進程放入此容器中。

3.同步和自動運維相關的參數

SyncFrequency、FileCheckFrequency、HTTPCheckFrequency:Pod容器同步週期、當前運行容器實例分別與kubernetes註冊表中的信息,本地的Pod定義文件及HTTP方式提供信息的數據源進行對比同步。

RegistryPullQPS、RegistryBurst:從註冊表中拉取待創建的Pod列表時的流控參數。

NodeStatusUpdateFrequency:kubelet多久彙報一次當前Node的狀態。

ImageGCHighThresholdPercent、ImageGCLowThresholdPercent、LowDiskSpaceThresholdMB:分別是Image鏡像佔用磁盤空間的高低水位閾值及本機磁盤最小空閒容量,當可用容量低於這麼容量時,所有新Pod的創建請求會被拒絕。

MaxContainerCount、MaxPerPodContainerCount:分別是maximum-dead-containers與max-dead-containers-per-container,表示保留多少個死亡容器的實例在磁盤上,因爲每個實例都會佔用一定的磁盤,所以需要控制,默認是MaxContainerCount爲100,MaxPerPodContainerCount爲2,即每個Pod最多保存2個死亡實例,每個Node保存最多100個死亡實例。

通過分析上述kubeletserver結構體的關鍵屬性,我們可以得到這樣一個推論:kubelet進程的工作量還是很飽滿的。

在繼續下面代碼分析之前,我們先要理解這裏的一個重要概念“Pod Source”,它是kubelet用於獲取Pod定義和描述信息的一個數據源,kubelet進程查詢並監聽Pod source來獲取屬於自己所在節點的Pod列表,當前支持三種Pod source類型。

1.Config file:本地配置文件作爲Pod數據源

2.Http URL:Pod數據源的內容通過一個HTTP URL方式獲取

3.kubernetes API server:默認方式,從API server獲取Pod數據源。

進程根據啓動參數創建了kubeletserver以後,調用kubeletserver的Run方法,進入啓動流程,在流程的一開始首先設置了自身進程的oom_adj(默認爲-900),這是利用了Linux的OOM機制,當系統發生OOM時,oom-adj的值越小,越不容易被系統kill。

爲什麼在之前的Master節點進程上都沒見到這個調用,而在kubelet進程上卻看到這段邏輯?答案很簡單,因爲Master節點不運行Pod和容器,主機資源通常是穩定和寬裕的,而minion節點由於需要運行大量的Pod和容器,因此容易產生OOM問題,所以這裏要確保“守護者”不會因此被系統kill掉。

由於kubelet會跟API Server打交道,所以接下來創建了一個Rest Client對象來訪問API Server。隨後,啓動進程構造了CAdvisor來監控本地的docker容器,CAdvisor具體的創建代碼則位於pkg/kubelet/cadvisor/cadvisor_linux.go裏。

接着,初始化CloudProvider。這是因爲如果kubernetes運行在某個運營商的cloud環境中,則很多環境和資源都需要從CloudProvider中獲取,比如在創建Pod過程中可能需要知道某個Node的真實主機名。

雖然容器可以綁定宿主機的網絡空間,但若不當使用會導致系統安全漏洞,所以kubeletserver中的hostnetworksources的屬性是用來控制哪些Pod允許綁定宿主機的網絡空間,默認都是禁止綁定。比如,設置hostnetworksources=api,http,則表明當一個Pod的定義源來自kubernetes API  Server或者某個HTTP URL時,則允許此Pod綁定到宿主機的網絡空間。下面這行代碼即上述處理邏輯的一小部分:

接下來加載數字證書,如果沒有提供證書和私鑰,則默認創建了一個自簽名的X509證書並保存到本地,下一步,創建一個mounter對象,用於實現容器的文件系統掛載功能。

接下來這段代碼根據指定了DockerExecHandlerName參數的值,確定dockerExecHandler是採用Docker的exec命令還是nsenter來實現,默認採用了docker的exec這種本地方式,docker從1.3版本開始提供了exec命令,爲進入容器內部提供了更好的手段。

運行至此,程序構造了一個kubeletConfig結構體,回座位參數調用RunKubelet()方法,程序運行到這裏,才真正進入流程的核心步驟。下面這段代碼表面kubelet會把自己的事件通知API Server:

接下來,啓動進程進入關鍵函數createAndInitKubelet中,這裏首先創建一個PodConfig對象,並根據啓動參數中Pod Source參數是否提供,來創建相應類型的Pod Source對象,這些PodSource在各種協程中運行,拉取Pod信息並彙總輸出到同一個Pod Channel中等待kubelet處理。創建PodConfig的具體代碼如下:

然後創建一個kubelet並宣告它的誕生:

接着觸發kubelet開啓垃圾回收協程以清理無用的容器和鏡像,釋放磁盤空間,下面是代碼片段:

createAndInitKubelet方法創建kubelet實例後,返回到RunKubelet方法裏,接下來調用startKubelet方法,此方法首先啓動一個協程,讓kubelet處理來自PodSource的Pod Update消息,然後啓動kubelet server,下面是具體代碼:

至此,kubelet進程啓動完畢。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章