Kubernetes Kubelet
本文最后更新于 2025年8月14日 上午
kubelet
kubelet 进程用于处理Master下发到本Node的任务,管理Pod和Pod中的容器。每个kubelet进程都会在API Server上注册Node信息,定期向Master汇报Node资源的使用情况,并通过cAdvisor监控容器和Node的资源。kubelet中会合并以下几部分的配置
- 命令行参数
- kubelet配置文件
- 排序的插件配置文件
- 在命令行中指定的特性门控 —feature-gates
Node通过设置kubelet的启动参数—register-node来决定是否向API Server注册自己。如果该参数的值为true,那么kubelet将试着通过API Server 注册自己。在注册时,kubelet启动还涉及以下参数
—api-servers: API Server的位置
—kubeconfig: kubeconfig文件,用于访问API Server的安全配置文件
kubelet在启动时通过API Server注册Node信息,并定时向API Server 发送Node的新信息,API Server在接收到这些信息后,会将其写入etcd。通过kubelet的启动参数nodeStatusUpdateFrequency,可设置kubelet每隔多长时间向API Server报告Node状态,默认为10s。目前仅允许kubelet修改和创建其所在的Node。
资源管理
节点可分配资源
Cgroups 用于对容器资源进行限制
核心概念
task :task是一个要被Cgroups控制的进程
cgroup:cgroup定义了一组task作为一个整体,对其进行具体的配额限制和保护。一个task在被加入到某个cgroup后,就受到这个cgroup的资源限制和保护。cgroup有层级关系,类似树结构,子节点的cgroup继承父节点的cgroup资源限制配额
subsystem:子系统代表了一种资源控制器,Kubernetes中常用的子系统有cpu,cpuset,memory,huge_tlb 和 pids等
Cgroups 会根据每种子系统定义一套cgroup控制树。
当我们需要针对某个进程实施资源控制时,就把它的进程与对应资的cgourp控制树上的某个节点cgroup捆绑,控制哪几类资源就绑定到哪几类资源的控制树上。Cgroup 作为虚拟文件系统结构挂载在/sys/fs/cgroup目录
1 | |
在默认情况下,systemd会设置3个slice单元
- system.slice : 所有系统服务的默认位置
- user.slice:所有用户会话的默认位置
- machine.slice:所有虚拟机和Linux容器的默认位置
systemd会在slice下面挂载两种名称的cgroup
- service: 一个或一组进程,systemd管理的每个服务都作为一个service单元被控制,在service cgroup中关联具体的服务进程
- scope:一组外部创建的进程,由强制进程通过fork函数启动和终止,之后由systemd在运行时注册的进程被认为是scope
systemd在每个具体的cgroup控制树上都创建了一组与systemd目录结构相对应的Cgroups,后者才是真正实现资源控制的根控制器
1 | |
通过设计两个slice 来分别控制
- kube-reserved 资源预留
- system-reserved 资源预留
一个 32GB 内存,16各CPU 和100GB 本地磁盘存储的 资源设置示例
1 | |
CPU资源限制
CPU属于Kubernete中的可压缩资源,如果一个容器鲜卑分配了0.1Core的CPU资源上限,则容器实际使用的CPU资源是无法超过这个上限的。kubelet通过控制容器进程所能占用的CPU时间片数量来完成对进程CPU占用量的限制
CPU Limit 限制是通过Cgroups的cpu.cfs_quota_us 和 cpu.cfs_period_us参数来实现的。CPU Request限制通过Cgroups的cpu.shares参数来实现的。当一个CPU被多个进程共享时,每个进程的cpu.shares值都表示它最少能占用的CPU时间百分比。而CPU Limt则会严格限制进程请求的时间片小于所限制的资源
Memory资源限制
Memory属于Kubernete中的不可压缩资源。资源限制一旦确认,当进程尝试申请超过限制的资源时就会触发OOM异常。在Cgroups v2中提供了更详细的内存控制
- memory.min : 进程所能分配的最小内存,被设置为容器的Memory Request值。即使系统内存不足,Linux内核也不能回收这些内存,由此确保了内存分配的服务质量等级
- memory.max : 进程所能分配的最大内存,被设置为容器的Memory Limit值,当进程占用的内存达到memory.max 设定值且不再减少内存使用量时,则会触发OOM并被终止。
- memory.high : 内存限流阀,用于内存使用量的预警和限流控制,当进程使用的内存达到memory.high后,会触发内存限流动作,同时增加系统执行内存回收的压力。
在设置了系统和kubelet的预留资源后,再通过cgroups 的设计, Node Allocatable Resources 解决了以下两个关键问题:
- Node上实际可分配资源的数量
- 基于QoS实现了资源保护机制,当资源严重不足时,低优先级的Pod会优先被清除,包括被操作系统优先清除
通过查询Pod的QoS优先级和Pod的cgroup根控制器中提供的容器占用的实时资源数据,kubelet就可以实现基于Pod优先级的资资源驱逐计划,当优先级相同时,优先驱逐高耗能的Pod。
对于操作系统来说,只要触发了超过cgroup内存访问大小的进程就可以进行清除过程,
所以在kubelet中也为 每一个pod设置了 kubepods.slice 的 根控制器,用于控制Pod可使用资源的大小。最终
- Node上的Pod只能一起分配和共享Node Allocatable的资源总量,并且从机制上保证它们不会突破许可的资源总量
- 通过Cgroups机制结合Pod的QoS等级的方式,确保每一类QoS等级的Pod都能使用其许可范围内的资源
- 通过单独为Burstable和BestEffor级别的Pod设置cgroup 根控制器来限制它们可用的资源总量
其结构为
1 | |
Node资源管理
HugePage
在现代操作系统中,内存是以页而不是以字节为单位继续宁管理的,内存的分配和回收都是基于Page进行。典型的Page大小为4KB。当需要申请较多内存时则会导致:
- 这些分配的内存页在物理内存地址上并不是连续的,因此数据的读写性能会比单一的Page要差一些
- 由于进程分配的内存页数量非常多,所以会导致内存地址寻址表TLB的缓存命中率大大降低。
所以通过使用HugePage 可用解决命中率和内存地址连续性问题。
PID
PID是Linux操作系统中很重要的一种基础资源,每个进程都有唯一的PID,每创建一个进程就需要分配一个新的PID,如果Pod容器滥用多进程,随意开启大量的子进程,容易触及系统上线。Kubernete中增加了“pod-max-pids”对PID最大值的限制
Pod管理
概述
kubelet通过以下方式获取在自身Node上运行的Pod清单
- 静态Pod配置文件: kubelet通过启动参数 —config来指定目录中的Pod YAML文件。kubelet会持续监控指定目录中的文件变化,以创建或删除Pod。
- HTTP端点:通过—manifest-url 参数设置,通过—http-check-frequency参数检测该HTTP端点数据的时间间隔,默认为20s
- API Server:kubelet通过API Server监听 etcd目录,同步Pod列表
kubelet 监听etcd, 所有针对Pod的操作都会被kubelet监听,如果发现有新绑定到本Node的Pod,则kubelet按照Pod清单的要求创建该Pod。如果发现本地的Pod需要被修改,则kubelet会做出相应的修改。
当kubelet读取到的信息是创建和修改Pod任务时,会做如下处理
- 为该Pod创建一个数据目录
- 从API Server中读取该Pod清单
- 为该Pod挂载外部卷
- 下载Pod用到的Secret
- 检查已经运行在Node上的Pod,如果该Pod没有容器或Pause容器没有启动,则先停止Pod中所有容器的进程,如果Pod中有需要删除的容器,则删除这些容器
- 用kubernetes/pause 镜像为每个Pod都创建一个容器。该Pause容器是用于接管Pod中所有其他容器的网络。每创建一个新的Pod,kubelet 都会创建又给Pause容器,再创建其他容器。
- 为Pod的每个容器都做如下处理
- 为容器计算一个哈希值,然后用容器的名称去查询对应Docker容器的哈希值,若查找到容器,且二者的哈希值不同,则停止Docker中容器的进程,并停止与之关联的Pause进程;若两者相同,则不做任何处理。
- 如果容器被终止,且容器没有指定的重启策略,则不做任何处理
- 调用Docker Client 下载容器镜像,调用Docker Client 运行容器
容器探针
kubelet 通过三种探针用于检查Pod的状态
- Startup Probe
只有等到Startup Probe执行结果返回success后才开始执行其他探针,如果执行失败则尝试重启容器
- Readiness Probe
Readiness Probe 用于判断容器是否已经启动完成且处于可用状态,如果Readiness Probe检测到容器启动失败,则Pod的状态将被修改,Endpoint Controller 将从Service的Endpoint中删除包含该容器所在Pod的IP地址的Endpoint 条目
- Liveness Probe
Liveness Probe 用于判断容器是否处于正常工作状态,如果探测到容器不健康,则将删除该容器,并根据重启策略尝试做出相应的处理。
kube-proxy
在Kubernetes集群的每个Node上都运行着一个kube-proxy服务进程,我们可用把这个进程看作Service的透明代理兼负载均衡器,其核心功能是将某个Service上的访问请求转发到后端的多个Pod上。
第一代Proxy
kube-proxy 作为一个真实的TCP/UDP数据代理,负责转发从Service到Pod的访问流量。当某个Pod以ClusterIP地址访问某个Service时,这个流量就被Pod所在的Node的iptables规则转发到kube-proxy,由kube-proxy建立到后端Pod的TCP/UDP连接,之后将请求转发到后端的某个Pod上,并在这个过程中实现负载均衡
Service的ClusterIP与NodePort等概念是kube-proxy服务通过iptables的NAT规则实现的。kube-proxy在运行过程中动态地创建于Service相关的iptables规则,这些规则实现了将访问服务的请求负载分发到后端Pod的功能,由于iptables针对的是本地的kube-proxy端口,所以每个Node上都要运行kube-proxy组件。这样一来,在Kubernetes集群内部,我们可以在任意Node上发起对Service的访问请求。
第二代Proxy
iptables模式下的第二代kube-proxy不再起到数据层面的Proxy作用,Client向Server的请求流量通过iptables的NAT规则直接发送到目标Pod,不经过kube-proxy转发,kube-proxy只承担了控制层面的功能,即通过API Server的watch接口实时跟踪Service与Endpoint的变更信息,并更新Node上相应的iptables规则。
第三代Proxy
基于IPVS模式相比iptables模式 可以提供更高的转发性能。
- 为大型集群提供了更好的可扩展性和性能
- 支持比iptables更复杂的负载均衡算法
- 支持服务器健康检查和连接重试
- 可以动态修改ipset的集合,即使iptables规则正在使用这个集合
由于IPVS模式无法提供包过滤等功能,因此某些场景下要与iptables模式搭配使用。
垃圾回收
Pod对象的垃圾回收
对于阶段为Failed的Pod,对应的容器虽然已经停止,但是其资源对象仍然存储在API Server中。kube-controller-manager 提供了一个PodGC的服务专门清理这些Pod。当垃圾Pod数量超过kube-controller-manager参数设置的—terminated-pod-gc-threshold值时,会触发清理过程
Job对象的垃圾回收
Job对象的.spec.ttlSecondsAfterFinished属性中设置合理的过期时间,然后Kubernetes TTL Controller会等待Job对象的TTL时间结束,并自动回收该对象。
无主对象的垃圾回收
kubernetes中的很多对象都具有所属关系,即一个对象属于另外一个对象,对象的Owner属性显示了Kubernetes控制层和其他关注者的依赖关系,同时给了删除对象及其所关联对象的机会。在正常情况下,Kubernete会根据对象之间的关联关系,实现关联性删除操作。但是如果删除了一些中间层对象,导致其关联对象变为无主对象。Kubernetes会检查并自动删除这些无主对象,以完成垃圾回收。
容器和镜像的垃圾回收
kubelet进程负责清理容器和镜像垃圾,默认频率如下
- 每两分钟清理一次镜像垃圾
- 每一分钟清理一次容器垃圾
通常情况下,kubelet会先回收“死亡”最久的容器,具体来说,kubelet基于以下几个参数调整容器的回收动作
- MinAge : kubelet可回收的容器的最短生存时间,如果设置为零,则禁止回收
- MaxPerPodContainer : 每个Pod允许的最大“死亡”容器数量
- MaxContainers : 集群中允许的最大“死亡”容器数量
Node与Node Lease对象的垃圾回收
当Node对应的Server在公有云上被删除或终止后,该Node也将被Cloud Controller Manager自动清除。如果Kubernetes集群规模很大,则大量的Node心跳汇报会大大加重API Server的负担。因为API Server需要更新并持久化Node Statue对象,而Node Statue对象是一个比较重的资源对象。为了解决这个问题,Kubernetes用非常轻量级的Node Lease对象替代了Node Statue对象,实现了高性能的心跳检查和Node状态更新功能。每个Node都有一个同名的Node Lease对象。
1 | |
Node Lease对象的RenewTime表示Node最后一次更新的时间戳,Controller可以用这个时间戳来判断对应的Node是否Ready。当Node被回收后,对应的Node Lease对象会被Kubernetes自动清除