源碼解析:一文讀懂 Kubelet

本文主要介紹 kubelet 功能、核心組件,以及啓動流程的源碼分析,總結了 kubelet 的工作原理。

kubelet 簡介

Kubernetes 的架構圖

從官方的架構圖中很容易就能找到 kubelet

執行 kubelet -h 看到 kubelet 的功能介紹:

•kubelet 是每個 Node 節點上都運行的主要 “節點代理”。使用如下的一個向 apiserver 註冊 Node 節點:主機的 hostname;覆蓋 host 的參數;或者雲提供商指定的邏輯。•kubelet 基於 PodSpec 工作。PodSpec 是用 YAML 或者 JSON 對象來描述 Pod。Kubelet 接受通過各種機制(主要是 apiserver)提供的一組 PodSpec,並確保裏面描述的容器良好運行。

除了由 apiserver 提供 PodSpec,還可以通過以下方式提供:

• 文件 •HTTP 端點 •HTTP 服務器

kubelet 功能歸納一下就是上報 Node 節點信息,和管理(創建、銷燬)Pod。功能看似簡單,實際不然。每一個點拿出來都需要很大的篇幅來講,比如 Node 節點的計算資源,除了傳統的 CPU、內存、硬盤,還提供擴展來支持類似 GPU 等資源;Pod 不僅僅有容器,還有相關的網絡、安全策略等。

kubelet 架構

2021-06-14-21-55-08

重要組件

kubelet 的架構由 N 多的組件組成,下面簡單介紹下比較重要的幾個:

PLEG

即 Pod Lifecycle Event Generator,字面意思 Pod 生命週期事件(ContainerStartedContainerDiedContainerRemovedContainerChanged)生成器。

其維護着 Pod 緩存;定期通過 ContainerRuntime 獲取 Pod 的信息,與緩存中的信息比較,生成如上的事件;將事件寫入其維護的通道(channel)中。

PodWorkers

處理事件中 Pod 的同步。核心方法 managePodLoop() 間接調用 kubelet.syncPod() 完成 Pod 的同步:

• 如果 Pod 正在被創建,記錄其延遲 • 生成 Pod 的 API Status,即 v1.PodStatus:從運行時的 status 轉換成 api status• 記錄 Pod 從 pending 到 running 的耗時 • 在 StatusManager 中更新 pod 的狀態 • 殺掉不應該運行的 Pod• 如果網絡插件未就緒,只啓動使用了主機網絡(host network)的 Pod• 如果 static pod 不存在,爲其創建鏡像(Mirror)Pod• 爲 Pod 創建文件系統目錄:Pod 目錄、卷目錄、插件目錄 • 使用 VolumeManager 爲 Pod 掛載卷 • 獲取 image pull secrets• 調用容器運行時(container runtime)的 #SyncPod() 方法

PodManager

存儲 Pod 的期望狀態,kubelet 服務的不同渠道的 Pod

StatsProvider

提供節點和容器的統計信息,有 cAdvisor 和 CRI 兩種實現。

ContainerRuntime

顧名思義,容器運行時。與遵循 CRI 規範的高級容器運行時進行交互。

Deps.PodConfig

PodConfig 是一個配置多路複用器,它將許多 Pod 配置源合併成一個單一的一致結構,然後按順序向監聽器傳遞增量變更通知。

配置源有:文件、apiserver、HTTP

#syncLoop

接收來自 PodConfig 的 Pod 變更通知、定時任務、PLEG 的事件,以及 ProbeManager 的事件,將 Pod 同步到期望狀態

PodAdmitHandlers

Pod admission 過程中調用的一系列處理器,比如 eviction handler(節點內存有壓力時,不會驅逐 QoS 設置爲 BestEffort 的 Pod)、shutdown admit handler(當節點關閉時,不處理 pod 的同步操作)等。

OOMWatcher

從系統日誌中獲取容器的 OOM 日誌,將其封裝成事件並記錄。

VolumeManger

VolumeManager 運行一組異步循環,根據在此節點上調度的 pod 確定需要附加 / 掛載 / 卸載 / 分離哪些卷並執行操作。

CertificateManager

處理證書輪換。

ProbeManager

實際上包含了三種 Probe,提供 probe 結果緩存和通道。

•LivenessManager•ReadinessManager•StartupManager

EvictionManager

監控 Node 節點的資源佔用情況,根據驅逐規則驅逐 Pod 釋放資源,緩解節點的壓力。

PluginManager

PluginManager 運行一組異步循環,根據此節點確定哪些插件需要註冊 / 取消註冊並執行。如 CSI 驅動和設備管理器插件(Device Plugin)。

CSI

Container Storage Interface,由存儲廠商實現的存儲驅動。

設備管理器插件(Device Plugin)

Kubernetes 提供了一個 設備插件框架,你可以用它來將系統硬件資源發佈到 Kubelet。

