kubelet

时间:July 22, 2020 分类:

目录:

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

包含模块

  1. PLEG,调用container runtime获取节点的pod信息,和pod的cache对比,生成PodlifecycleEvent防入eventChannel,syncLoop进行消费
  2. cAdvisor是开源监控工具,被集成到kubelet,收集节点和容器的监控信息,对外提供了interface,被imageManager,OOMWatcher和containerManager使用
  3. OOMWatcher通过Watch的方式从cAdvisor获取OOM信号,产生事件
  4. probeManager依赖statusManager,livenessManager和containerRefManager监控pod的容器健康情况,支持livenessProbe和readinessProbe探针,livenessProbe探测失败会执行对应的重启策略,readinessProbe探测成功会将容器加入到对一个的svc和endpoint
  5. statusManager用于维护和存储状态信息,用于其他模块调用
  6. containerRefManager
  7. evictionManager获取资源信息,不足时触发驱逐策略,--eviction-hard设置
  8. imageGC负责镜像回收,--image-gc-high-threshold--image-gc-low-threshold设置
  9. containerGC清理已经Exited的container
  10. imageManager调用kubecontainer管理镜像相关
  11. volumeManager负责volume和pod的生命周期
  12. containerManager管理container的cgroup配置,--cgroups-per-qos设置
  13. runtimeManager负责与CRI对接,默认是dockcer
  14. podManager存储和访问pod信息,会被其他模块调用
  15. secretManager
  16. configMapManager

kubelet启动流程

调用NewKubeletCommand获取配置

  1. 创建不允许修改的配置集KubeletFlag和可动态配置的KubeletConfiguration
  2. 解析命令行参数
  3. 初始化默认配置
  4. 加载配置loadConfigFile
  5. 校验配置ValidateKubeletConfiguration
  6. 检查是否启用动态配置
  7. 初始化kubeletDeps
  8. 调用Run方法Run(kubeletServer, kubeletDeps, stopCh)

Run方法调用run启动,最开始也是检查lock,配置文件注册到configz,检查是否为standalone的调试模式,设置OOM分数为-999

  1. RunKubelet启动
  2. http.ListenAndServe启动对应的healthz接口
  3. daemon.SdNotify向systemd发信号

RunKubelet使用createAndInitKubelet进行kubelet组件的初始化,然后调用startKubelet启动kubelet的组件

  1. createAndInitKubelet包括初始化PodConfig,和其他各种Manager和Watcher等模块
  2. startKubelet进行启动所有模块k.Run

createAndInitKubelet初始化模块的流程

  1. kubelet.NewMainKubelet实例化kubelet对象,对依赖的模块初始化
  2. k.BirthCry向apiserver发送kubelet启动的event
  3. k.StartGarbageCollection启动containers和images的回收

kubelet.NewMainKubelet就会初始化绝大多数包含的模块

startKubelet调用的k.Run启动所有模块和主要的循环逻辑

  1. 注册logServer
  2. 启动各种模块
  3. 启动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被删除

  1. podManager先会判断是否为mirror podmirror pod需要单独调用方法handleMirrorPod启动(静态Pod不会受apiserver管理也没有rs关联)
  2. 检验Pod是否能在节点运行,例如磁盘空间,如果不可以就进行拒绝
  3. dispatchWork把创建pod的任务下发到podWorkers异步处理
  4. 在probeManager中添加pod
  5. 如果有readiness和liveness,启动goroutine定时检测

dispatchWork下发的任务是将任务转为UpdatePodOptions,调用podWorkers.UpdatePod()

podWorkers收到更新事件之后,将事件发送更新管道

  1. 获取pod是否有对应的channle
  2. 如果没有p.podUpdates[uid](channel),创建单独的channel,goroutine启动managePodLoop接受channel的事件,channel存在则pass
  3. 如果没有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

  1. 拉取镜像
  2. 设置pod重启定时器,FindContainerStatusByName
  3. 生成业务容器配置generateContainerConfig
  4. 执行Lifecycle.PreStartContainer
  5. 调用CRI创建容器并启动CreateContainer调用runtimeService.StartContainer
  6. 执行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方法

  1. 启动定时器syncTicker,时长syncPeriod默认为10s
  2. 监听podStatusChannel,如果有pod变化使用syncPod同步,定时器触发使用syncBatch同步

syncPod

  1. 判断apiStatusVersions是否小于status的版本号
  2. 从apiserver获取pod的oldStatus
  3. 对比oldStatus和currentStatus是否相同,并不相同则是重建过
  4. 同步最新的状态到apiserver
  5. 判断pod是否处于canBeDeleted,如果在就在apiserver删除并删除cache

syncBatch

  1. 调用GetUIDTranslations从podManager获取pod和uid的关系
  2. 遍历apiStatusVersions的pod是否在podStatuses,不在就删除
  3. 遍历podStatuses,needsUpdate或needsReconcile过的就放到更新列表updatedStatuses
  4. 遍历updatedStatuses调用syncPod

SetPodStatus可以设置的状态仅有ContainersReady,Initialized,Ready,PodScheduled和Unschedulable,更新状态并写入podStatusChannel

pod的删除机制

apiserver

  • 删除pod的时候,kube-apiserver只是将pod的DeletionTimestamp和DeletionGracePeriodSeconds字段改变

kubelet

  1. 检测到kubetypes.UPDATE,使用HandlePodUpdates方法的dispatchWork
  2. dispatchWork的podIsTerminated方法判断,目前还不是terminated状态
  3. syncPod的时候有DeletionTimestamp执行killPod
  4. killPod调用的killPodWithSyncResult,包括停掉container的killContainer,和再停掉sandbox的StopPodSandbox
  5. HandlePodCleanups对container处于crash的进行清理
  6. 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进行处理

  1. 判断pod属于哪种Qos
  2. 根据pod信息计算Qos的cgroup值
  3. 更新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启动流程

  1. 检查依赖的内核参数
  2. 如果CgroupsPerQOS为true,调用cm.createNodeAllocatableCgroups创建rootcgroup,然后调用cm.qosContainerManager.Start启动osContainerManager
  3. cm.enforceNodeAllocatableCgroups计算node的可用(allocatable)资源配置到rootcgroup,然后根据配置的SystemReserved和KubeReserved配置cgroup
  4. 为系统组件配置cgroup资源限制
  5. 为系统进程配置oom_score_adj

cm.qosContainerManager.Start的流程

  1. 检查rootcgroup是否存在
  2. 为Burstable和BestEffort创建cgroup
  3. m.UpdateCgroups每分钟定时更新cgroup

m.UpdateCgroups的流程

  1. 调用m.setCPUCgroupConfig计算node上的activePods来更新bestEffor和burstable的cgroup的cpu.shares
  2. 调用m.setHugePagesConfig更新huge pages
  3. 检查是--qos-reserved参数是否启用,如果启用调用m.setMemoryReserve计算每个Qos class中需要设定的值然后调用m.cgroupManager.Update更新cgroup中的值
  4. 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

  1. kubelet初始化后,调用了k.StartGarbageCollection启动GarbageCollect
  2. StartGarbageCollection启动containerGC(默认1min的间隔)和imageGC(默认5min的间隔)
  3. containerGC使用gc.evictableContainers获取需要回收的容器有三种
  • deleted状态(pod删除,status.phase为failed且status.reason为evicted, pod.deletionTimestamp != nil且status为terminated或waiting)回收所有容器
  • terminated状态(Failed和succeeded )回收所有容器
  • 非deleted和terminated状态使用cgc.enforceMaxContainersPerEvictUnit保留配置的停止容器