3. Kubelet 内部架构:Pod 管理循环、SyncLoop 机制、PLEG 工作原理

Kubelet 是 Kubernetes 集群中运行在每个节点上的核心代理,负责管理节点上所有容器的生命周期。理解 Kubelet 的内部架构,特别是其核心的 Pod 管理循环、SyncLoop 机制以及 PLEG(Pod Lifecycle Event Generator)组件,是分析容器运行时 CRI 与 Kubelet 资源交互瓶颈的关键前提。

3.1 Kubelet 核心架构概览

Kubelet 内部采用模块化设计,通过多个紧密协作的组件来驱动 Pod 的期望状态与当前状态保持一致。其核心工作流围绕一个主循环(SyncLoop)展开,该循环负责监听来自 API Server 的 Pod 变更事件,并协调内部组件执行相应的操作。

关键认知: Kubelet 本质上是一个“状态机控制器”,其核心职责是不断将节点上 Pod 的“当前状态”拉向由 API Server 定义的“期望状态”。SyncLoop 和 PLEG 正是实现这一目标的两大核心引擎。
Kubelet 内部核心组件与数据流架构 API Server SyncLoop (主循环) PLEG Pod Manager CRI Runtime 容器运行时 PodWorkers (协程池) Status Manager Watch / List 触发 Relist 事件通知 CRI 调用 分发 Pod 更新 状态上报 更新 Pod Status

3.2 Pod 管理循环与 SyncLoop 机制

SyncLoop 是 Kubelet 的“心脏”,它是一个无限循环,负责监听多个通道(Channels)上的事件,并根据事件类型触发相应的同步操作。这些事件来源包括:

  • API Server 变更: 通过 List-Watch 机制获取 Pod 的创建、更新、删除事件。
  • PLEG 事件: 接收来自 PLEG 的 Pod 生命周期事件(如容器启动、退出、健康检查失败)。
  • 探针结果: 接收来自存活探针(Liveness Probe)和就绪探针(Readiness Probe)的结果。
  • 内部定时器: 定期触发同步,处理状态上报和垃圾回收等任务。

当 SyncLoop 接收到一个 Pod 的更新事件时,它会将该 Pod 的同步请求放入一个工作队列中。随后,一组称为 PodWorkers 的协程池会从队列中取出任务,并执行实际的同步逻辑。

3.3 SyncPod 流程详解

每个 PodWorker 负责处理一个特定 Pod 的同步。其核心方法是 syncPod(),该方法执行以下关键步骤:

  1. 计算 Pod 变更差异: 对比 Pod 的期望状态(来自 API Server)与当前状态(来自 Pod Manager 缓存)。
  2. 创建或更新 Pod 沙箱: 通过 CRI 调用 RunPodSandbox 或检查现有沙箱状态。
  3. 创建或更新 Init 容器: 按顺序启动 Init 容器,等待其成功退出。
  4. 创建或更新业务容器: 并行启动或更新业务容器,包括拉取镜像、创建容器、启动容器。
  5. 启动探针: 为容器配置并启动存活、就绪和启动探针。
  6. 更新 Pod 状态: 将最新的 Pod 状态(如容器状态、Pod 阶段)上报给 Status Manager。
性能瓶颈点: syncPod() 是一个同步阻塞操作。如果某个 Pod 的同步过程耗时过长(例如,镜像拉取慢、CRI 调用超时),它会阻塞该 PodWorker 处理该 Pod 的其他更新,但不会阻塞其他 PodWorker 处理其他 Pod。然而,如果大量 Pod 同时触发同步,可能会导致 PodWorker 协程池耗尽,从而引发连锁延迟。

3.4 PLEG(Pod Lifecycle Event Generator)工作原理

PLEG 是 Kubelet 内部一个至关重要的组件,它负责“感知”容器运行时的状态变化,并将这些变化转化为 Pod 生命周期事件,从而驱动 SyncLoop 进行重新同步。在没有 PLEG 之前,Kubelet 只能通过定期轮询容器运行时来获取状态,效率低下且延迟高。

PLEG 的核心工作原理可以概括为“Relist 与事件生成”:

步骤 操作 说明
1 定期 Relist PLEG 以一个可配置的间隔(默认 1 秒)调用 CRI 的 ListContainers 和 ListPodSandbox 接口,获取节点上所有容器和沙箱的当前状态快照。
2 状态比对 将本次 Relist 获取的状态快照与上一次 Relist 的缓存状态进行逐项比对。
3 生成事件 如果发现某个容器或沙箱的状态发生了变化(例如,从 Running 变为 Exited,或 Created 变为 Running),PLEG 会生成一个对应的 Pod 生命周期事件。
4 事件推送 将生成的事件推送到一个事件通道中,SyncLoop 会监听该通道并触发相应的 Pod 同步。
PLEG 工作流程:Relist 与事件生成 定时器 (1s) PLEG 核心 Pod 状态缓存 CRI List 接口 容器运行时 事件通道 触发 调用 返回状态 返回结果 更新/比对 生成事件

3.5 PLEG 的瓶颈与优化

尽管 PLEG 极大地提升了 Kubelet 的响应性,但它本身也可能成为性能瓶颈,尤其是在大规模节点或高密度 Pod 部署场景下。

核心瓶颈:Relist 开销
PLEG 的 Relist 操作会调用 CRI 的 ListContainers 和 ListPodSandbox 接口。当节点上运行着数百甚至上千个 Pod 时,每次 Relist 都需要从容器运行时获取并序列化大量状态数据。如果容器运行时(如 containerd 或 CRI-O)在处理这些 List 请求时效率不高,或者 gRPC 调用本身存在延迟,就会导致 Relist 耗时过长,进而影响 PLEG 的事件生成速度,最终拖慢整个 SyncLoop。

针对 PLEG 的瓶颈,社区和业界提出了多种优化方案:

  • 调整 Relist 间隔: 适当增大 --node-status-update-frequency 和 PLEG 的 Relist 周期(通过 Kubelet 配置 plegRelistInterval),减少不必要的 List 调用。
  • 使用事件驱动机制: 探索基于 CRI 的 ContainerEvent 或 PodSandboxEvent 等流式事件接口,替代主动轮询的 Relist 模式,实现真正的“零轮询”状态感知。
  • 优化 CRI List 实现: 推动容器运行时优化其 List 接口的性能,例如通过索引、缓存或更高效的序列化方式(如 protobuf)来降低延迟。
  • 引入增量 Relist: 在 PLEG 内部实现增量状态同步,只获取自上次 Relist 以来发生变化的部分,而不是全量快照。

3.6 总结:从 SyncLoop 到 CRI 的调用链

综合以上分析,我们可以梳理出一条从 Kubelet 到容器运行时的完整调用链:

  1. 事件触发: API Server 的 Pod 变更或 PLEG 的容器状态变化事件进入 SyncLoop。
  2. 任务分发: SyncLoop 将 Pod 同步任务分发给对应的 PodWorker。
  3. 状态计算: PodWorker 调用 syncPod(),计算期望状态与当前状态的差异。
  4. CRI 调用: PodWorker 通过 Pod Manager 调用 CRI 接口(如 RunPodSandbox, CreateContainer, StartContainer)来执行具体的容器操作。
  5. 状态反馈: 容器运行时执行操作后,将结果返回给 Kubelet。
  6. 状态更新: PodWorker 更新本地状态缓存,并通过 Status Manager 将 Pod 状态上报给 API Server。