源碼導(dǎo)讀系列之 kubelet

kubelet是運行在kubernetes集群中每個節(jié)點上的一個代理,管理多種來源的pod,并盡力確保pod中的容器正常運行。kubelet功能繁多,想要大而全的解析kubelet的功能顯然得不償失。本篇文章只做一些重要的常見的kubelet功能的源碼導(dǎo)讀,以期達到更深入的理解kubelet的目的。

根據(jù)kubelet的功能介紹,大致梳理一下分析的大致思路。如kubelet處理pod的創(chuàng)建刪除流程,pod的liveness和readness管理,kubelet如何監(jiān)控pod中的container的狀態(tài),如何向api-server更新節(jié)點和pod的狀態(tài)信息,kubelet與cri都有哪些交互等。

個人感覺以源碼導(dǎo)讀的方式記錄閱讀代碼的成果比源碼解析的方式更科學(xué),代碼細節(jié)可能會變化,主干流程是相對比較穩(wěn)定的,并且要真正的學(xué)習(xí)和理解代碼,還是需要自己深入到代碼中探索和發(fā)現(xiàn)的。當(dāng)發(fā)現(xiàn)問題需要處理時,可以更快找到代碼的入口點,細讀可疑的部分代碼,提升處理問題的效率。

kubelet循環(huán)

pod調(diào)諧.png

從圖中可以看到kubelet就是在一個循環(huán)中通過主動輪訓(xùn)或者事件通知的方式不斷的監(jiān)測pod,將pod調(diào)整到預(yù)期的狀態(tài)。

  • pod的創(chuàng)建來源可以有多種,可以是api-server將一個pod調(diào)度到本節(jié)點,可以是一個本地的文件目錄,也稱作mirror pod或static pod(/etc/kubernetes/manifests)。
  • 如果pod配置了liveness,當(dāng)探針檢查失敗時,會重啟pod。
  • PLEG,如果pod中的container因為某些原因退出了,kubelet可以通過查詢和對比新舊pod數(shù)據(jù)感知到pod的變化,并調(diào)整pod到預(yù)期狀態(tài)。
  • 為了分離任務(wù),抽象出了PodWorkers這一層,pod的狀態(tài)由kubelet通過dispatchWork接口將任務(wù)分配到PodWorkers中去處理。

kubelet創(chuàng)建pod

創(chuàng)建pod流程圖.png

當(dāng)kubelet接受到新建pod的請求后,開始做一系列的處理,涉及到kubelet本身,podWorker,kubeGenericRuntimeManager等多個組件,最后調(diào)用CRI真正的去啟動pod中的容器。這里要梳理清楚pod所需的資源是哪些組件負責(zé)的,如拉取鏡像是由CRI完成的,sandbox相關(guān)的網(wǎng)絡(luò)是由CRI調(diào)用CNI完成的,cgroup目錄是由kubelet本身完成的等等。
其中SyncPod是pod狀態(tài)同步中一個比較重要的函數(shù),創(chuàng)建刪除更新等都會調(diào)用到這個函數(shù),為方便理解,放上代碼中的注釋,注釋中的流程說明淺顯易懂且清晰。

pkg/kubelet/kuberuntime/kuberuntime_manager.go
// SyncPod syncs the running pod into the desired pod by executing following steps:
//
//  1. Compute sandbox and container changes.
//  2. Kill pod sandbox if necessary.
//  3. Kill any containers that should not be running.
//  4. Create sandbox if necessary.
//  5. Create ephemeral containers.
//  6. Create init containers.
//  7. Create normal containers.
func (m *kubeGenericRuntimeManager) SyncPod(pod *v1.Pod, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, backOff *flowcontrol.Backoff) (result kubecontainer.PodSyncResult) {
}

PLEG

pod生命周期管理.png

PLEG全稱Pod lifecycle event generator,準(zhǔn)確的翻譯應(yīng)該是Pod生命周期事件生成器。我把它叫做Pod生命周期管理,因為它是Pod的生命周期管理中的重要的一部分,是產(chǎn)生Pod變更事件的來源。

kubelet的pleg模塊不斷的檢查Pod中的sandbox和container的狀態(tài)變化,如果有變化,則產(chǎn)生事件,kubelet讀取事件,并將pod狀態(tài)調(diào)整到期望的狀態(tài)。例如Pod中的container的1號進程因為內(nèi)存超限觸發(fā)oom導(dǎo)致容器退出,pleg通過對比新舊pod數(shù)據(jù),生成一個事件,kubelet感知到容器退出就可以重啟失敗的容器。這里要注意區(qū)分被oom的進程是容器的1號進程還是容器內(nèi)的普通進程,1號進程很重要,可以清理僵尸進程,可以實現(xiàn)容器內(nèi)子進程的優(yōu)雅退出等等,專門有一個tini項目來實現(xiàn)容器中的1號進程,新版本的docker中也增加了參數(shù)--init來啟動專門的1號進程

檢查的時間間隔g.relistPeriod是可以配置的,默認值是1s。

kubelet中的那些manager

kubelet中有很多的manager,這些manager根據(jù)名字就可以判斷出它的作用,這些功能獨立的manager相互協(xié)作,穿插在pod的生命周期中,實現(xiàn)了kubelet的完整功能。或者說podSpec中有很多的字段,這些manager就是處理這些podSpec中的字段的。例如statusManager負責(zé)緩存pod的狀態(tài)并更新到apiserver,evictionManager負責(zé)pod的驅(qū)逐,imageManager負責(zé)gc,通過調(diào)用CRI的image接口刪除不再使用的鏡像等等。

