源码导读系列之 kubelet

kubelet是运行在kubernetes集群中每个节点上的一个代理,管理多种来源的pod,并尽力确保pod中的容器正常运行。kubelet功能繁多,想要大而全的解析kubelet的功能显然得不偿失。本篇文章只做一些重要的常见的kubelet功能的源码导读,以期达到更深入的理解kubelet的目的。

根据kubelet的功能介绍,大致梳理一下分析的大致思路。如kubelet处理pod的创建删除流程,pod的liveness和readness管理,kubelet如何监控pod中的container的状态,如何向api-server更新节点和pod的状态信息,kubelet与cri都有哪些交互等。

个人感觉以源码导读的方式记录阅读代码的成果比源码解析的方式更科学,代码细节可能会变化,主干流程是相对比较稳定的,并且要真正的学习和理解代码,还是需要自己深入到代码中探索和发现的。当发现问题需要处理时,可以更快找到代码的入口点,细读可疑的部分代码,提升处理问题的效率。

kubelet循环

pod调谐.png

从图中可以看到kubelet就是在一个循环中通过主动轮训或者事件通知的方式不断的监测pod,将pod调整到预期的状态。

  • pod的创建来源可以有多种,可以是api-server将一个pod调度到本节点,可以是一个本地的文件目录,也称作mirror pod或static pod(/etc/kubernetes/manifests)。
  • 如果pod配置了liveness,当探针检查失败时,会重启pod。
  • PLEG,如果pod中的container因为某些原因退出了,kubelet可以通过查询和对比新旧pod数据感知到pod的变化,并调整pod到预期状态。
  • 为了分离任务,抽象出了PodWorkers这一层,pod的状态由kubelet通过dispatchWork接口将任务分配到PodWorkers中去处理。

kubelet创建pod

创建pod流程图.png

当kubelet接受到新建pod的请求后,开始做一系列的处理,涉及到kubelet本身,podWorker,kubeGenericRuntimeManager等多个组件,最后调用CRI真正的去启动pod中的容器。这里要梳理清楚pod所需的资源是哪些组件负责的,如拉取镜像是由CRI完成的,sandbox相关的网络是由CRI调用CNI完成的,cgroup目录是由kubelet本身完成的等等。
其中SyncPod是pod状态同步中一个比较重要的函数,创建删除更新等都会调用到这个函数,为方便理解,放上代码中的注释,注释中的流程说明浅显易懂且清晰。

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,准确的翻译应该是Pod生命周期事件生成器。我把它叫做Pod生命周期管理,因为它是Pod的生命周期管理中的重要的一部分,是产生Pod变更事件的来源。

kubelet的pleg模块不断的检查Pod中的sandbox和container的状态变化,如果有变化,则产生事件,kubelet读取事件,并将pod状态调整到期望的状态。例如Pod中的container的1号进程因为内存超限触发oom导致容器退出,pleg通过对比新旧pod数据,生成一个事件,kubelet感知到容器退出就可以重启失败的容器。这里要注意区分被oom的进程是容器的1号进程还是容器内的普通进程,1号进程很重要,可以清理僵尸进程,可以实现容器内子进程的优雅退出等等,专门有一个tini项目来实现容器中的1号进程,新版本的docker中也增加了参数--init来启动专门的1号进程

检查的时间间隔g.relistPeriod是可以配置的,默认值是1s。

kubelet中的那些manager

kubelet中有很多的manager,这些manager根据名字就可以判断出它的作用,这些功能独立的manager相互协作,穿插在pod的生命周期中,实现了kubelet的完整功能。或者说podSpec中有很多的字段,这些manager就是处理这些podSpec中的字段的。例如statusManager负责缓存pod的状态并更新到apiserver,evictionManager负责pod的驱逐,imageManager负责gc,通过调用CRI的image接口删除不再使用的镜像等等。

了解了这些manager,在看kubelet的代码时,就可以快速的把握住主线,不会迷失在纷繁复杂的代码中。如果关注configmap在创建pod时如何生效,就可以重点看下configMapManager。

这些manager运行的套路基本都是一致的,先是通过包装的New()函数初始化一个Manager对象,在调用manager的Start或者Run方法开始做具体的工作。

manager.png

下面代码中也是一些重要的函数入口,通过记录这些入口函数,也可以很快速的找到具体功能的代码片段。如查看node的update更新节点状态,就可以查看syncNodeStatus函数。

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数据结构与CRI接口之间的一个桥梁。如创建pod会调用到klet.syncPod。

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

metrics

云原生组件一般都提供了metrics接口来获取服务的一些细节的状态,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}
    相关代码入口如下:

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")

重要的数据结构

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 ...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容