kubelet 主要功能:
-
pod 管理:kubelet 定期從所監聽的數據源獲取節點上 pod/container 的期望狀態(運行什么容器、運行的副本數量、網絡或者存儲如何配置等等),並調用對應的容器平台接口達到這個狀態。
-
容器健康檢查:kubelet 創建了容器之后還要查看容器是否正常運行,如果容器運行出錯,就要根據 pod 設置的重啟策略進行處理。
-
容器監控:kubelet 會監控所在節點的資源使用情況,並定時向 master 報告,資源使用數據都是通過 cAdvisor 獲取的。知道整個集群所有節點的資源情況,對於 pod 的調度和正常運行至關重要
kubelet 組件中的模塊
-
1、PLEG(Pod Lifecycle Event Generator) PLEG 是 kubelet 的核心模塊,PLEG 會一直調用 container runtime 獲取本節點 containers/sandboxes 的信息,並與自身維護的 pods cache 信息進行對比,生成對應的 PodLifecycleEvent,然后輸出到 eventChannel 中,通過 eventChannel 發送到 kubelet syncLoop 進行消費,然后由 kubelet syncPod 來觸發 pod 同步處理過程,最終達到用戶的期望狀態。
-
2、cAdvisor cAdvisor(https://github.com/google/cadvisor)是 google 開發的容器監控工具,集成在 kubelet 中,起到收集本節點和容器的監控信息,大部分公司對容器的監控數據都是從 cAdvisor 中獲取的 ,cAvisor 模塊對外提供了 interface 接口,該接口也被 imageManager,OOMWatcher,containerManager 等所使用。
-
3、OOMWatcher 系統 OOM 的監聽器,會與 cadvisor 模塊之間建立 SystemOOM,通過 Watch方式從 cadvisor 那里收到的 OOM 信號,並產生相關事件。
-
4、probeManager probeManager 依賴於 statusManager,livenessManager,containerRefManager,會定時去監控 pod 中容器的健康狀況,當前支持兩種類型的探針:livenessProbe 和readinessProbe。 livenessProbe:用於判斷容器是否存活,如果探測失敗,kubelet 會 kill 掉該容器,並根據容器的重啟策略做相應的處理。 readinessProbe:用於判斷容器是否啟動完成,將探測成功的容器加入到該 pod 所在 service 的 endpoints 中,反之則移除。readinessProbe 和 livenessProbe 有三種實現方式:http、tcp 以及 cmd。
-
5、statusManager statusManager 負責維護狀態信息,並把 pod 狀態更新到 apiserver,但是它並不負責監控 pod 狀態的變化,而是提供對應的接口供其他組件調用,比如 probeManager。
-
6、containerRefManager 容器引用的管理,相對簡單的Manager,用來報告容器的創建,失敗等事件,通過定義 map 來實現了 containerID 與 v1.ObjectReferece 容器引用的映射。
-
7、evictionManager 當節點的內存、磁盤或 inode 等資源不足時,達到了配置的 evict 策略, node 會變為 pressure 狀態,此時 kubelet 會按照 qosClass 順序來驅趕 pod,以此來保證節點的穩定性。可以通過配置 kubelet 啟動參數
--eviction-hard=
來決定 evict 的策略值。 -
8、imageGC imageGC 負責 node 節點的鏡像回收,當本地的存放鏡像的本地磁盤空間達到某閾值的時候,會觸發鏡像的回收,刪除掉不被 pod 所使用的鏡像,回收鏡像的閾值可以通過 kubelet 的啟動參數
--image-gc-high-threshold
和--image-gc-low-threshold
來設置。 -
9、containerGC containerGC 負責清理 node 節點上已消亡的 container,具體的 GC 操作由runtime 來實現。
-
10、imageManager 調用 kubecontainer 提供的PullImage/GetImageRef/ListImages/RemoveImage/ImageStates 方法來保證pod 運行所需要的鏡像。
-
11、volumeManager 負責 node 節點上 pod 所使用 volume 的管理,volume 與 pod 的生命周期關聯,負責 pod 創建刪除過程中 volume 的 mount/umount/attach/detach 流程,kubernetes 采用 volume Plugins 的方式,實現存儲卷的掛載等操作,內置幾十種存儲插件。
-
12、containerManager 負責 node 節點上運行的容器的 cgroup 配置信息,kubelet 啟動參數如果指定
--cgroups-per-qos
的時候,kubelet 會啟動 goroutine 來周期性的更新 pod 的 cgroup 信息,維護其正確性,該參數默認為true
,實現了 pod 的Guaranteed/BestEffort/Burstable 三種級別的 Qos。 -
13、runtimeManager containerRuntime 負責 kubelet 與不同的 runtime 實現進行對接,實現對於底層 container 的操作,初始化之后得到的 runtime 實例將會被之前描述的組件所使用。可以通過 kubelet 的啟動參數
--container-runtime
來定義是使用docker 還是 rkt,默認是docker
。 -
14、podManager podManager 提供了接口來存儲和訪問 pod 的信息,維持 static pod 和 mirror pods 的關系,podManager 會被statusManager/volumeManager/runtimeManager 所調用,podManager 的接口處理流程里面會調用 secretManager 以及 configMapManager
kubelet啟動流程
首先從 kubelet 的 main
函數開始,其中調用的 NewKubeletCommand
方法主要負責獲取配置文件中的參數,校驗參數以及為參數設置默認值。主要邏輯為:
- 1、解析命令行參數;
- 2、為 kubelet 初始化 feature gates 參數;
- 3、加載 kubelet 配置文件;
- 4、校驗配置文件中的參數;
- 5、檢查 kubelet 是否啟用動態配置功能;
- 6、初始化 kubeletDeps,kubeletDeps 包含 kubelet 運行所必須的配置,是為了實現 dependency injection,其目的是為了把 kubelet 依賴的組件對象作為參數傳進來,這樣可以控制 kubelet 的行為;
- 7、調用
Run
方法;
Run
該方法中僅僅調用 run
方法執行后面的啟動邏輯。
run
方法中主要是為 kubelet 的啟動做一些基本的配置及檢查工作,主要邏輯為:
- 1、為 kubelet 設置默認的 FeatureGates,kubelet 所有的 FeatureGates 可以通過命令參數查看,k8s 中處於
Alpha
狀態的 FeatureGates 在組件啟動時默認關閉,處於Beta
和 GA 狀態的默認開啟; - 2、校驗 kubelet 的參數;
- 3、嘗試獲取 kubelet 的
lock file
,需要在 kubelet 啟動時指定--exit-on-lock-contention
和--lock-file
,該功能處於Alpha
版本默認為關閉狀態; - 4、將當前的配置文件注冊到 http server
/configz
URL 中; - 5、檢查 kubelet 啟動模式是否為 standalone 模式,此模式下不會和 apiserver 交互,主要用於 kubelet 的調試;
- 6、初始化 kubeDeps,kubeDeps 中包含 kubelet 的一些依賴,主要有
KubeClient
、EventClient
、HeartbeatClient
、Auth
、cadvisor
、ContainerManager
; - 7、檢查是否以 root 用戶啟動;
- 8、為進程設置 oom 分數,默認為 -999,分數范圍為 [-1000, 1000],越小越不容易被 kill 掉;
- 9、調用
RunKubelet
方法; - 10、檢查 kubelet 是否啟動了動態配置功能;
- 11、啟動 Healthz http server;
- 12、如果使用 systemd 啟動,通知 systemd kubelet 已經啟動;
RunKubelet
RunKubelet
中主要調用了 createAndInitKubelet
方法執行 kubelet 組件的初始化,然后調用 startKubelet
啟動 kubelet 中的組件。
createAndInitKubelet
createAndInitKubelet
中主要調用了三個方法來完成 kubelet 的初始化:
kubelet.NewMainKubelet
:實例化 kubelet 對象,並對 kubelet 依賴的所有模塊進行初始化;k.BirthCry
:向 apiserver 發送一條 kubelet 啟動了的 event;k.StartGarbageCollection
:啟動垃圾回收服務,回收 container 和 images;
kubelet.NewMainKubelet
NewMainKubelet
是初始化 kubelet 的一個方法,主要邏輯為:
- 1、初始化 PodConfig 即監聽 pod 元數據的來源(file,http,apiserver),將不同 source 的 pod configuration 合並到一個結構中;
- 2、初始化 containerGCPolicy、imageGCPolicy、evictionConfig 配置;
- 3、啟動 serviceInformer 和 nodeInformer;
- 4、初始化
containerRefManager
、oomWatcher
; - 5、初始化 kubelet 對象;
- 6、初始化
secretManager
、configMapManager
; - 7、初始化
livenessManager
、podManager
、statusManager
、resourceAnalyzer
; - 8、調用
kuberuntime.NewKubeGenericRuntimeManager
初始化containerRuntime
; - 9、初始化
pleg
; - 10、初始化
containerGC
、containerDeletor
、imageManager
、containerLogManager
; - 11、初始化
serverCertificateManager
、probeManager
、tokenManager
、volumePluginMgr
、pluginManager
、volumeManager
; - 12、初始化
workQueue
、podWorkers
、evictionManager
; - 13、最后注冊相關模塊的 handler;
NewMainKubelet
中對 kubelet 依賴的所有模塊進行了初始化,每個模塊對應的功能在上篇文章“kubelet 架構淺析”有介紹
在startKubelet
中通過調用 k.Run
來啟動 kubelet 中的所有模塊以及主流程,然后啟動 kubelet 所需要的 http server,在 v1.16 中,kubelet 默認僅啟動健康檢查端口 10248 和 kubelet server 的端口 10250
此處總結一下 kubelet 啟動邏輯中的調用關系如下所示:
|--> NewMainKubelet | |--> createAndInitKubelet --|--> BirthCry | | |--> RunKubelet --| |--> StartGarbageCollection | | | |--> startKubelet --> k.Run | NewKubeletCommand --> Run --> run --|--> http.ListenAndServe | |--> daemon.SdNotify
Run
Run
方法是啟動 kubelet 的核心方法,其中會啟動 kubelet 的依賴模塊以及主循環邏輯,該方法的主要邏輯為:
- 1、注冊 logServer;
- 2、判斷是否需要啟動 cloud provider sync manager;
- 3、調用
kl.initializeModules
首先啟動不依賴 container runtime 的一些模塊; - 4、啟動
volume manager
; - 5、執行
kl.syncNodeStatus
定時同步 Node 狀態; - 6、調用
kl.fastStatusUpdateOnce
更新容器運行時啟動時間以及執行首次狀態同步; - 7、判斷是否啟用
NodeLease
機制; - 8、執行
kl.updateRuntimeUp
定時更新 Runtime 狀態; - 9、執行
kl.syncNetworkUtil
定時同步 iptables 規則; - 10、執行
kl.podKiller
定時清理異常 pod,當 pod 沒有被 podworker 正確處理的時候,啟動一個goroutine 負責 kill 掉 pod; - 11、啟動
statusManager
; - 12、啟動
probeManager
; - 13、啟動
runtimeClassManager
; - 14、啟動
pleg
; - 15、調用
kl.syncLoop
監聽 pod 變化;
在 Run
方法中主要調用了兩個方法 kl.initializeModules
和 kl.fastStatusUpdateOnce
來完成啟動前的一些初始化,在初始化完所有的模塊后會啟動主循環
initializeModules
initializeModules
中啟動的模塊是不依賴於 container runtime 的,並且不依賴於尚未初始化的模塊,其主要邏輯為:
- 1、調用
kl.setupDataDirs
創建 kubelet 所需要的文件目錄; - 2、創建 ContainerLogsDir
/var/log/containers
; - 3、啟動
imageManager
,image gc 的功能已經在 RunKubelet 中啟動了,此處主要是監控 image 的變化; - 4、啟動
certificateManager
,負責證書更新; - 5、啟動
oomWatcher
,監聽 oom 並記錄事件; - 6、啟動
resourceAnalyzer
;
fastStatusUpdateOnce
fastStatusUpdateOnce
會不斷嘗試更新 pod CIDR,一旦更新成功會立即執行updateRuntimeUp
和syncNodeStatus
來進行運行時的更新和節點狀態更新。此方法只在 kubelet 啟動時執行一次,目的是為了通過更新 pod CIDR,減少節點達到 ready 狀態的時延,盡可能快的進行 runtime update 和 node status update
updateRuntimeUp
updateRuntimeUp
方法在容器運行時首次啟動過程中初始化運行時依賴的模塊,並在 kubelet 的runtimeState
中更新容器運行時的啟動時間。updateRuntimeUp
方法首先檢查 network 以及 runtime 是否處於 ready 狀態,如果 network 以及 runtime 都處於 ready 狀態,然后調用 initializeRuntimeDependentModules
初始化 runtime 的依賴模塊,包括 cadvisor
、containerManager
、evictionManager
、containerLogManager
、pluginManage
等
initializeRuntimeDependentModules
該方法的主要邏輯為:
- 1、啟動
cadvisor
; - 2、獲取 CgroupStats;
- 3、啟動
containerManager
、evictionManager
、containerLogManager
; - 4、將 CSI Driver 和 Device Manager 注冊到
pluginManager
,然后啟動pluginManager
小結
在 Run
方法中可以看到,會直接調用 kl.syncNodeStatus
和 kl.updateRuntimeUp
,但在 kl.fastStatusUpdateOnce
中也調用了這兩個方法,而在 kl.fastStatusUpdateOnce
中僅執行一次,在 Run
方法中會定期執行。在kl.fastStatusUpdateOnce
中調用的目的就是當 kubelet 首次啟動時盡可能快的進行 runtime update 和 node status update,減少節點達到 ready 狀態的時延。而在 kl.updateRuntimeUp
中調用的初始化 runtime 依賴模塊的方法 kl.initializeRuntimeDependentModules
通過 sync.Once 調用僅僅會被執行一次。
syncLoop
syncLoop
是 kubelet 的主循環方法,它從不同的管道(file,http,apiserver)監聽 pod 的變化,並把它們匯聚起來。當有新的變化發生時,它會調用對應的函數,保證 pod 處於期望的狀態。
syncLoop
中首先定義了一個 syncTicker
和 housekeepingTicker
,即使沒有需要更新的 pod 配置,kubelet 也會定時去做同步和清理 pod 的工作。然后在 for 循環中一直調用 syncLoopIteration
,如果在每次循環過程中出現錯誤時,kubelet 會記錄到 runtimeState
中,遇到錯誤就等待 5 秒中繼續循環
syncLoopIteration
syncLoopIteration
方法會監聽多個 channel,當發現任何一個 channel 有數據就交給 handler 去處理,在 handler 中通過調用 dispatchWork
分發任務。它會從以下幾個 channel 中獲取消息:
- 1、configCh:該信息源由 kubeDeps 對象中的 PodConfig 子模塊提供,該模塊將同時 watch 3 個不同來源的 pod 信息的變化(file,http,apiserver),一旦某個來源的 pod 信息發生了更新(創建/更新/刪除),這個 channel 中就會出現被更新的 pod 信息和更新的具體操作;
- 2、syncCh:定時器,每隔一秒去同步最新保存的 pod 狀態;
- 3、houseKeepingCh:housekeeping 事件的通道,做 pod 清理工作;
- 4、plegCh:該信息源由 kubelet 對象中的 pleg 子模塊提供,該模塊主要用於周期性地向 container runtime 查詢當前所有容器的狀態,如果狀態發生變化,則這個 channel 產生事件;
- 5、liveness Manager:健康檢查模塊發現某個 pod 異常時,kubelet 將根據 pod 的 restartPolicy 自動執行正確的操作;