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
  • HTTP端点:通过--mainfest-url参数设置,通过–http-check-frequency参数间隔时间,默认20s
  • API Server:kubelet通过API Server监听etcd目录,同步Pod列表

大当Kubelet读取到信息是创建和修改Pod任务,会做以下处理

  1. 为该Pod创建一个数据目录
  2. 从API Server读取该Pod清单
  3. 为该Pod挂载外部卷
  4. 下载Pod用到的Secret
  5. 检查已经运行Node上的Pod,如果该Pod没有容器或者Pause容器没有启动,先停止Pod所有的容器
  6. 用kubernetes/pause镜像为Pod都创建一个容器,pause容器建立起头容器的网络
  7. 为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无须处理容器的性能指标