Kubelet运行机制解析
Kubelet运行机制解析
基于1.29
在K8s集群中,在每个Node上都会启动一个kubelet进程,进程用于处理Master下发Node任务,管理Pod以及Pod中人容器。
每个kubelet进程都会在API Server注册Node信息,定期向Master汇报Node资源使用情况,并且通过cAdvisor监控容器和Node资源
自从K8s v1.28开始,kubelet通过
--config-dir
支持一个插件配置目录,在启动时,kubelet会合并以下几个部分的配置- 命令行参数(优先级最低)
- kubelet配置文件
- 排序的插件配置文件
- 在命令行中指定的特性门控–feature-gates(优先级最高)
Node通过kubelet启动参数
--register-node
决定是否向API Server注册自己,还可能有以下的参数--api-servers
:API Server的配置--kubeconfig
:kubeconfig文件,用于访问API Server的安全配置文件
资源管理
深入理解节点可分配资源(Node Allocatable Resources)
kubelet管理的Node计算资源、包含CPU、Memory、磁盘、GPU等等
容器的资源限制基于Linux内核的Cgroups机制实现,在K8s 1.19引入cgroupv2,在1.25进入到Stable
- 一开始Docker默认使用cgroupfs驱动管理容器,但是Linux中更多使用systemd管理cgroups
- systemd进程是Linux系统中的1号进程,在随着迭代,K8s鼓励使用systemd而不是docker的cgroups
kubelet限制进程的CPU占用时间
原理是通过设置Cgroups中的cpu.cfs_quota_us
参数来实现
一个CPU同一时刻只能被一个进程占用,Linux操作系统中,默认把一个调度周期的CPU时间段定义为100ms=100*1000微秒,对应Cgroups中的cpu.cfs_period_us
=10000
kubelet限制Memory
Memory和CPU不同,Memory是不可压缩资源,当使用内存超出的时候会出现OOM
在K8s v1.22中引入了具备QoS的内存分配机制,在v1.27中进入到 Alpha,在Cgroups v2支持以下三个:
memory.min:进程所能分配的最小内存,被设置成容器的Memroy Request值。即使系统不足,Linux内核也不能回收这些内存,确保了内存分配的服务质量等级
memory.max:进程所能分配的最大内存,被设置成容器的Memory Limit值。当进程占用的内存达到memory.max设置值不再减少内存使用量,则会触发OOM并且终止,因此可以被视为内存红线
memory.high:内存限流阀,一般设置接近memory.max,当进程达到memory.high,触发内存限流操作,同时增加系统执行内存回收压力
memory.high需要介于Memory Request与Memory Limit之间值,设置太小,会过早触发容器的内存限流策略,如果设置太大起不到保护作用,容易触发OOM,所以引入了memoryThrottlingFactor,kubelet的参数,默认0.9
memory.high=floor[(requests.memory)+memoryThrottlingFactor * (limits.memory or node allocateable memory - requests.memory) / pageSize ] * pageSize
Node资源管理概述
总体上负责以下资源管理工作:
- 收集Node上的资源总量数据,并上报为API Server
- 给新的Pod分配合适的计算资源
- 监测在本Node上被删除的Pod,回收已分配的资源
- 当Node上的资源不足,通过驱逐部分Pod来释放资源
在1.28 Stable阶段引入的HugePage
在1.20 Stable阶段引入的PID管理
kubelet开启SpportPodPidsLimit特性之后,通过设置pod-max-pids参数就可以限制Node上每个Pod的PID最大值
SupportNodePidsLimit特性:可以开启PID的预留功能
资源分配机制的设计与实现
主要是分化为三个部分管理:CPU Manager、Memory Manager和Device Manager
为了协调三种管理,出现了Topology Manager
在v1.26,CPU Manager和Device Manager一起成为正式版本
在V1.27 加大了Topology Manager的集成
//TODO more info
Node资源的防护机制
Node资源保护机制有俩种
- 静态保护:通过严格的资源配额管理机制结合Node资源预留的方式来避免Node上的资源被滥用或过度使用
- 动态防护:kubelet时刻监控Node资源的使用情况,当发现Node上的系统资源严重不足,会开启主动回收资源的Pod驱逐机制,防止系统崩溃
Pod管理
概述
kubelet通过以下方式获取在自身Node上的运行的Pod清单
- 静态Pod配置文件:kubelet通过启动参数–config来制定目录中的PodYAML文件(默认目录/etc/kubernetes/mainifests)
- kubelet会持续监控目录文件变化,通过启动参数
--file-check-frequency
检查目录的时间,默认20s
- kubelet会持续监控目录文件变化,通过启动参数
- HTTP端点:通过
--mainfest-url
参数设置,通过–http-check-frequency参数间隔时间,默认20s - API Server:kubelet通过API Server监听etcd目录,同步Pod列表
大当Kubelet读取到信息是创建和修改Pod任务,会做以下处理
- 为该Pod创建一个数据目录
- 从API Server读取该Pod清单
- 为该Pod挂载外部卷
- 下载Pod用到的Secret
- 检查已经运行Node上的Pod,如果该Pod没有容器或者Pause容器没有启动,先停止Pod所有的容器
- 用kubernetes/pause镜像为Pod都创建一个容器,pause容器建立起头容器的网络
- 为Pod中的每个容器都做以下处理
- 为容器计算一个哈希值,然后用容器名词去 查询对应Docker容器的哈希值。若查询容器,如果不同,就停止Docker容器中的进程
- 如果容器被终止,且容器没有指定的重启策略,则不做任何处理
- 调用Docker Client下载容器镜像,调用Docker Client 运行容器
容器探针
- Startup Probe:判断容器是否已经启动处于完成状态
- Readiness Probe:判断容器是否启动完成处于可用
- Liveness Probe:用于判断容器是否处于正常状态,即是可以正常提供服务
…不一一介绍
生命周期管理
Pod在K8s不是一个非持久化对象,每个Pod都会被赋予唯一UUID
Pod的生命周期先后经过:
Pending
Running
Unknown
Terminal:Pod根据容器结束时候的状态,Pod归属于Succeeded还是Failed
Pod生命周期是直线,不能回退
容器的状态
容器有自己的生命周期
- Waiting:容器在创建和启动过程中的状态,比如拉取镜像、等待PV、启动容器
- Running:容器内的主进程启动并正常运行
- Terminated:容器结束运行,进程要么正常结束,要么发生错误退出
容器除了生命周期,还有相关的回调钩子
- PostStart:容器在启动成功回调这个钩子,一般用于资源准备
- PreStop:容器在停止之前回调用这个钩子,一般用于清理和释放资源
性能指标API
API Server以代理的方式提供了Node的性能指标API-Summary Metrics API
这个API数据源是kubelet通过内嵌的cAdvisor收集到
cAdvisor是一个开源的分析容器资源使用率和性能特性的代理工具
默认在kubelet的10250端口上开放了性能指标API服务
除了通过kubelet使用内嵌的cAvisor获取Node的性能指标数据,也可以通过CRI获取Pod,条件如下
- 开启PodAndContainersStatsFromCRI特性,K8s 1.23 Alpha版本
- CRI支持访问统计(Containerd 版本v1.6.0以上,CRIO版本1.23.0)
通过CRI直接获取Pod和容器的性能数据特性优势如下:
- 提升性能,不用通过kubelet再次聚合
- kubelet与CRI进一步解耦,cAvisor无须处理容器的性能指标