kubelet
目录:
kubelet介绍
kubelet用于处理apiserver下发的任务,并且上报节点上的pod状态
- pod管理 从apiserer获取期望状态进行变更
- 容器健康检测 容器运行出错根据重启策略处理
- 容器监控 包括节点资源使用等
端口有
- 10250端口 kubeletAPI端口,可以获取node状态,用于与kube-apiserver交互
- 10248端口 健康检测端口
- 4194端口 cAdvisor,节点环境,运行pod状态和资源使用情况
- 10255端口 只读的端口
$ curl http://127.0.0.1:10255/pods
$ curl http://127.0.0.1:10255/spec
包含模块
- PLEG,调用container runtime获取节点的pod信息,和pod的cache对比,生成PodlifecycleEvent防入eventChannel,syncLoop进行消费
- cAdvisor是开源监控工具,被集成到kubelet,收集节点和容器的监控信息,对外提供了interface,被imageManager,OOMWatcher和containerManager使用
- OOMWatcher通过Watch的方式从cAdvisor获取OOM信号,产生事件
- probeManager依赖statusManager,livenessManager和containerRefManager监控pod的容器健康情况,支持livenessProbe和readinessProbe探针,livenessProbe探测失败会执行对应的重启策略,readinessProbe探测成功会将容器加入到对一个的svc和endpoint
- statusManager用于维护和存储状态信息,用于其他模块调用
- containerRefManager
- evictionManager获取资源信息,不足时触发驱逐策略,
--eviction-hard
设置 - imageGC负责镜像回收,
--image-gc-high-threshold
和--image-gc-low-threshold
设置 - containerGC清理已经Exited的container
- imageManager调用kubecontainer管理镜像相关
- volumeManager负责volume和pod的生命周期
- containerManager管理container的cgroup配置,
--cgroups-per-qos
设置 - runtimeManager负责与CRI对接,默认是dockcer
- podManager存储和访问pod信息,会被其他模块调用
- secretManager
- configMapManager
kubelet启动流程
调用NewKubeletCommand获取配置
- 创建不允许修改的配置集KubeletFlag和可动态配置的KubeletConfiguration
- 解析命令行参数
- 初始化默认配置
- 加载配置loadConfigFile
- 校验配置ValidateKubeletConfiguration
- 检查是否启用动态配置
- 初始化kubeletDeps
- 调用Run方法
Run(kubeletServer, kubeletDeps, stopCh)
Run方法调用run启动,最开始也是检查lock,配置文件注册到configz,检查是否为standalone的调试模式,设置OOM分数为-999
- RunKubelet启动
- http.ListenAndServe启动对应的healthz接口
- daemon.SdNotify向systemd发信号
RunKubelet使用createAndInitKubelet进行kubelet组件的初始化,然后调用startKubelet启动kubelet的组件
- createAndInitKubelet包括初始化PodConfig,和其他各种Manager和Watcher等模块
- startKubelet进行启动所有模块k.Run
createAndInitKubelet初始化模块的流程
- kubelet.NewMainKubelet实例化kubelet对象,对依赖的模块初始化
- k.BirthCry向apiserver发送kubelet启动的event
- k.StartGarbageCollection启动containers和images的回收
kubelet.NewMainKubelet就会初始化绝大多数包含的模块
startKubelet调用的k.Run启动所有模块和主要的循环逻辑
- 注册logServer
- 启动各种模块
- 启动kl.syncLoop监听Pod变化
kubelet创建pod流程
kubelet中定义了syncLoop,syncLoop包含syncTicker和housekeepingTicker
- syncTicker 每秒检测一次是否有需要同步的pod
- housekeepingTicker 每2秒检测一次是否有需要清理的pod
然后执行syncLoopIteration对多个Channel遍历,有消息就放到对应的handler处理,Channel包括
- configCh pod的更新情况,由kubeDeps的PodConfig提供,包括ADD,UPDATE,REMOVE,RECONCILE,DELETE,RESTORE和SET等,watch的有file(
/etc/kubernetes/manifests/
下配置的静态Pod),http(--manifest-url
配置的静态Pod)和apiserver - syncCh 定时器管道,每秒获取Pod的新状态
- houseKeepingCh housekeeping管道,需要清理的Pod
- plegCh 向container runtime查询容器状态,如果状态变更,产生事件
- livenessManager.Updates() 发现pod不可用,会根据restartPolicy执行对应操作
对于新增的Pod,调用的HandlePodAdditions方法,会根据Pod创建日期排序,按照创建时间创建,将Pod加入到podManager,podManager管理机器上的所有Pod信息,如果podManager没有对应数据则认为Pod被删除
- podManager先会判断是否为
mirror pod
,mirror pod
需要单独调用方法handleMirrorPod启动(静态Pod不会受apiserver管理也没有rs关联) - 检验Pod是否能在节点运行,例如磁盘空间,如果不可以就进行拒绝
- dispatchWork把创建pod的任务下发到podWorkers异步处理
- 在probeManager中添加pod
- 如果有readiness和liveness,启动goroutine定时检测
dispatchWork下发的任务是将任务转为UpdatePodOptions,调用podWorkers.UpdatePod()
podWorkers收到更新事件之后,将事件发送更新管道
- 获取pod是否有对应的channle
- 如果没有
p.podUpdates[uid]
(channel),创建单独的channel,goroutine启动managePodLoop接受channel的事件,channel存在则pass - 如果没有
p.isWorking[pod.UID]
,podWorkers向channel发送事件,如果有,看是否未送达到sync,写入lastUndeliveredWorkUpdate
managePodLoop调用syncPodFn(kubelet.SyncPod),然后调用wrapUp函数
- pod信息插入kubelet的workQueue队列,等待下一个周期对这个pod信息进行同步
- 将sync期间堆积的update操作加入到channel中立刻处理
syncPod完成容器前的准备工作
- 如果是SyncPodKill删除直接返回
- 检查pod能否在本节点运行canRunPod,包括网络模式,privileged等
- 如果pod为非running状态就kill
- 同步podStatus到kubelet.statusManager
- 如果配置了
--cgroups-per-qos
会进行限制 - 创建containerManagar对象,并创建
pod level cgroup
,更新Qos level cgroup
- StaticPod直接创建
- 创建pod的数据目录和挂载等信息,如果涉及到pv交由volumeManager完成,并等待其完成
- 调用containerRuntime的SyncPod创建
pkg/kubelet/kuberuntime
中的SyncPod完成pod的创建
- computePodActions检测sandbox和Container是否变化
- 创建sandbox容器createPodSandbox,根据网络模式加载网络配置(CNI),添加端口映射
- 启动init容器,获取podContainerChanges.NextInitContainerToStart中的容器调用startContainer启动容器,指定参数为kubecontainer.ContainerTypeInit
- 启动业务容器,获取podContainerChanges.ContainersToStart中的容器调用startContainer启动容器,指定参数为kubecontainer.ContainerTypeRegular
启动容器startContainer
- 拉取镜像
- 设置pod重启定时器,FindContainerStatusByName
- 生成业务容器配置generateContainerConfig
- 执行Lifecycle.PreStartContainer
- 调用CRI创建容器并启动CreateContainer调用runtimeService.StartContainer
- 执行Lifecycle.PostStart
kubelet状态上报
kube-apiserver根据kubelet的心跳上报(也包括自身数据信息)判断是否正常,长时间没有上报则认为NotReady,其上容器也会变为Nodelost或Unknown,上报频率由--node-status-update-frequency
参数配置,默认是10s
kubelet心跳上报的数据包括
- Addresses HostName,ExternalIP和InternalIP
- Condition Node的运行状态
- Capacity 节点上的CPU,内存,可调度到node上的最大pod数
- Info 包括内核,系统,kubelet,docker版本
当Node处于非Ready状态超过pod-eviction-timeout
配置(默认5min),kube-controller-manager会将Pod设置为Terminating或Unknown,直到node被master删除或者node重新Ready。
上报有两种方式
- NodeStatus
- NodeLease(v1.13后支持,节点变化或者超过node-monitor-grace-period上报)
数据使用defaultNodeStatusFuncs生成
kubelet事件处理机制
Kubernetes的所有组件都会将event上报到apiserver
组件使用的都是EventRecorder,kubelet也不例外
Event包括一些字段
- Type
- Reason
- Age
- From
- Message
- EventTime
EventBroadcaster通过makeEventRecorder方法初始化
- 初始化Broadcaster,Broadcaster会将EventRecorder发送来的events进行广播,在其上注册的watcher会获取到event
- 启动一个StartEventWatcher
- StartLogging使用StartEventWatcher创建的Watcher,注册到Broadcaster,将event写入日志
- StartRecordingToSink使用StartEventWatcher创建的Watcher,注册到Broadcaster,将event发往APIServer,写入etcd,供kubectl或其他client使用
其他模块通过是EventRecorder生成event,发往Broadcaster接收队列
kubelet的statusManager
statusManager是用于pod状态同步到apiserver
NewManager初始化statusManager,包括
- kubeClient与apiserver交互
- podManager负责内存中pod信息维护
- podStatuses是cache
- podStatusesChannel是其他组件修改pod状态时,将pod状态发送到channel
- apiStatusVersions维护pod的status版本号,每次更新+1
- podDeletionSafety删除pod的接口
k8s.io/kubernetes/pkg/kubelet/status的Manager的interface包括
- PodStatusProvider其他模块获取podstatus
- SetPodStatus设置pod的状态并触发状态同步操作
- SetContainerReadiness设置.status.containerStatuses为ready并触发状态同步操作
- SetContainerStartup设置.status.containerStatuses为start并触发状态同步操作
- TerminatePod设置.status.containerStatuses和.status.initContainerStatuses为Terminated并触发状态同步操作
- RemoveOrphanedStatuses
- start
start方法
- 启动定时器syncTicker,时长syncPeriod默认为10s
- 监听podStatusChannel,如果有pod变化使用syncPod同步,定时器触发使用syncBatch同步
syncPod
- 判断apiStatusVersions是否小于status的版本号
- 从apiserver获取pod的oldStatus
- 对比oldStatus和currentStatus是否相同,并不相同则是重建过
- 同步最新的状态到apiserver
- 判断pod是否处于canBeDeleted,如果在就在apiserver删除并删除cache
syncBatch
- 调用GetUIDTranslations从podManager获取pod和uid的关系
- 遍历apiStatusVersions的pod是否在podStatuses,不在就删除
- 遍历podStatuses,needsUpdate或needsReconcile过的就放到更新列表updatedStatuses
- 遍历updatedStatuses调用syncPod
SetPodStatus可以设置的状态仅有ContainersReady,Initialized,Ready,PodScheduled和Unschedulable,更新状态并写入podStatusChannel
pod的删除机制
apiserver
- 删除pod的时候,kube-apiserver只是将pod的DeletionTimestamp和DeletionGracePeriodSeconds字段改变
kubelet
- 检测到kubetypes.UPDATE,使用HandlePodUpdates方法的dispatchWork
- dispatchWork的podIsTerminated方法判断,目前还不是terminated状态
- syncPod的时候有DeletionTimestamp执行killPod
- killPod调用的killPodWithSyncResult,包括停掉container的killContainer,和再停掉sandbox的StopPodSandbox
- HandlePodCleanups对container处于crash的进行清理
- syncPod的canBeDeleted确认容器关闭,volume卸载,cgroup清理等,在同步到apiserver
kubelet的Qos机制
QoS分三个级别
- Guaranteed pod的requests和limits设定的值相等
- Burstable pod的requests小于limits,limits值不为0
- BestEffort pod的requests和limit都为0
Qos的表现形式
- 在调度的时候,调度器只会根据request值进行调度
- 当OOM的时候,会kill掉OOM值低的进程,Guaranteed为-998,Burstable为2~999,BestEffort为1000,就是先kill掉BestEffort进程
- cgroup配置不同,Guaranteed位于RootCgroup/kubepods,Burstable位于RootCgroup/kubepods/burstable,BestEffort位于ootCgroup/kubepods/BestEffort
kubelet只支持cpu,mem,pid和hugetlb,主要参数
--cgroups-per-qos
启用后为每个Pod和Qos创建Cgroup树,默认开启--cgroup-root
指定root cgroup,默认使用root cgroup--cpu-manager-policy
开启支持Guaranteed的Pod进行CPU--kube-reserved
kubelet等组件预留资源--kube-reserved-cgroup
kubelet等组件cgroup dir--system-reserved
非kubelet等组件--system-reserved-cgroup
kubelet启动后会在root cgroup下创建kubepods的cgroup,将本机可用资源写入到kubepods下对应文件,例如kubepods/cpu.share,然后创建burstable和besteffort的cgroup
kubelet在接收到pod,会基于pod申请的资源情况和对应的Qos进行处理
- 判断pod属于哪种Qos
- 根据pod信息计算Qos的cgroup值
- 更新kubepods中的信息
创建的路径为ROOT/kubepods/burstable/pod<UID>/container<UID>
示例
/sys/fs/cgroup/cpu/kubepods/burstable/pod<UID>/container<UID>/cpu.shares = 256
/sys/fs/cgroup/cpu/kubepods/burstable/pod<UID>/container<UID>/cpu.cfs_quota_us = 50000
/sys/fs/cgroup/memory/kubepods/burstable/pod<UID>/container<UID>/memory.limit_in_bytes = 104857600
guaranteed因为有明确的资源限制,而burstable和besteffort没有,需要一个cgroup来进行统一管理
所以--qos-reserved
的配置,就是对于预留的资源,例如机器的可用资源为8G,配置的memory=100%,创建一个1G的guaranteed的Pod,burstable和besteffort的memory.limit_in_bytes
都会变为7G,再创建一个2G的,就变为5G
containerManager初始化的时候会初始化qosContainerManager(NewQOSContainerManager方法),cm.setupNode启动流程
- 检查依赖的内核参数
- 如果CgroupsPerQOS为true,调用cm.createNodeAllocatableCgroups创建rootcgroup,然后调用cm.qosContainerManager.Start启动osContainerManager
- cm.enforceNodeAllocatableCgroups计算node的可用(allocatable)资源配置到rootcgroup,然后根据配置的SystemReserved和KubeReserved配置cgroup
- 为系统组件配置cgroup资源限制
- 为系统进程配置
oom_score_adj
cm.qosContainerManager.Start的流程
- 检查rootcgroup是否存在
- 为Burstable和BestEffort创建cgroup
- m.UpdateCgroups每分钟定时更新cgroup
m.UpdateCgroups的流程
- 调用m.setCPUCgroupConfig计算node上的activePods来更新bestEffor和burstable的cgroup的cpu.shares
- 调用m.setHugePagesConfig更新huge pages
- 检查是
--qos-reserved
参数是否启用,如果启用调用m.setMemoryReserve计算每个Qos class中需要设定的值然后调用m.cgroupManager.Update
更新cgroup中的值 - m.cgroupManager.Update更新cgroup
PodCgroup是在创建pod的时候,syncPod方法会调用kl.containerManager.UpdateQOSCgroups更新qos的cgroup,pcm.EnsureExists创建Pod的Cgroup。EnsureExists的主要逻辑是判断cgroup是否存在,不存在则使用m.cgroupManager.Create创建
ContainerCgroup通过runtime进行创建
kubelet资源回收机制
资源回收分两种
- kube-controller-manager的gc回收kubernetes对象
- 节点上回收容器和镜像
容器回收相关
--maximum-dead-containers-per-container
一个pod最多可以保存多个已经停止的容器,默认为1--maximum-dead-containers
node上保留最多的容器数,默认-1没有限制--minimum-container-ttl-duration
退出容器存活时间,默认0s
镜像回收相关
--image-gc-high-threshold
磁盘达到多少开始回收镜像,默认85%--image-gc-low-threshold
磁盘减少到多少停止回收镜像,默认80%--minimum-image-ttl-duration
未使用镜像保留时间,默认2m
容器回收流程
pod中容器退出超过--minimum-container-ttl-duration
标记为可回收,pod最多可以保留--maximum-dead-containers-per-container
个停止的容器,一个node最多可以保留--maximum-dead-containers
,会按照退出时间先后进行回收,回收容器,sandbox和对应的logdir
镜像回收流程
磁盘使用率大于--image-gc-high-threshold
开始回收未使用的镜像,直到磁盘使用率小于--image-gc-low-threshold
- kubelet初始化后,调用了k.StartGarbageCollection启动GarbageCollect
- StartGarbageCollection启动containerGC(默认1min的间隔)和imageGC(默认5min的间隔)
- containerGC使用gc.evictableContainers获取需要回收的容器有三种
- deleted状态(pod删除,status.phase为failed且status.reason为evicted,
pod.deletionTimestamp != nil
且status为terminated或waiting)回收所有容器 - terminated状态(Failed和succeeded )回收所有容器
- 非deleted和terminated状态使用cgc.enforceMaxContainersPerEvictUnit保留配置的停止容器