供應商可以實現設備插件,由你手動部署或作爲 DaemonSet 來部署,而不必定製 Kubernetes 本身的代碼。目標設備包括 GPU、高性能 NIC、FPGA、 InfiniBand 適配器以及其他類似的、可能需要特定於供應商的初始化和設置的計算資源。

kubelet 的啓動流程

要分析 kubelet 的啓動流程,可以從 kubelet 運行方式着手。找一個 Node 節點,很容易就能找到 kubelet 的進程。由於其是以 systemd 的方式啓動,也可以通過 systemctl 查看其狀態。

kubelet 啓動命令

kubelet 的啓動命令(minikube 環境)

$ ps -aux | grep '/kubelet' | grep -v grep
root        4917  2.6  0.3 1857652 106152 ?      Ssl  01:34  13:05 /var/lib/minikube/binaries/v1.21.0/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --config=/var/lib/kubelet/config.yaml --container-runtime=docker --hostname-override=1.21.0 --kubeconfig=/etc/kubernetes/kubelet.conf --node-ip=192.168.64.5

或者

$ systemctl status kubelet.service
● kubelet.service - kubelet: The Kubernetes Node Agent
     Loaded: loaded (/usr/lib/systemd/system/kubelet.service; disabled; vendor preset: enabled)
    Drop-In: /etc/systemd/system/kubelet.service.d
             └─10-kubeadm.conf
     Active: active (running) since Sun 2021-06-13 01:34:42 UTC; 11h ago
       Docs: http://kubernetes.io/docs/
   Main PID: 4917 (kubelet)
      Tasks: 15 (limit: 38314)
     Memory: 39.4M
     CGroup: /system.slice/kubelet.service
             └─4917 /var/lib/minikube/binaries/v1.21.0/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --config=/var/lib/kubelet/config.yaml --container-runtime=docker --hostname-override=1.21.0 --kubeconfig=/etc/kubernetes/kubelet.conf --node-ip=192.168.64

源碼分析

從 git@github.com:kubernetes/kubernetes.git 倉庫獲取代碼,使用最新的 release-1.21 分支。

cmd/kubelet/kubelet.go:35 的 main 方法爲程序入口。• 調用 NewKubeletCommand 方法,創建 command• 執行 command•cmd/kubelet/app/server.go:434 的 Run 方法。• 調用 RunKubelet 方法。• 調用 createAndInitKubelet 方法,創建並初始化 kubelet•pkg/kubelet/kubelet.go 的 NewMainKubelet 方法,創建 kubelet 的 各種組件。共十幾個組件,見 kubelet 的構架。• 調用 BirtyCry 方法:放出 Starting 事件 • 調用 StartGarbageCollection 方法,開啓 ContainerGC 和 ImageGC• 調用 startKubelet 方法(大量使用 goroutine 和通道)•goroutine:kubelet.Run()• 初始化模塊 •metrics 相關 • 創建文件系統目錄目錄 • 創建容器日誌目錄 • 啓動 ImageGCManager• 啓動 ServerCertificateManager• 啓動 OOMWatcher• 啓動 ResourceAnalyzer•goroutine:VolumeManager.Run() 開始處理 Pod Volume 的卸載和掛載 •goroutine:狀態更新 fastStatusUpdateOnce() (更新 Pod CIDR -> 更新 ContainerRuntime 狀態 -> 更新 Node 節點狀態)•goroutine: NodeLeaseController.Run() 更新節點租約 •goroutine:podKiller.PerformPodKillingWork 殺掉未被正確處理的 pod•StatusManager.Start() 開始向 apiserver 更新 Pod 狀態 •RuntimeClassManager.Start()PLEG.Start():持續從 ContainerRuntime 獲取 Pod / 容器的狀態,並與 kubelet 本地 cache 中的比較,生成對應的 EventsyncLoop() 重點,持續監控並處理來自文件、apiserver、http 的變更。包括 Pod 的增加、更新、優雅刪除、非優雅刪除、調和。• 啓動 server,暴露 /healthz 端點 • 通知 systemd kuberlet 服務已經啓動

kubelet 的工作原理

  1. 來靜態文件、apiserver 以及 HTTP 請求的 Pod 配置變更,被髮送到 kubelet.syncLoop2.PLEG 會定期通過容器運行時獲取節點上 Pod 的狀態,與其緩存中的 Pod 信息進行比較,封裝成事件,進入 PLEG 的通道 3. 定期檢查工作隊列中的 Pod4.ProbeManager 的通道中的 Pod5. 以上 1~4,都會進入 syncLoopIteration,並從對應的通道中獲取到對應 Pod,將 Pod 的信息保存到 PodManager;然後分發給 PodWorker,完成一些列的同步工作。

總結

kubelet 啓動流量就講到這裏,雖然複雜,還是有跡可循。只要瞭解了 kubelet 在 Kubernetes 中的定位及角色,就很容易理解其工作流量。

後面會再深入分析 Pod 創建及啓動流程。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/O7k3MlgyonNtOUxNPrN8lg