了解了這些manager,在看kubelet的代碼時,就可以快速的把握住主線,不會迷失在紛繁復(fù)雜的代碼中。如果關(guān)注configmap在創(chuàng)建pod時如何生效,就可以重點看下configMapManager。

這些manager運行的套路基本都是一致的,先是通過包裝的New()函數(shù)初始化一個Manager對象,在調(diào)用manager的Start或者Run方法開始做具體的工作。

manager.png

下面代碼中也是一些重要的函數(shù)入口,通過記錄這些入口函數(shù),也可以很快速的找到具體功能的代碼片段。如查看node的update更新節(jié)點狀態(tài),就可以查看syncNodeStatus函數(shù)。

kl.imageManager.Start()
go kl.volumeManager.Run(kl.sourcesReady, wait.NeverStop)
kl.oomWatcher.Start(kl.nodeRef)
kl.cadvisor.Start()
kubeInformers.Start(wait.NeverStop)
kl.containerLogManager.Start()
kl.pleg.Start()
kl.runtimeClassManager.Start(wait.NeverStop)
kl.statusManager.Start()   
kl.containerManager.Start(node, kl.GetActivePods, kl.sourcesReady, kl.statusManager, kl.runtimeService)
kl.evictionManager.Start(kl.StatsProvider, kl.GetActivePods, kl.podResourcesAreReclaimed, evictionMonitoringPeriod)
go kl.pluginManager.Run(kl.sourcesReady, wait.NeverStop)
go wait.Until(kl.syncNodeStatus, kl.nodeStatusUpdateFrequency, wait.NeverStop)  ->  defaultNodeStatusFuncs
go kl.fastStatusUpdateOnce()
go kl.nodeLeaseController.Run(wait.NeverStop)
go wait.Until(kl.updateRuntimeUp, 5*time.Second, wait.NeverStop)
go k.Run(podCfg.Updates())  -> syncLoop -> syncLoopIteration

podWorkers

podWokers對象可以看做是kubelet的pod數(shù)據(jù)結(jié)構(gòu)與CRI接口之間的一個橋梁。如創(chuàng)建pod會調(diào)用到klet.syncPod。

    klet.podWorkers = newPodWorkers(
        klet.syncPod,
        klet.syncTerminatingPod,
        klet.syncTerminatedPod,
        kubeDeps.Recorder,
        klet.workQueue,
        klet.resyncInterval,
        backOffPeriod,
        klet.podCache,
    )

metrics

云原生組件一般都提供了metrics接口來獲取服務(wù)的一些細節(jié)的狀態(tài),kubelet除了提供metrics可供prometheus采集外,還提供了stats接口來獲取運行時信息,包括pod,container等。
訪問10250端口 需要證書,10255端口 不需要證書
另外kubelet還開放了其他幾個有意義的http接口

  • /stats/
  • /stats/summary
  • /stats/container
  • 127.0.0.1:10255/pods
  • /{podName}/{containerName}
  • /{namespace}/{podName}/{uid}/{containerName}
    相關(guān)代碼入口如下:

func (s *Server) InstallDefaultHandlers(enableCAdvisorJSONEndpoints bool) {
s.addMetricsBucketMatcher("healthz")
healthz.InstallHandler(s.restfulCont,
healthz.PingHealthz,
healthz.LogHealthz,
healthz.NamedCheck("syncloop", s.syncLoopHealthCheck),
)

s.addMetricsBucketMatcher("pods")
ws := new(restful.WebService)
ws.
    Path("/pods").
    Produces(restful.MIME_JSON)
ws.Route(ws.GET("").
    To(s.getPods).
    Operation("getPods"))
s.restfulCont.Add(ws)

s.addMetricsBucketMatcher("stats")
s.restfulCont.Add(stats.CreateHandlers(statsPath, s.host, s.resourceAnalyzer, enableCAdvisorJSONEndpoints))

s.addMetricsBucketMatcher("metrics")
s.addMetricsBucketMatcher("metrics/cadvisor")
s.addMetricsBucketMatcher("metrics/probes")
s.addMetricsBucketMatcher("metrics/resource/v1alpha1")

重要的數(shù)據(jù)結(jié)構(gòu)

pkg/apis/core/types.go
type Pod struct {
}
type Node struct {
}

pkg/kubelet/kubelet.go
type Kubelet struct {
}

type SyncHanler interface {
    HandlePodAdditions(pods []*v1.Pod)
    HandlePodUpdates(pods []*v1.Pod)
    HandlePodRemoves(pods []*v1.Pod)
    HandlePodReconcile(pods []*v1.Pod)
    HandlePodSyncs(pods []*v1.Pod)
    HandlePodCleanups() error
}

pkg/kubelet/pod_workers.go
type podWorkers struct {
}

pkg/kubelet/kuberuntime/kuberuntime_manager.go
func (m *kubeGenericRuntimeManager) SyncPod(pod *v1.Pod, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, backOff *flowcontrol.Backoff) (result kubecontainer.PodSyncResult) {
}

type kubeGenericRuntimeManager struct {
}

pkg/kubelet/kubelet.go
makePodSourceConfig static_pod, api-server ...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容