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 自动执行正确的操作;