K8s-kubelet深度剖析
Kubelet 深度分析
本文档深入分析 Kubernetes Kubelet 组件的架构设计、启动流程、核心功能和关键子系统。
目录
1. 架构概览
1.1 Kubelet 在 Kubernetes 架构中的位置
Kubelet 是 Kubernetes 集群中运行在每个节点上的核心代理(Node Agent),是 Kubernetes 控制平面与节点级容器运行时之间的桥梁。它负责维护节点上容器的生命周期,并将节点状态上报给 API Server。
在 Kubernetes 整体架构中,Kubelet 处于如下位置:
1 | +-------------------------+ +-------------------------+ |
1.2 主要职责和功能边界
Kubelet 的核心职责包括以下几个主要方面:
1.2.1 Pod 生命周期管理
Kubelet 负责管理节点上 Pod 的完整生命周期,包括:
- Pod 接收与解析:从多个来源(API Server、静态文件、HTTP 端点)接收 Pod 规范
- 容器创建与启动:根据 Pod Spec 创建并启动容器
- 健康检查:执行探针(liveness、readiness、startup)检测容器状态
- 容器生命周期:处理容器的优雅终止、重启策略等
- 垃圾回收:清理已终止的容器和未使用的镜像
1.2.2 节点管理
- 节点注册:向 API Server 注册节点信息
- 节点状态维护:定期上报节点状态(内存、磁盘、网络)
- 节点租约(Node Lease):维护节点的租约以实现高效的节点心跳
1.2.3 资源管理
- CPU 和内存管理:通过 cgroups 实现资源限制和 QoS
- 存储卷管理:挂载、卸载 PersistentVolume,处理 CSI 操作
- 资源驱逐:在资源压力下主动驱逐低优先级 Pod
1.2.4 与外部系统交互
- 容器运行时接口(CRI):通过 CRI 与容器运行时(docker、containerd、CRI-O)交互
- 镜像管理:拉取、缓存、垃圾回收容器镜像
- CSI 插件交互:与外部存储插件通信
1.3 核心模块划分
Kubelet 的核心功能被划分为多个协作的模块,主要包括:
1.3.1 Pod 配置源(Pod Sources)
Kubelet 支持三种 Pod 配置来源:
| 来源 | 描述 | 配置方式 |
|---|---|---|
| API Server | 通过 Watch 机制从 API Server 获取 Pod 规范 | --kubeconfig |
| File | 从本地文件目录读取静态 Pod 配置 | --pod-manifest-path |
| HTTP | 通过 HTTP 端点定期获取 Pod 配置 | --manifest-url |
这三种来源的配置会通过 PodConfig 进行聚合,统一对外提供 Pod 变更通知。
1.3.2 核心组件
flowchart TB
subgraph "Kubelet 架构"
A[Pod Sources] --> B[PodConfig]
B --> C[Kubelet Core]
C --> D[Pod Workers]
C --> E[PLEG]
C --> F[Managers]
subgraph "Managers"
F1[PodManager]
F2[StatusManager]
F3[ProbeManager]
F4[VolumeManager]
F5[EvictionManager]
end
D --> G[Container Runtime Interface]
G --> H[Container Runtime<br/>docker/containerd/cri-o]
end
主要模块说明:
- PodConfig:Pod 配置聚合器,监听多个配置源的变化,生成统一的 Pod 更新通道
- PodManager:管理期望的 Pod 集合(包括普通 Pod 和 Mirror Pod),维护 Pod 的元数据
- PodWorkers:Pod 工作者池,每个 Pod 拥有独立的工作协程,处理 Pod 的同步、终止、清理等状态转换
- PLEG(Pod Lifecycle Event Generator):定期 Relist 容器状态,生成 Pod 生命周期事件
- StatusManager:接收 Pod 状态更新,向 API Server 同步 Pod 状态
- ProbeManager:管理健康探针的执行,包括 Liveness、Readiness、Startup 探针
- VolumeManager:管理卷的挂载、卸载、附加、分离操作
- EvictionManager:监控节点资源使用,在资源压力下执行 Pod 驱逐
1.3.3 启动流程概览
Kubelet 的启动流程主要分为以下几个阶段:
- 命令行解析 (
cmd/kubelet/kubelet.go:34):通过NewKubeletCommand()创建 Cobra 命令 - 配置初始化 (
cmd/kubelet/app/server.go):加载 Kubelet 配置、创建依赖对象 - Kubelet 实例创建 (
pkg/kubelet/kubelet.go:339):通过NewMainKubelet()创建主实例,初始化所有 Manager - 服务启动 (
cmd/kubelet/app/server.go:1297):启动主循环、HTTP 服务器、只读端口等
1.4 核心组件职责
Kubelet 通过多个专业 Manager 组件协同工作,每个组件负责特定的职责领域。以下是核心组件的职责说明:
| 组件 | 文件路径 | 核心职责 | 关键数据结构 |
|---|---|---|---|
| PodManager | pkg/kubelet/pod/pod_manager.go |
Pod 元数据管理,维护静态 Pod 和镜像 Pod 之间的映射关系。Kubelet 从三个来源(File、HTTP、API Server)发现 Pod 更新,并保持内存中的 Pod 索引 | podByUID、mirrorPodByUID、podByFullName、translationByUID |
| StatusManager | pkg/kubelet/status/status_manager.go |
Pod 状态同步到 API Server。作为 Kubelet 中 Pod 状态的权威来源,接收来自 Worker 的状态更新并缓存,通过状态适配器将状态发布到 API Server | podStatuses、apiStatusVersions |
| ProbeManager | pkg/kubelet/prober/prober_manager.go |
健康检查探针管理。为每个需要探针的容器创建探针 Worker(Liveness、Readiness、Startup),定期执行探针并缓存结果,用于设置 PodStatus 中的 Ready 状态 | workers(map[probeKey]*worker)、readinessManager、livenessManager、startupManager |
| VolumeManager | pkg/kubelet/volumemanager/volume_manager.go |
存储卷管理。运行多个异步循环确定需要附加/挂载/卸载/分离的卷,并根据节点上调度的 Pod 执行相应的卷操作 | desiredStateOfWorld、actualStateOfWorld、operationExecutor |
| EvictionManager | pkg/kubelet/eviction/eviction_manager.go |
资源驱逐管理。监控节点资源使用(内存、磁盘等),在资源压力超过阈值时主动驱逐低优先级 Pod,维护节点稳定性 | nodeConditions、thresholdsMet、signalToRankFunc |
| PLEG | pkg/kubelet/pleg/pleg.go |
Pod 生命周期事件生成。定期 Relist 容器状态,生成 Pod 生命周期事件(ContainerStarted、ContainerDied、ContainerRemoved、PodSync),触发 Pod 同步 | PodLifecycleEvent、RelistPeriod |
组件协作关系
flowchart LR
subgraph "配置源"
A[API Server] --> F[PodConfig]
B[File] --> F
C[HTTP] --> F
end
F --> D[PodManager]
D --> E[PodWorkers]
D --> PLEG[PLEG]
D --> VM[VolumeManager]
D --> EM[EvictionManager]
PLEG --> E
E --> PM[ProbeManager]
E --> SM[StatusManager]
E --> VM
PM --> SM
SM --> API[API Server]
协作流程说明:
- Pod 接收:PodManager 从 PodConfig 接收 Pod 规范,维护 Pod 元数据
- Pod 同步:PodWorkers 处理 Pod 的创建、更新、删除操作
- 探针执行:ProbeManager 为容器执行健康检查,结果写入 StatusManager
- 状态上报:StatusManager 将 Pod 状态同步到 API Server
- 卷管理:VolumeManager 处理 Pod 的存储卷挂载操作
- 生命周期事件:PLEG 监控容器状态变化,生成事件触发 Pod 同步
- 资源驱逐:EvictionManager 在资源压力下执行 Pod 驱逐
1.5 依赖注入设计
Kubelet 使用依赖注入模式,通过 Dependencies 结构体(pkg/kubelet/kubelet.go:249)管理可注入的依赖项:
1 | type Dependencies struct { |
这种设计使得 Kubelet 核心逻辑与外部依赖解耦,便于单元测试时注入 mock 对象。
2. 关键数据结构
Kubelet 的核心功能依赖于几个关键的数据结构,理解这些结构对于深入分析 Kubelet 的工作原理至关重要。
2.1 Kubelet 主结构体
Kubelet 结构体是 Kubelet 的核心,封装了所有状态和管理器。以下是主要字段的说明:
位置: pkg/kubelet/kubelet.go:941
1 | type Kubelet struct { |
设计要点:
Kubelet结构体使用组合模式,将所有子管理器(Manager)作为字段组合进来- 每个 Manager 负责特定的职责领域,实现关注点分离
- 结构体字段大多为接口类型,便于依赖注入和单元测试
2.2 Dependencies 依赖注入容器
Dependencies 结构体是 Kubelet 的依赖注入容器,用于在运行时注入外部依赖项。这种设计实现了核心逻辑与外部依赖的解耦。
位置: pkg/kubelet/kubelet.go:249
1 | type Dependencies struct { |
设计要点:
Dependencies是临时解决方案,用于在确定更全面的依赖注入方案之前对依赖进行分组- 所有外部依赖通过此结构体注入,便于单元测试时替换为 mock 对象
Options字段允许使用函数式选项模式进行配置
2.3 SyncHandler 接口
SyncHandler 接口定义了 Pod 同步处理的契约,是 Kubelet 与外部交互的关键接口。
位置: pkg/kubelet/kubelet.go:222
1 | type SyncHandler interface { |
接口方法说明:
| 方法 | 用途 |
|---|---|
HandlePodAdditions |
处理从配置源新增的 Pod,触发 Pod 创建流程 |
HandlePodUpdates |
处理 Pod 规格更新,触发 Pod 更新同步 |
HandlePodRemoves |
处理删除的 Pod,触发 Pod 终止流程 |
HandlePodReconcile |
处理定期协调,确保实际状态与期望状态一致 |
HandlePodSyncs |
处理需要强制同步的 Pod |
HandlePodCleanups |
执行清理任务,如清理已删除的 Pod 相关资源 |
设计要点:
SyncHandler接口实现了 观察者模式,PodConfig 通过此接口通知 Kubelet Pod 变更- 接口设计便于测试,测试时可以传入 mock 实现
- Kubelet 自身实现了此接口,作为主要的同步处理器
2.4 versionedPodStatus 版本化 Pod 状态
versionedPodStatus 是包装 v1.PodStatus 的结构体,带有版本号用于确保不会将过时的 Pod 状态发送到 API Server。
位置: pkg/kubelet/status/status_manager.go:54
1 | type versionedPodStatus struct { |
设计要点:
- 版本控制:
version字段是单调递增的,确保状态更新的顺序性,避免因异步更新导致状态回退 - 时间戳:
at字段记录状态更新时间,用于判断状态的时效性 - 完成标志:
podIsFinished标志用于区分 Pod 是否已完成终止过程 - 线程安全:
statusManager使用podStatusesLock保护podStatusesmap 的并发访问
StatusManager 核心结构:
1 | type manager struct { |
2.5 数据结构关系图
classDiagram
class Kubelet {
+podManager: PodManager
+podWorkers: PodWorkers
+statusManager: StatusManager
+probeManager: ProbeManager
+volumeManager: VolumeManager
+evictionManager: EvictionManager
}
class Dependencies {
+KubeClient: clientset.Interface
+PodConfig: *PodConfig
+ProbeManager: Manager
+VolumePlugins: []VolumePlugin
+RemoteRuntimeService: RuntimeService
}
class SyncHandler {
<<interface>>
+HandlePodAdditions()
+HandlePodUpdates()
+HandlePodRemoves()
+HandlePodReconcile()
+HandlePodSyncs()
+HandlePodCleanups()
}
class versionedPodStatus {
+version: uint64
+podName: string
+podNamespace: string
+at: time.Time
+podIsFinished: bool
+status: v1.PodStatus
}
Kubelet ..> Dependencies : 依赖注入
Kubelet ..> SyncHandler : 实现
StatusManager --> versionedPodStatus : 管理
3. 启动流程分析
3.1 启动流程概述
Kubelet 的启动流程主要分为以下几个阶段:
- 命令行解析 (
cmd/kubelet/app/server.go:135):通过NewKubeletCommand()创建 Cobra 命令 - 配置初始化 (
cmd/kubelet/app/server.go):加载 Kubelet 配置、创建依赖对象 - Kubelet 实例创建 (
pkg/kubelet/kubelet.go:337):通过NewMainKubelet()创建主实例,初始化所有 Manager - 服务启动 (
cmd/kubelet/app/server.go:1297):启动主循环、HTTP 服务器、只读端口等
3.2 详细启动流程
3.2.1 命令行参数解析流程
Kubelet 使用 Cobra 框架构建命令行接口,NewKubeletCommand() 函数负责创建命令和解析参数。
位置: cmd/kubelet/app/server.go:135-249
1 | func NewKubeletCommand() *cobra.Command { |
参数解析关键步骤:
| 步骤 | 函数/操作 | 说明 |
|---|---|---|
| 1 | NewKubeletFlags() |
初始化命令行参数结构体 |
| 2 | NewKubeletConfiguration() |
初始化默认配置 |
| 3 | cleanFlagSet.Parse(args) |
解析命令行参数 |
| 4 | loadConfigFile() |
加载 YAML 配置文件 |
| 5 | mergeKubeletConfigurations() |
合并 drop-in 配置文件 |
| 6 | kubeletConfigFlagPrecedence() |
确保命令行参数优先级最高 |
| 7 | logs.InitLogs() |
初始化日志系统 |
3.2.2 依赖初始化顺序
RunKubelet() 函数负责协调依赖对象的创建和初始化。
位置: cmd/kubelet/app/server.go:1207-1295
1 | func RunKubelet(kubeServer *options.KubeletServer, kubeDeps *kubelet.Dependencies, runOnce bool) error { |
容器运行时服务初始化:
PreInitRuntimeService() 在主 Kubelet 启动前初始化 CRI 服务。
位置: cmd/kubelet/app/server.go:318-335
1 | func PreInitRuntimeService(kubeCfg *KubeletConfiguration, kubeDeps *Dependencies) error { |
CreateAndInitKubelet 流程:
位置: cmd/kubelet/app/server.go:1317-1362
1 | func createAndInitKubelet(...) (k kubelet.Bootstrap, err error) { |
3.2.3 运行时启动流程
Run() 方法是 Kubelet 的主循环,启动所有后台 goroutine。
位置: pkg/kubelet/kubelet.go:1742-1900
1 | func (kl *Kubelet) Run(updates <-chan kubetypes.PodUpdate) { |
各 goroutine 启动顺序:
| 顺序 | 组件 | 说明 |
|---|---|---|
| 1 | CloudResourceSyncManager | 同步云提供商资源 |
| 2 | initializeModules | 初始化内部模块 |
| 3 | VolumeManager | 卷管理器 |
| 4 | syncNodeStatus | 节点状态同步 |
| 5 | nodeLeaseController | 节点租约 |
| 6 | updateRuntimeUp | 运行时状态更新 |
| 7 | StatusManager | 状态同步 |
| 8 | RuntimeClassManager | RuntimeClass 同步 |
| 9 | PLEG | 生命周期事件生成 |
| 10 | syncLoop | Pod 同步主循环 |
3.3 启动流程序列图
sequenceDiagram
participant Main as main()
participant Cobra as NewKubeletCommand()
participant Server as RunKubelet()
participant Runtime as PreInitRuntimeService
participant Kubelet as NewMainKubelet
participant Modules as Run()
Note over Main,Cobra: 1. 命令行解析阶段
Main->>Cobra: app.NewKubeletCommand()
Cobra->>Cobra: NewKubeletFlags()
Cobra->>Cobra: NewKubeletConfiguration()
Cobra->>Cobra: loadConfigFile()
Cobra->>Cobra: mergeKubeletConfigurations()
Cobra->>Cobra: kubeletConfigFlagPrecedence()
Note over Cobra,Server: 2. 依赖初始化阶段
Cobra->>Server: RunE()
Server->>Server: GetHostname()
Server->>Server: getNodeName()
Server->>Server: makeEventRecorder()
Server->>Server: ParseNodeIPArgument()
Note over Server,Runtime: 3. 运行时服务初始化
Server->>Runtime: PreInitRuntimeService()
Runtime->>Runtime: remote.NewRemoteRuntimeService()
Runtime->>Runtime: remote.NewRemoteImageService()
Note over Runtime,Kubelet: 4. Kubelet 实例创建
Server->>Kubelet: createAndInitKubelet()
Kubelet->>Kubelet: NewMainKubelet()
Note over Kubelet: 初始化所有 Manager<br/>- PodManager<br/>- StatusManager<br/>- ProbeManager<br/>- VolumeManager<br/>- EvictionManager<br/>- PLEG
Kubelet->>Kubelet: BirthCry()
Kubelet->>Kubelet: StartGarbageCollection()
Note over Kubelet,Modules: 5. 服务启动阶段
Server->>Modules: k.Run(updates)
Modules->>Modules: initializeModules()
Modules->>Modules: volumeManager.Run()
Modules->>Modules: syncNodeStatus()
Modules->>Modules: nodeLeaseController.Run()
Modules->>Modules: updateRuntimeUp()
Modules->>Modules: statusManager.Start()
Modules->>Modules: runtimeClassManager.Start()
Modules->>Modules: pleg.Start()
Modules->>Modules: syncLoop()
Note over Modules,Server: 6. HTTP 服务启动
Server->>Server: ListenAndServe()
Server->>Server: ListenAndServeReadOnly()
Server->>Server: ListenAndServePodResources()
3.4 关键代码路径
启动入口 (cmd/kubelet/kubelet.go):
1 | func main() { |
配置加载流程:
1 | 命令行参数 --config --> KubeletConfiguration |
核心初始化顺序:
- API Server 连接建立
- CRI 服务连接建立
- PodConfig 源注册(File、HTTP、API Server)
- 各 Manager 初始化
- 主循环启动
4. 核心功能原理
本章深入分析 Kubelet 的核心功能实现原理,包括 Pod 生命周期管理和 Pod 同步机制。
4.1 Pod 生命周期概述
Pod 是 Kubernetes 的最小调度单元,Kubelet 负责管理节点上 Pod 的完整生命周期。理解 Pod 状态转换和 Kubelet 的同步机制对于掌握 Kubernetes 容器编排至关重要。
4.1.1 Pod 状态阶段(Phase)
Pod 的生命周期由 v1.PodStatus.Phase 字段表示,主要有以下几种状态:
| 状态 | 描述 | 转换条件 |
|---|---|---|
| Pending | Pod 已被 API Server 接收,但尚未在节点上完成调度和容器创建 | Pod 创建后进入此状态 |
| Running | Pod 已绑定到节点,所有容器已创建,至少有一个容器正在运行 | 所有容器启动成功后 |
| Succeeded | Pod 中的所有容器已成功终止,且不会重启 | RestartPolicy 为 Never 且所有容器成功退出 |
| Failed | Pod 中的所有容器已终止,至少有一个容器因失败退出 | 容器以非零状态码退出且 RestartPolicy 不是 Always |
| Unknown | 无法获取 Pod 状态,通常是由于与节点通信问题 | API Server 无法获取 Pod 状态 |
状态转换图:
stateDiagram-v2
[*] --> Pending: Pod 创建
Pending --> Running: 容器创建成功
Running --> Succeeded: 所有容器成功退出
Running --> Failed: 容器失败退出
Failed --> [*]
Succeeded --> [*]
4.1.2 Pod 生命周期管理接口
Kubelet 通过 podSyncer 接口(pkg/kubelet/kubelet.go)定义 Pod 生命周期管理的核心方法:
1 | type podSyncer interface { |
4.2 Pod 同步机制详解
Pod 同步是 Kubelet 的核心职责,确保节点上的容器状态与 API Server 中的 Pod 规范保持一致。
4.2.1 Pod Workers 架构
Pod Workers 是 Kubelet 中管理 Pod 同步的核心组件(pkg/kubelet/pod_workers.go)。每个 Pod 拥有独立的工作协程,实现了以下功能:
- FIFO 顺序处理:保证同一 Pod 的更新按接收顺序处理
- 状态机管理:跟踪 Pod 的当前状态(SyncPod/TerminatingPod/TerminatedPod)
- 并发控制:避免同一 Pod 的并发同步
PodWorkerState 定义:
1 | const ( |
4.2.2 syncPod() 方法分析
syncPod() 是 Kubelet 的核心同步方法(pkg/kubelet/kubelet.go:1942),负责将 Pod 规范同步到容器运行时。
主要流程:
flowchart TD
A[syncPod 开始] --> B{Phase 是 Succeeded/Failed?}
B -->|Yes| C[设置终止状态并返回]
B -->|No| D{Pod 是否可运行?}
D -->|No| E[更新状态为 Pending<br/>杀死运行中的容器]
D -->|Yes| F{网络插件就绪?}
F -->|No| G[返回网络未就绪错误]
F -->|Yes| H[注册 Secret/ConfigMap]
H --> I[创建/更新 Pod Cgroups]
I --> J{静态 Pod?}
J -->|Yes| K[创建/更新镜像 Pod]
J -->|No| L[创建 Pod 数据目录]
L --> M[等待卷挂载]
M --> N[添加 Pod 到探针管理器]
N --> O[调用 CRI SyncPod]
O --> P[返回结果]
关键代码路径(pkg/kubelet/kubelet.go:1942-2283):
- 状态生成:
generateAPIPodStatus()根据容器运行时状态生成 API Pod 状态 - Pod 可运行性检查:
canRunPod()检查 Pod 是否可以在节点上运行 - 资源准备:
- Secret/ConfigMap 注册
- Pod Cgroups 创建
- 镜像 Pod 管理(静态 Pod)
- 数据目录创建
- 卷挂载等待
- 容器同步:调用
containerRuntime.SyncPod()与 CRI 交互
4.2.3 Pod 状态计算
generateAPIPodStatus() 方法(pkg/kubelet/kubelet.go)负责计算 Pod 的最终状态:
1 | func (kl *Kubelet) generateAPIPodStatus(pod *v1.Pod, podStatus *kubecontainer.PodStatus, |
状态计算规则:
- 如果任何容器处于运行状态,Phase 为 Running
- 如果 Init 容器失败,Phase 为 Failed
- 如果所有容器成功退出,Phase 为 Succeeded
- 如果任何容器失败退出,Phase 为 Failed
- 否则,Phase 为 Pending
4.2.4 卷挂载处理
卷挂载是 Pod 启动的关键步骤,volumeManager.WaitForAttachAndMount() 确保所有卷在容器启动前完成挂载:
1 | // pkg/kubelet/kubelet.go:2211 |
4.3 容器运行时同步
Kubelet 通过容器运行时接口(CRI)与具体的容器运行时(containerd、CRI-O 等)交互。kuberuntimeManager.SyncPod() 是具体的实现(pkg/kubelet/kuberuntime/kuberuntime_manager.go:1066)。
4.3.1 SyncPod 流程
flowchart TD
A[SyncPod 开始] --> B[计算 Pod 操作]
B --> C{需要创建沙箱?}
C -->|Yes| D[停止旧沙箱]
C -->|No| E{需要杀死容器?}
E -->|Yes| F[杀死不需要的容器]
E -->|No| G[创建 Pod 沙箱]
D --> G
F --> G
G --> H[启动临时容器]
H --> I{有可重启 Init 容器?}
I -->|No| J[启动下一个 Init 容器]
I -->|Yes| K[启动所有可重启 Init 容器]
J --> L[启动主容器]
K --> L
L --> M[返回结果]
关键步骤(pkg/kubelet/kuberuntime/kuberuntime_manager.go):
- 计算操作:
computePodActions()确定需要执行的操作 - 沙箱管理:创建/更新/删除 Pod 沙箱
- Init 容器启动:
- 普通 Init 容器按顺序执行
- 可重启 Init 容器可以并行执行
- 主容器启动:按 Pod Spec 中的顺序启动
4.3.2 Init 容器流程
Init 容器在主容器之前运行,必须按 Pod Spec 中的顺序执行:
1 | // pkg/kubelet/kuberuntime/kuberuntime_manager.go:1290-1318 |
Init 容器失败处理:
- 普通 Init 容器失败:Pod 进入 Failed 状态
- 可重启 Init 容器失败:跳过并尝试下一个
4.4 Pod 终止流程
Pod 终止是一个复杂的多阶段过程,涉及优雅关闭、资源清理和状态上报。
4.4.1 终止阶段划分
Pod 终止分为三个主要阶段:
sequenceDiagram
participant API as API Server
participant Worker as Pod Worker
participant Kubelet as Kubelet
participant Runtime as 容器运行时
API->>Worker: 删除 Pod 请求
Worker->>Kubelet: SyncTerminatingPod
Note over Kubelet: 1. 停止探针<br/>2. 杀死容器(优雅期)<br/>3. 等待容器停止
Kubelet->>Runtime: KillContainer
Runtime-->>Kubelet: 容器已停止
Kubelet->>Worker: SyncTerminatedPod
Note over Kubelet: 1. 卸载卷<br/>2. 清理资源<br/>3. 设置最终状态
Kubelet->>API: 更新 Pod 状态
4.4.2 SyncTerminatingPod
SyncTerminatingPod()(pkg/kubelet/kubelet.go:2294)负责优雅终止 Pod:
1 | func (kl *Kubelet) SyncTerminatingPod(ctx context.Context, pod *v1.Pod, |
关键操作:
- 停止 Liveness 和 Startup 探针
- 使用优雅期(grace period)杀死容器
- 等待容器实际停止
- 清理动态资源(如有)
4.4.3 SyncTerminatedPod
SyncTerminatedPod()(pkg/kubelet/kubelet.go:2441)执行最终清理:
1 | func (kl *Kubelet) SyncTerminatedPod(ctx context.Context, pod *v1.Pod, |
4.4.4 优雅终止机制
优雅终止允许容器完成正在进行的操作并清理资源:
1 | // pkg/kubelet/kubelet.go:2322 |
优雅终止流程:
- 发送 SIGTERM 信号给容器内的主进程
- 等待优雅期(默认 30 秒)
- 如果容器未停止,发送 SIGKILL 信号强制终止
4.5 Pod 同步类型
Kubelet 处理多种类型的 Pod 同步,每种类型触发不同的处理逻辑:
| 同步类型 | 描述 | 触发时机 |
|---|---|---|
| SyncPodCreate | 新 Pod 创建 | Pod 首次被 Kubelet 接收 |
| SyncPodUpdate | Pod 规格更新 | Pod 定义发生变化 |
| SyncPodSync | 定期同步 | 定期触发或状态检查 |
| SyncPodKill | Pod 终止 | 删除请求或驱逐 |
4.6 CRI 接口(Container Runtime Interface)
CRI(Container Runtime Interface)是 Kubernetes 定义的一套标准接口,用于 Kubelet 与各种容器运行时(如 containerd、CRI-O、docker)进行通信。CRI 的设计实现了 Kubelet 与容器运行时的解耦,使得 Kubernetes 可以支持多种容器运行时而无需修改核心代码。
4.6.1 CRI 架构概述
CRI 采用 gRPC 协议进行通信,定义了两类主要的服务接口:
1 | +----------------+ gRPC +-------------------+ |
CRI 核心组件(位于 staging/src/k8s.io/cri-api/pkg/apis/services.go):
| 接口 | 职责 | 核心方法 |
|---|---|---|
| RuntimeService | 容器和沙箱管理 | CreateContainer、StartContainer、StopContainer、RunPodSandbox 等 |
| ImageManagerService | 镜像管理 | PullImage、ListImages、RemoveImage、ImageStatus 等 |
4.6.2 RuntimeService 接口
RuntimeService 是 CRI 的核心接口,定义了容器和 Pod 沙箱的完整生命周期管理。以下是主要方法分类:
沙箱管理方法(PodSandboxManager):
1 | // staging/src/k8s.io/cri-api/pkg/apis/services.go:69-85 |
容器管理方法(ContainerManager):
1 | // staging/src/k8s.io/cri-api/pkg/apis/services.go:34-65 |
统计管理方法(ContainerStatsManager):
1 | // staging/src/k8s.io/cri-api/pkg/apis/services.go:89-104 |
4.6.3 ImageManagerService 接口
ImageManagerService 接口负责容器镜像的管理,包括拉取、查询、删除镜像等操作:
1 | // staging/src/k8s.io/cri-api/pkg/apis/services.go:125-136 |
4.6.4 gRPC 通信机制
Kubelet 通过 pkg/kubelet/cri/remote 包中的远程客户端与容器运行时进行 gRPC 通信。
远程运行时服务初始化(pkg/kubelet/cri/remote/remote_runtime.go:79-131):
1 | func NewRemoteRuntimeService(endpoint string, connectionTimeout time.Duration, tp trace.TracerProvider) (internalapi.RuntimeService, error) { |
gRPC 连接配置参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
minConnectionTimeout |
5 秒 | 最小连接超时时间 |
baseBackoffDelay |
100ms | 基础退避延迟 |
maxBackoffDelay |
3 秒 | 最大退避延迟 |
maxMsgSize |
16MB | 最大消息大小 |
gRPC 通信特点:
- Unix Socket 通信:CRI 通常使用 Unix Socket(如
/var/run/containerd/containerd.sock)进行本地通信,提高性能和安全性 - 超时控制:每个 CRI 调用都设置超时,避免长时间阻塞
- 错误处理:使用 gRPC 状态码(如
codes.DeadlineExceeded、codes.Unknown)处理错误 - 追踪支持:集成 OpenTelemetry 实现分布式追踪
- 日志抑制:使用
LogReduction减少重复错误日志
4.6.5 KubeRuntimeManager 实现
KubeRuntimeManager(pkg/kubelet/kuberuntime/kuberuntime_manager.go)是 Kubelet 中 CRI 接口的具体实现。它封装了 RuntimeService 和 ImageManagerService,提供高级的容器管理功能。
核心结构(pkg/kubelet/kuberuntime/kuberuntime_manager.go:101-150):
1 | type kubeGenericRuntimeManager struct { |
KubeRuntimeManager 主要功能:
| 方法 | 职责 |
|---|---|
SyncPod |
同步 Pod 状态(创建沙箱、启动容器) |
CreatePodSandbox |
创建 Pod 沙箱 |
StartPodSandbox |
启动 Pod 沙箱 |
StopPodSandbox |
停止 Pod 沙箱 |
CreateContainer |
创建容器 |
StartContainer |
启动容器 |
StopContainer |
停止容器 |
RemoveContainer |
删除容器 |
PullImage |
拉取镜像 |
ListImages |
列出镜像 |
ListContainers |
列出容器 |
ContainerStatus |
获取容器状态 |
4.6.6 CRI 在 Pod 生命周期中的应用
CRI 接口贯穿 Pod 的整个生命周期:
1 | +------------------------------------------------------------------+ |
参考代码文件:
| 文件路径 | 说明 |
|---|---|
staging/src/k8s.io/cri-api/pkg/apis/services.go |
CRI 接口定义 |
pkg/kubelet/cri/remote/remote_runtime.go |
远程运行时服务客户端实现 |
pkg/kubelet/cri/remote/remote_image.go |
远程镜像服务客户端实现 |
pkg/kubelet/kuberuntime/kuberuntime_manager.go |
KubeRuntimeManager 实现 |
pkg/kubelet/kuberuntime/kuberuntime_sandbox.go |
沙箱管理实现 |
pkg/kubelet/kuberuntime/kuberuntime_container.go |
容器管理实现 |
参考代码文件:
/cmd/kubelet/app/server.go:135- NewKubeletCommand 命令行解析/cmd/kubelet/app/server.go:318- PreInitRuntimeService 运行时服务/cmd/kubelet/app/server.go:1207- RunKubelet 启动入口/cmd/kubelet/app/server.go:1317- CreateAndInitKubelet 创建 Kubelet/pkg/kubelet/kubelet.go:337- NewMainKubelet 创建主实例/pkg/kubelet/kubelet.go:1742- Run 主循环
5. 关键子系统详解
本章深入分析 Kubelet 的核心子系统实现,包括 PodManager、StatusManager、ProbeManager、VolumeManager、PLEG 和 EvictionManager。
5.1 PodManager
PodManager 是 Kubelet 中负责管理 Pod 元数据的核心组件,维护静态 Pod 和镜像 Pod 之间的映射关系。它是 Kubelet 与 API Server 之间的重要桥梁。
5.1.1 功能概述
PodManager 的核心职责包括:
- Pod 元数据存储:维护内存中的 Pod 索引,支持多种查询方式
- 静态 Pod 和镜像 Pod 映射:建立并维护静态 Pod 与其对应的镜像 Pod 之间的关联
- Pod 查询接口:提供多种查询方法,支持按 UID、完整名称、命名空间和名称查询
Pod 来源说明:
Kubelet 从三个来源发现 Pod 更新:
- File:本地配置文件定义的 Pod
- HTTP:通过 HTTP 端点接收的 Pod 规范
- API Server:从 Kubernetes API Server 同步的 Pod
来自非 API Server 源的 Pod 被称为静态 Pod(Static Pod),API Server 并不知道静态 Pod 的存在。为了让 API Server 能够感知静态 Pod 的状态,Kubelet 会为每个静态 Pod 创建一个镜像 Pod(Mirror Pod)。
5.1.2 接口定义
位置: pkg/kubelet/pod/pod_manager.go:30-55
1 | type Manager interface { |
5.1.3 实现原理
PodManager 的实现 (basicManager) 使用多个索引 Map 来高效存储和查询 Pod:
位置: pkg/kubelet/pod/pod_manager.go:138-158
1 | type basicManager struct { |
Pod 类型识别:
- 镜像 Pod 识别:通过检查 Pod 的 Annotations 中是否存在
mirror.kubelet.io/annotation-key注解(pkg/kubelet/types/pod_update.go:144-149) - 静态 Pod 识别:通过检查 Pod 的来源是否为 API Server(
pkg/kubelet/types/pod_update.go:153-156)
1 | // IsMirrorPod 检查是否为镜像 Pod |
索引更新逻辑:
在 updatePodsInternal 方法中(pkg/kubelet/pod/pod_manager.go:215-239),当更新 Pod 时:
- 如果是镜像 Pod:更新
mirrorPodByUID、mirrorPodByFullName和translationByUID - 如果是普通 Pod:更新
podByUID、podByFullName,并建立与镜像 Pod 的映射关系
镜像 Pod 的生命周期:
- Kubelet 为静态 Pod 在 API Server 中创建镜像 Pod
- 镜像 Pod 与静态 Pod 具有相同的名称和命名空间(但 UID 不同)
- Kubelet 使用镜像 Pod 的名称向 API Server 报告状态
- 当静态 Pod 被删除时,关联的镜像 Pod 也会被清理
5.1.4 核心方法示例
按 UID 查询 Pod:
1 | func (pm *basicManager) GetPodByUID(uid types.UID) (*v1.Pod, bool) { |
UID 转换:
1 | func (pm *basicManager) TranslatePodUID(uid types.UID) kubetypes.ResolvedPodUID { |
5.1.5 孤立镜像 Pod 检测
PodManager 提供检测孤立镜像 Pod 的能力(GetPodsAndMirrorPods 方法):
1 | func (pm *basicManager) GetPodsAndMirrorPods() (allPods []*v1.Pod, allMirrorPods []*v1.Pod, orphanedMirrorPodFullnames []string) { |
孤立镜像 Pod 出现的原因可能是:
- 静态 Pod 的配置文件被删除
- 静态 Pod 的定义发生变化,但镜像 Pod 尚未同步
5.1.6 设计要点
- 线程安全:使用
sync.RWMutex保护所有内部 Map,支持并发读操作 - 多索引设计:通过 UID、完整名称、镜像 Pod 映射等多个维度建立索引,满足不同的查询需求
- 内存存储:PodManager 只维护内存中的 Pod 索引,不持久化状态
- 只读视图:所有查询方法都返回只读指针,确保数据一致性
5.1.7 参考代码文件
| 文件路径 | 说明 |
|---|---|
pkg/kubelet/pod/pod_manager.go |
PodManager 接口定义和 basicManager 实现 |
pkg/kubelet/types/pod_update.go |
Pod 类型识别函数(IsMirrorPod、IsStaticPod) |
pkg/kubelet/container/pod.go |
GetPodFullName、BuildPodFullName 函数定义 |
staging/src/k8s.io/apimachinery/pkg/types/types.go |
UID 类型定义 |
5.2 StatusManager
StatusManager 是 Kubelet 中负责管理 Pod 状态的核心组件,承担着将 Pod 状态同步到 API Server 的关键职责。它维护 Pod 状态的本地缓存,并通过版本控制机制确保状态更新的正确性和顺序性。
5.2.1 功能概述
StatusManager 的核心职责包括:
- Pod 状态缓存:维护内存中的 Pod 状态,支持快速查询
- 状态同步到 API Server:定期将本地状态同步到 Kubernetes API Server
- 版本控制机制:通过单调递增的版本号防止过时的状态更新覆盖新状态
- 容器状态管理:管理容器的就绪状态、启动状态等
状态同步机制说明:
StatusManager 采用两种同步策略:
- 主动触发:当 Pod 状态发生变化时,立即触发同步
- 定期同步:每 10 秒进行一次全量状态检查和同步(
pkg/kubelet/status/status_manager.go:159定义syncPeriod = 10 * time.Second)
5.2.2 接口定义
位置: pkg/kubelet/status/status_manager.go:69-88
1 | // manager 结构体 |
版本化状态结构 (pkg/kubelet/status/status_manager.go:52-67):
1 | // A wrapper around v1.PodStatus that includes a version to enforce that stale pod statuses are |
Manager 接口 (pkg/kubelet/status/status_manager.go:119-157):
1 | type Manager interface { |
5.2.3 实现原理
版本控制机制:
StatusManager 使用版本号来防止过时的状态更新覆盖新的状态。每次状态更新时,版本号都会单调递增:
1 | // pkg/kubelet/status/status_manager.go:697-703 |
在 syncBatch 方法中,只有当本地版本号大于等于已发送到 API Server 的版本号时才进行同步:
1 | // pkg/kubelet/status/status_manager.go:808-811 |
状态同步流程 (pkg/kubelet/status/status_manager.go:194-235):
- 启动同步循环:在
Start()方法中启动一个 goroutine,监听两种触发条件:podStatusChannel:当 Pod 状态发生变化时收到通知syncTicker:每 10 秒触发一次定期同步
1 | func (m *manager) Start() { |
状态更新内部逻辑 (pkg/kubelet/status/status_manager.go:603-721):
- 检查容器状态转换的合法性
- 更新各条件(Conditions)的
LastTransitionTime - 设置或保持
StartTime不变 - 规范化状态(
normalizeStatus) - 如果状态未发生变化且不是强制更新,则跳过
- 创建新的版本化状态并缓存
- 触发同步
状态去重机制:
使用带缓冲的 channel(容量为 1)来避免频繁触发同步:
1 | // pkg/kubelet/status/status_manager.go:716-720 |
5.2.4 状态一致性保障
Pod 状态去重 (pkg/kubelet/status/status_manager.go:175-192):
使用 isPodStatusByKubeletEqual 函数比较状态,只有当 kubelet 管理的条件(Conditions)发生变化时才触发同步:
1 | func isPodStatusByKubeletEqual(oldStatus, status *v1.PodStatus) bool { |
容器状态转换合法性检查 (pkg/kubelet/status/status_manager.go:550-598):
防止容器从终止状态非法转换回非终止状态:
1 | func checkContainerStateTransition(oldStatuses, newStatuses *v1.PodStatus, podSpec *v1.PodSpec) error { |
5.2.5 孤立状态清理
当 Pod 被删除时,StatusManager 需要清理相关的缓存状态。RemoveOrphanedStatuses 方法扫描状态缓存,移除不再存在的 Pod 的状态:
1 | // pkg/kubelet/status/status_manager.go:750-762 |
5.2.6 Pod 终止处理
TerminatePod 方法确保在 Pod 生命周期结束时,容器状态被正确设置为终止状态,并处理缺失的容器状态信息:
1 | // pkg/kubelet/status/status_manager.go:428-492 |
5.2.7 设计要点
- 线程安全:使用
sync.RWMutex保护podStatusesMap,支持并发读操作 - 版本控制:通过单调递增的版本号防止过时的状态更新覆盖新状态
- 去重优化:使用带缓冲的 channel 避免频繁触发同步
- 定期同步:每 10 秒进行一次全量状态检查,确保最终一致性
- 状态持久化:支持将 Pod 资源分配状态 checkpoint 到磁盘,支持 Kubelet 重启后恢复
- Pod 删除安全:在删除 Pod 前确保容器已终止(通过
PodDeletionSafetyProvider接口)
5.2.8 参考代码文件
| 文件路径 | 说明 |
|---|---|
pkg/kubelet/status/status_manager.go |
StatusManager 接口定义和 manager 实现 |
pkg/kubelet/status/state/state.go |
状态持久化接口定义 |
pkg/kubelet/container/pod.go |
GetPodFullName、BuildPodFullName 函数定义 |
pkg/api/v1/pod/util.go |
Pod 工具函数 |
5.3 ProbeManager
ProbeManager 是 Kubelet 中负责管理容器健康检查探针的核心子系统。它为每个配置了探针的容器创建独立的探针工作线程,定期执行探针并缓存结果,用于更新 Pod 的 Ready 和 Started 状态。
5.3.1 功能概述
ProbeManager 主要负责以下功能:
- 探针生命周期管理:为每个容器的每种探针创建独立的工作线程(worker)
- 探针执行:定期执行 Liveness、Readiness 和 Startup 三种类型的探针
- 结果缓存:将探针结果存储在缓存中供其他组件查询
- 状态更新:根据探针结果更新 Pod 状态中的 Ready 和 Started 字段
- 指标暴露:通过 Prometheus 暴露探针执行结果和耗时指标
探针类型说明:
- Liveness Probe(存活探针):检测容器是否存活,失败时会导致容器重启
- Readiness Probe(就绪探针):检测容器是否准备好接收流量,失败时将容器从 Service 端点中移除
- Startup Probe(启动探针):检测容器是否启动完成,失败时会导致容器重启,在启动完成前会阻止 Liveness 和 Readiness 探针执行
5.3.2 接口定义
ProbeManager 接口定义如下(pkg/kubelet/prober/prober_manager.go):
1 | type Manager interface { |
关键数据结构 - probeKey 用于唯一标识容器探针:
1 | type probeKey struct { |
5.3.3 实现原理
ProbeManager 的核心实现包含以下组件:
1. Manager 结构体
1 | type manager struct { |
2. 探针执行器(prober)
prober 负责实际执行探针逻辑,支持四种探测方式:
1 | type prober struct { |
3. 工作线程(worker)
每个探针由独立的工作线程处理,主要字段包括:
1 | type worker struct { |
4. 结果管理器(results.Manager)
结果管理器提供探针结果的缓存和更新通知:
1 | type Result int |
5.3.4 三种探针类型
Startup Probe(启动探针)
- 用途:检测容器是否成功启动
- 初始值:
results.Unknown - 成功时:设置为
results.Success,允许 Liveness 和 Readiness 探针开始执行 - 失败时:容器会被重启
- 适用场景:启动较慢的容器,避免在启动期间误判为存活失败
Liveness Probe(存活探针)
- 用途:检测容器是否仍在运行
- 初始值:
results.Success(容器启动时默认为健康) - 失败时:容器会被重启
- 适用场景:检测死锁、hang 等导致容器无法继续运行的情况
- 重要:在 Pod 删除时会设置为 Success 以实现优雅关闭
Readiness Probe(就绪探针)
- 用途:检测容器是否准备好接收流量
- 初始值:
results.Failure(容器启动时默认不健康) - 成功时:容器被添加到 Service 端点
- 失败时:容器从 Service 端点中移除
- 适用场景:检测依赖外部服务、初始化未完成等情况
5.3.5 探针执行流程
AddPod 流程
- 遍历 Pod 的所有容器和可重启的 Init 容器
- 为每个配置了探针的容器创建对应的 worker
- 启动 worker 的探针循环(
go w.run())
Worker 探针循环(run 方法)
- 初始化定时器(
PeriodSeconds指定的间隔) - 如果 Kubelet 最近启动,随机等待一段时间避免探针风暴
- 进入探测循环:
- 调用
doProbe()执行单次探测 - 等待下一个探测周期或停止信号
- 调用
单次探测流程(doProbe 方法)
- 获取 Pod 最新状态
- 检查 Pod 是否已终止,已终止则退出 worker
- 获取容器状态和容器 ID
- 如果容器重启(容器 ID 变化),重置探针状态
- 检查
InitialDelaySeconds,在初始延迟期内跳过探测 - 如果存在 Startup Probe 且未成功,阻止 Liveness 和 Readiness 探针执行
- 调用
prober.probe()执行实际探测 - 根据
FailureThreshold和SuccessThreshold判断是否更新结果 - 如果 Liveness/Startup 探针失败,设置
onHold=true暂停后续探测
探针探测方式
- Exec Probe:在容器内执行命令,检查退出码
- HTTP Get Probe:发送 HTTP GET 请求,检查状态码(2xx 为成功)
- TCP Socket Probe:尝试建立 TCP 连接,成功则通过
- gRPC Probe:使用 gRPC Health Check 协议
UpdatePodStatus 流程
- 遍历所有容器状态
- 检查容器是否已启动(通过 Startup Probe 结果)
- 如果容器已启动,检查 Readiness Probe 结果设置 Ready 状态
- 如果容器未启动,设置 Started=false,Ready=false
- 对于 readiness 探针,如果探针尚未执行,触发立即探测
5.3.6 设计要点
- 线程安全:使用
sync.RWMutex保护 workers Map,支持并发读操作 - 独立工作线程:每种探针类型独立运行,避免相互影响
- 启动屏障:Startup Probe 成功前,Liveness 和 Readiness 探针不会执行
- 重试机制:探针失败会重试最多 3 次(
maxProbeRetries = 3) - 防抖动设计:使用
FailureThreshold和SuccessThreshold避免临时波动导致误判 - 优雅关闭:Pod 删除时设置 Liveness 和 Startup 探针结果为 Success
- 指标暴露:通过 Prometheus 暴露探针总次数和耗时指标
- 容器重启处理:容器重启后自动重置探针状态,避免使用旧的容器 ID
5.3.7 参考代码文件
| 文件路径 | 说明 |
|---|---|
pkg/kubelet/prober/prober_manager.go |
ProbeManager 接口定义和 manager 实现 |
pkg/kubelet/prober/prober.go |
探针执行器实现 |
pkg/kubelet/prober/worker.go |
探针工作线程实现 |
pkg/kubelet/prober/results/results_manager.go |
探针结果缓存管理 |
pkg/probe/exec/exec.go |
Exec 探针实现 |
pkg/probe/http/http.go |
HTTP 探针实现 |
pkg/probe/tcp/tcp.go |
TCP 探针实现 |
pkg/probe/grpc/grpc.go |
gRPC 探针实现 |
5.4 VolumeManager
5.4.1 功能概述
VolumeManager 是 Kubelet 中负责管理存储卷(Volume)的核心组件。它的主要职责是根据调度到当前节点的 Pod 的需求,异步地完成卷的挂载(Mount)、卸载(Unmount)、附加(Attach)和分离(Detach)操作。VolumeManager 确保 Pod 所需的存储卷在容器启动前准备就绪,并在 Pod 删除后正确清理。
VolumeManager 的核心设计理念是维护两个状态世界:
- 期望状态(Desired State of World):根据当前调度的 Pod,应该存在哪些卷
- 实际状态(Actual State of World):当前节点上实际附加和挂载的卷
通过不断调和这两个状态的差异,VolumeManager 确保存储卷的实际状态与 Pod 需求保持一致。
5.4.2 接口定义
1 | // VolumeManager 定义了卷管理器的核心接口 |
5.4.3 核心组件架构
VolumeManager 由以下核心组件构成:
DesiredStateOfWorld(期望状态缓存)
- 存储应该附加到当前节点的卷以及引用这些卷的 Pod
- 由 DesiredStateOfWorldPopulator 定期更新
- 文件位置:
pkg/kubelet/volumemanager/cache/desired_state_of_world.go
ActualStateOfWorld(实际状态缓存)
- 存储当前节点上实际附加的卷以及已成功挂载的 Pod
- 在 attach、detach、mount、unmount 操作成功完成后更新
- 文件位置:
pkg/kubelet/volumemanager/cache/actual_state_of_world.go
DesiredStateOfWorldPopulator(期望状态填充器)
- 定期扫描 PodManager 获取当前调度的 Pod
- 将 Pod 所需的卷添加到期望状态
- 处理 CSI 迁移(将 in-tree 卷规范转换为 CSI 规范)
- 文件位置:
pkg/kubelet/volumemanager/populator/desired_state_of_world_populator.go
Reconciler(调和器)
- 定期运行,比较期望状态和实际状态
- 触发 attach、detach、mount、unmount 操作来消除差异
- 执行顺序:先卸载 -> 再挂载/附加 -> 最后分离设备
- 文件位置:
pkg/kubelet/volumemanager/reconciler/reconciler.go
OperationExecutor(操作执行器)
- 实际执行卷的附加、分离、挂载、卸载操作
- 提供操作重试、超时控制、错误处理机制
- 位于
pkg/volume/util/operationexecutor/
5.4.4 卷挂载/卸载流程
卷挂载流程(Mount):
1 | 1. DesiredStateOfWorldPopulator 检测到新 Pod 调度到节点 |
卷卸载流程(Unmount):
1 | 1. Pod 被删除或卷不再需要 |
关键时间常量:
reconcilerLoopSleepPeriod:100ms - Reconciler 循环执行间隔desiredStateOfWorldPopulatorLoopSleepPeriod:100ms - 期望状态填充间隔podAttachAndMountTimeout:2分3秒 - Pod 挂载超时时间waitForAttachTimeout:10分钟 - 卷附加超时时间
5.4.5 CSI 驱动集成
VolumeManager 通过以下机制支持 CSI(Container Storage Interface)驱动:
CSI 迁移(CSI Migration)
- 将传统的 in-tree 卷规范转换为 CSI 规范
- 使用
csimigration.PluginManager判断是否需要迁移 - 使用
intreeToCSITranslator执行规范转换
CSI 插件发现
- 通过 CSIDriver informer 动态发现集群中的 CSI 驱动
- 启动时运行
volumePluginMgr.Run(stopCh)启动 CSI 驱动 informer
卷生命周期管理
- CSI 卷通过 CSI 驱动实现 Attach/Mount/Detach/Unmount
- OperationExecutor 调用 CSI 插件的具体方法完成操作
CSI 迁移处理流程:
1 | // 在 desired_state_of_world_populator.go 中 |
5.4.6 卷状态管理
VolumeManager 维护详细的卷状态信息:
卷状态类型
VolumeMounted:卷已成功挂载到 PodVolumeMountUncertain:卷可能正在挂载中(用于处理重建场景)VolumeUnmounted:卷已从 Pod 卸载DeviceGloballyMounted:设备已全局挂载DeviceMountUncertain:设备可能已全局挂载
状态存储
- 期望状态:记录哪些卷应该被挂载
- 实际状态:记录哪些卷实际已被挂载
- 错误状态:记录挂载过程中的错误信息
卷使用报告
GetVolumesInUse()返回当前使用的所有卷- 用于更新 Node.Status.VolumesInUse
- 通过
MarkVolumesAsReportedInUse()标记已报告的卷
5.4.7 设计要点
- 异步操作:所有卷操作都是异步执行的,不会阻塞主线程
- 幂等性:挂载/卸载操作设计为可重复执行,支持重试
- 错误恢复:通过定期调和机制自动恢复失败的操作
- 状态一致性:
- 卸载先于挂载执行,避免同一卷被多个 Pod 争用
- 设备分离前确保所有 Pod 都已卸载卷
- 重建处理:Kubelet 重启后,Reconciler 会尝试重建卷的实际状态
- CSI 迁移透明性:对用户来说,CSI 迁移是无缝的,规范转换在内部完成
- SELinux 支持:支持 SELinux 上下文配置,确保安全策略正确应用
5.4.8 参考代码文件
| 文件路径 | 说明 |
|---|---|
pkg/kubelet/volumemanager/volume_manager.go |
VolumeManager 接口定义和主要实现 |
pkg/kubelet/volumemanager/cache/desired_state_of_world.go |
期望状态缓存实现 |
pkg/kubelet/volumemanager/cache/actual_state_of_world.go |
实际状态缓存实现 |
pkg/kubelet/volumemanager/populator/desired_state_of_world_populator.go |
期望状态填充器实现 |
pkg/kubelet/volumemanager/reconciler/reconciler.go |
调和器核心实现 |
pkg/volume/util/operationexecutor/operation_executor.go |
操作执行器实现 |
pkg/volume/csimigration/plugin_manager.go |
CSI 迁移管理器 |
pkg/volume/csimigration/translator.go |
In-tree 到 CSI 规范转换器 |
5.5 EvictionManager
5.5.1 功能概述
EvictionManager 是 Kubelet 的资源压力管理组件,负责在节点资源(内存、磁盘、PID)不足时主动驱逐 Pod 以保障节点稳定性。当节点资源达到配置的驱逐阈值时,EvictionManager 会按照一定的策略选择并终止低优先级 Pod,释放被占用的资源。
EvictionManager 的核心功能包括:
- 资源监控:持续监控节点的内存、磁盘空间、inode 和 PID 资源使用情况
- 阈值评估:判断当前资源使用是否达到配置的驱逐阈值
- 节点级回收:在驱逐 Pod 之前,优先尝试节点级资源回收(如镜像垃圾回收、容器垃圾回收)
- Pod 驱逐:当节点级回收无法满足需求时,按照优先级排序驱逐 Pod
- 节点状态管理:更新节点条件(如 MemoryPressure、DiskPressure)供调度器参考
5.5.2 接口定义
1 | // Manager evaluates when an eviction threshold for node stability has been met on the node. |
核心配置结构:
1 | type Config struct { |
驱逐信号类型(定义于 pkg/kubelet/eviction/api/types.go):
| 信号 | 描述 |
|---|---|
memory.available |
可用内存(capacity - workingSet),单位字节 |
allocatableMemory.available |
Pod 可分配的内存(allocatable - workingSet) |
nodefs.available |
Kubelet 使用的文件系统可用空间 |
nodefs.inodesFree |
Kubelet 使用的文件系统可用 inode |
imagefs.available |
容器运行时存储镜像的文件系统可用空间 |
imagefs.inodesFree |
容器运行时存储镜像的文件系统可用 inode |
containerfs.available |
容器可写层文件系统可用空间 |
containerfs.inodesFree |
容器可写层文件系统可用 inode |
pid.available |
可用进程 ID 数量 |
5.5.3 驱逐阈值配置
阈值配置示例:
1 | # Kubelet 配置 |
阈值配置说明:
- evictionHard:硬驱逐阈值,一旦触发立即执行驱逐
- evictionSoft:软驱逐阈值,需要配合 GracePeriod 使用,达到阈值后等待指定时间才执行驱逐
- evictionMinimumReclaim:最小回收量,驱逐后需要确保回收的资源量
- 阈值支持两种格式:
- 绝对值:
memory.available: "100Mi" - 百分比:
nodefs.available: "10%"
- 绝对值:
5.5.4 驱逐决策算法
EvictionManager 的核心决策流程如下(pkg/kubelet/eviction/eviction_manager.go:synchronize):
1. 资源观测:
1 | // 从 cAdvisor 获取节点资源统计信息 |
2. 阈值评估:
1 | // 判断哪些阈值已被满足(忽略宽限期) |
3. 节点条件更新:
1 | // 根据触发的阈值更新节点条件 |
4. 节点级资源回收(在驱逐 Pod 之前):
1 | // 尝试通过镜像垃圾回收和容器垃圾回收来释放资源 |
5. Pod 驱逐执行:
1 | // 按优先级排序阈值 |
驱逐决策关键点:
- 每个驱逐周期(默认 10 秒)最多驱逐一个 Pod
- 优先处理内存压力,然后是其他资源压力
- 驱逐 Pod 后会等待 Pod 资源清理完成再继续下一个周期
5.5.5 Pod 驱逐顺序
EvictionManager 根据不同的资源压力类型使用不同的排序策略:
1. 内存压力驱逐顺序(rankMemoryPressure):
1 | func rankMemoryPressure(pods []*v1.Pod, stats statsFunc) { |
排序优先级:
- 是否超过内存请求:先驱逐超过内存请求的 Pod
- Pod 优先级:优先级低的 Pod 优先被驱逐
- 内存使用量:超过请求量越大的 Pod 优先被驱逐
2. 磁盘压力驱逐顺序(rankDiskPressureFunc):
1 | func rankDiskPressureFunc(fsStatsToMeasure []fsStatsType, diskResource v1.ResourceName) rankFunc { |
排序优先级:
- 是否超过磁盘请求
- Pod 优先级
- 磁盘使用量
3. PID 压力驱逐顺序(rankPIDPressure):
1 | func rankPIDPressure(pods []*v1.Pod, stats statsFunc) { |
排序优先级:
- Pod 优先级
- 进程数量
Pod QoS 级别对驱逐的影响:
| QoS 级别 | 说明 | 驱逐优先级 |
|---|---|---|
| Guaranteed | 所有容器都有 CPU/内存 limits | 最低 |
| Burstable | 至少一个容器有 CPU/内存 requests | 中等 |
| BestEffort | 没有设置任何 requests/limits | 最高 |
特殊保护:
- Critical Pod:标记为 Critical 的静态 Pod 不会被驱逐
- PodDisruptionConditions:启用后,驱逐的 Pod 会获得 DisruptionTarget 条件,说明被驱逐的原因
5.5.6 本地存储驱逐
除了节点级资源压力,EvictionManager 还处理 Pod 级别的本地存储驱逐:
1 | // 本地存储驱逐检查 |
5.5.7 设计要点
- 优雅驱逐:支持配置驱逐宽限期,让 Pod 有时间完成清理工作
- 节点级回收优先:在驱逐 Pod 之前先尝试镜像和容器垃圾回收,减少对业务的影响
- 压力转换周期:节点离开压力状态需要等待一段时间,避免资源波动导致的频繁驱逐
- Critical Pod 保护:系统关键 Pod(如 kube-proxy、dns)不会被驱逐
- 调度器协同:通过节点条件(MemoryPressure、DiskPressure)通知调度器,避免新 Pod 被调度到压力节点
- 事件记录:驱逐事件会记录到 API Server,便于问题排查和监控
5.5.8 参考代码文件
| 文件路径 | 说明 |
|---|---|
pkg/kubelet/eviction/eviction_manager.go |
EvictionManager 接口定义和主要实现 |
pkg/kubelet/eviction/types.go |
Manager 接口和类型定义 |
pkg/kubelet/eviction/helpers.go |
驱逐阈值评估、Pod 排序等辅助函数 |
pkg/kubelet/eviction/api/types.go |
驱逐信号、阈值等 API 类型定义 |
pkg/kubelet/eviction/memory_threshold_notifier.go |
内存阈值通知器实现(基于 cgroup) |
5.6 Pod 生命周期事件生成器(PLEG)
Pod Lifecycle Event Generator(PLEG)是 Kubelet 的核心组件之一,负责检测 Pod 和容器的状态变化,并生成相应的事件通知给 Pod Worker 进行处理。PLEG 解决了 Kubelet 与容器运行时之间的状态同步问题,是实现 Pod 生命周期管理的关键机制。
5.6.1 PLEG 架构概述
PLEG 的核心功能是定期轮询容器运行时,获取当前 Pod 和容器的状态,并与内部缓存进行比较,从而检测状态变化并生成事件。这种设计使得 Kubelet 能够及时感知容器状态的变化,并触发相应的处理逻辑。
PLEG 在 Kubelet 架构中的位置十分关键:
1 | ┌─────────────────────────────────────────────────────────────────┐ |
PLEG 通过以下接口定义与 Kubelet 其他组件交互:
1 | // pkg/kubelet/pleg/pleg.go |
5.6.2 事件类型定义
PLEG 定义了以下几种生命周期事件类型:
1 | // pkg/kubelet/pleg/pleg.go |
每个事件包含以下信息:
1 | type PodLifecycleEvent struct { |
5.6.3 Generic PLEG 实现
Generic PLEG 是 PLEG 的基础实现,它通过定期轮询(relist)容器运行时来检测状态变化。这种设计简单可靠,适用于各种容器运行时。
核心数据结构:
1 | // pkg/kubelet/pleg/generic.go |
轮询机制(Relist):
Generic PLEG 的核心是 Relist() 方法,它定期执行以下步骤:
- 获取所有 Pod:调用容器运行时接口获取当前运行的 Pod 和容器列表
- 状态比较:将当前状态与上一次记录的状态进行比较
- 事件生成:根据状态变化生成相应的事件
- 缓存更新:更新内部缓存和 Pod 状态
- 事件发送:将事件发送到事件通道
1 | // pkg/kubelet/pleg/generic.go |
状态转换与事件生成:
Generic PLEG 使用以下容器状态模型:
1 | type plegContainerState string |
状态转换时会生成相应的事件:
1 | // pkg/kubelet/pleg/generic.go |
健康检查:
PLEG 提供了健康检查机制,用于监控 PLEG 是否正常工作:
1 | // pkg/kubelet/pleg/generic.go |
5.6.4 Evented PLEG 实现
Evented PLEG 是 Generic PLEG 的增强版本,它利用容器运行时的事件接口(CRI 事件流)来实现实时事件通知,减少了轮询带来的延迟和资源消耗。
架构设计:
Evented PLEG 通过订阅容器运行时的事件流来实现实时事件处理:
1 | // pkg/kubelet/pleg/evented.go |
事件处理流程:
Evented PLEG 的事件处理流程如下:
- 建立事件流:通过
GetContainerEvents接口订阅容器事件 - 事件解析:将 CRI 事件转换为 PLEG 事件
- 状态更新:更新缓存和生成指标
- 事件发送:将事件发送到事件通道
1 | // pkg/kubelet/pleg/evented.go |
CRI 事件类型映射:
Evented PLEG 将 CRI 事件类型映射为 PLEG 事件:
1 | // pkg/kubelet/pleg/evented.go |
降级机制:
Evented PLEG 具备完善的降级机制,当事件流出现问题时会自动回退到 Generic PLEG:
1 | // pkg/kubelet/pleg/evented.go |
全局缓存时间更新:
Evented PLEG 定期更新缓存的全局时间戳,以防止 Pod Worker 在等待状态更新时死锁:
1 | // pkg/kubelet/pleg/evented.go |
5.6.5 Generic PLEG 与 Evented PLEG 对比
| 特性 | Generic PLEG | Evented PLEG |
|---|---|---|
| 实现方式 | 定期轮询 | 事件流订阅 |
| 响应延迟 | 受轮询周期影响(默认1秒) | 实时响应 |
| 资源消耗 | 定期全量查询 | 仅处理变化事件 |
| 可靠性 | 高(无外部依赖) | 依赖运行时事件接口 |
| 回退机制 | 无 | 降级到 Generic PLEG |
| 适用场景 | 所有环境 | 支持 CRI 事件流的运行时 |
性能对比:
- Generic PLEG:每次轮询需要遍历所有 Pod 和容器,在大规模集群中可能产生显著开销
- Evented PLEG:仅处理变化的事件,大幅降低 CPU 和网络开销,事件响应延迟从秒级降低到毫秒级
5.6.6 事件处理流程
PLEG 生成的事件最终由 Pod Worker 处理,完整的处理流程如下:
1 | ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ |
- 容器运行时检测到容器状态变化
- PLEG通过轮询或事件流获取变化
- PLEG比较状态差异,生成事件
- Pod Worker接收事件,触发同步
- Pod Worker执行同步操作(创建/删除/更新容器)
- Status Manager将状态同步到 API Server
5.6.7 设计要点与最佳实践
事件可靠性:
- Generic PLEG 假设容器不会在一个轮询周期内被创建、终止和垃圾回收
- 如果发生这种情况,PLEG 会错过事件,因此轮询周期不宜过长
缓存一致性:
- PLEG 与 Pod Worker 通过缓存进行通信
- Evented PLEG 使用 CRI 事件中的时间戳来保证缓存时间戳的准确性
健康监控:
- PLEG 定期更新
PLEGLastSeen指标,供监控系统检测 PLEG 是否正常工作 - 可以配置告警:
time() - plef_last_seen_seconds > nn
- PLEG 定期更新
性能优化:
- 使用 Evented PLEG 可以显著降低事件处理延迟
- 定期轮询仍然作为降级方案和指标更新的保障
容器状态追踪:
- PLEG 追踪所有容器和沙箱(Sandbox)的状态变化
- 对于已退出的容器,保留其状态作为记录(tombstone)
5.6.8 参考代码文件
| 文件路径 | 说明 |
|---|---|
pkg/kubelet/pleg/pleg.go |
PLEG 接口定义、Generic PLEG 实现 |
pkg/kubelet/pleg/generic.go |
Generic PLEG 详细实现(轮询机制) |
pkg/kubelet/pleg/evented.go |
Evented PLEG 实现(事件流机制) |
pkg/kubelet/kubelet.go |
PLEG 在 Kubelet 中的初始化和调用 |
pkg/kubelet/container/runtime.go |
容器运行时接口定义 |
6. 边界情况与错误处理
Kubelet 在运行过程中会遇到各种异常情况,包括容器运行时故障、网络问题、资源不足等。本章分析 Kubelet 的错误处理机制和恢复策略。
6.1 常见错误场景
6.1.1 容器运行时不可用
当容器运行时(Container Runtime)不可用时,Kubelet 需要优雅地处理这种情况:
错误定义(pkg/kubelet/errors.go):
1 | const ( |
启动等待机制(pkg/kubelet/kubelet.go):
1 | const ( |
Kubelet 在启动时会等待容器运行时就绪,最长等待 30 秒。如果容器运行时在 120 秒内未就绪,会记录非 verbose 日志警告。
6.1.2 API Server 连接失败
Kubelet 需要与 API Server 保持通信以获取 Pod 规范和上报节点状态。当连接失败时:
节点状态重试机制(pkg/kubelet/kubelet_node_status.go):
1 | const ( |
Kubelet 在上报节点状态失败时会重试 5 次,每次重试之间有时间间隔(由客户端库控制),确保在临时网络故障时不会立即失败。
6.1.3 资源不足
当节点资源(内存、磁盘、CPU)不足时,Kubelet 的 EvictionManager 会触发资源回收:
驱逐监控周期(pkg/kubelet/kubelet.go):
1 | const ( |
EvictionManager 每 10 秒检查一次资源使用情况,当达到驱逐阈值时会按优先级顺序驱逐 Pod。
6.1.4 网络问题
网络不可用是常见错误场景,Kubelet 对网络问题有特殊处理:
Pod 同步时的网络错误处理(pkg/kubelet/pod_workers.go):
1 | case strings.Contains(syncErr.Error(), NetworkNotReadyErrorMsg): |
当 Pod 同步失败且错误信息包含 “network is not ready” 时,Kubelet 会使用较短的后退间隔(1 秒)进行重试,因为网络可能很快就会恢复。
6.2 恢复机制
6.2.1 重试策略
Kubelet 采用多种重试策略来处理不同类型的错误:
基础后退周期(pkg/kubelet/kubelet.go):
1 | const ( |
Pod Worker 中的错误处理(pkg/kubelet/pod_workers.go):
1 | const ( |
| 错误类型 | 后退周期 | 最大重试时间 |
|---|---|---|
| 网络未就绪 | 1 秒 | 取决于网络恢复 |
| 容器运行时错误 | 10 秒 | 5 分钟 |
| 其他同步错误 | 10 秒 | 5 分钟 |
6.2.2 指数退避
Kubelet 使用指数退避算法来避免频繁重试:
1 | // wait.Jitter 函数实现 |
通过添加随机抖动(jitter),Kubelet 避免了多个 Pod 同时重试导致的”惊群效应”(thundering herd)。
6.2.3 状态恢复
Kubelet 具备完整的状态恢复能力,确保在故障恢复后能够继续正常工作:
Housekeeping 周期清理(pkg/kubelet/kubelet.go):
1 | const ( |
Housekeeping 每 2 秒执行一次,负责:
- 清理已终止的 Pod
- 清理孤儿容器
- 同步期望状态与实际状态
PLEG 状态同步(pkg/kubelet/pleg/generic.go):
1 | const ( |
PLEG 每秒轮询一次容器状态,如果超过 3 分钟未收到任何事件,会触发告警。这确保了即使在事件丢失的情况下,状态也能在合理时间内被同步。
6.3 错误处理最佳实践
区分临时错误和永久错误:
- 临时错误(如网络抖动)使用短后退间隔
- 永久错误(如配置错误)记录日志但不持续重试
资源限制保护:
- 使用驱逐阈值保护节点资源
- 优先驱逐低优先级 Pod
监控和告警:
- 监控
PLEGLastSeen指标 - 监控节点状态更新失败次数
- 监控容器重启次数
- 监控
优雅关闭:
- Kubelet 关闭时先停止接收新 Pod
- 等待现有 Pod 优雅终止后再退出
6.4 参考代码文件
| 文件路径 | 说明 |
|---|---|
pkg/kubelet/errors.go |
错误定义常量 |
pkg/kubelet/kubelet.go |
超时和重试常量定义 |
pkg/kubelet/pod_workers.go |
Pod 同步重试逻辑 |
pkg/kubelet/kubelet_node_status.go |
节点状态上报重试 |
pkg/kubelet/eviction/eviction_manager.go |
资源驱逐管理 |
pkg/kubelet/pleg/generic.go |
PLEG 轮询机制 |
7. 性能优化
Kubelet 作为节点上的核心组件,其性能直接影响 Pod 的启动速度、资源利用率和系统稳定性。本章分析关键性能指标、性能瓶颈以及优化策略。
7.1 关键性能指标
Kubelet 暴露了丰富的性能指标,可通过 Prometheus 监控进行采集和分析。以下是核心性能指标:
7.1.1 Pod 启动延迟
Pod 启动延迟是衡量 Kubelet 性能的最重要指标之一。Kubelet 暴露了多个相关指标:
| 指标名称 | 描述 | 监控重要性 |
|---|---|---|
kubelet_pod_start_duration_seconds |
Pod 从被 Kubelet 首次发现到容器启动的总时间 | 高 |
kubelet_pod_start_sli_duration_seconds |
Pod 启动 SLI(排除镜像拉取时间) | 高 |
kubelet_pod_start_total_duration_seconds |
Pod 完整启动时间(含镜像拉取) | 高 |
kubelet_pod_worker_start_duration_seconds |
Kubelet 发现 Pod 到启动 Worker 的时间 | 中 |
kubelet_pod_worker_duration_seconds |
单个 Pod 同步操作的耗时,按操作类型分类 | 中 |
SLO 参考:
- 99% Pod 应在 30 秒内完成启动(排除大镜像拉取)
- 99% Pod 应在 15 分钟内完成完整启动(含镜像拉取)
7.1.2 状态同步延迟
Pod 状态同步延迟影响 Kubernetes 控制平面对 Pod 状态的感知:
| 指标名称 | 描述 |
|---|---|
kubelet_pod_status_sync_duration_seconds |
Pod 状态从生成到成功同步到 API Server 的耗时 |
kubelet_pleg_relist_duration_seconds |
PLEG 重新列出容器状态所花费的时间 |
kubelet_pleg_relist_interval_seconds |
PLEG 两次 relist 操作之间的时间间隔 |
kubelet_pleg_last_seen_seconds |
PLEG 最后一次活动的 Unix 时间戳 |
7.1.3 运行时操作延迟
容器运行时操作的性能直接影响 Pod 生命周期管理:
| 指标名称 | 描述 |
|---|---|
kubelet_runtime_operations_duration_seconds |
运行时操作耗时,按操作类型分类 |
kubelet_runtime_operations_total |
运行时操作总数,按类型统计 |
kubelet_runtime_operations_errors_total |
运行时操作错误数 |
kubelet_run_podsandbox_duration_seconds |
Pod Sandbox 创建耗时 |
7.2 核心性能常量
Kubelet 代码中定义了多个关键的时间常量,直接影响性能行为(定义于 pkg/kubelet/kubelet.go):
1 | // 清理任务周期 |
关键性能影响:
housekeepingPeriod(2秒)决定了 Pod 状态同步、探针执行等关键任务的频率genericPlegRelistPeriod(1秒)影响容器状态变化的检测延迟eventedPlegRelistPeriod(300秒)使用事件驱动模式时大幅降低 CPU 占用
7.3 性能优化策略
7.3.1 并行处理优化
Kubelet 内部采用多种并行处理机制提升吞吐量:
Pod Worker 池:
- 每个 Pod 分配独立的 Worker 进行处理
- Worker 之间相互独立,可并行处理不同 Pod
- 通过
--max-pods配置控制节点最大 Pod 数量,避免过载
容器操作并行化:
- 同一 Pod 内的多个容器可并行创建
- 镜像拉取支持并行下载
- Volume 挂载操作可与其他容器操作并行进行
批量状态更新:
- 批量同步多个 Pod 状态到 API Server
- 减少 API Server 请求次数和网络开销
代码位置:pkg/kubelet/pod_workers.go
7.3.2 缓存策略优化
Kubelet 使用多层缓存减少重复计算和 API Server 交互:
Pod 缓存:
- 本地维护 Pod 状态缓存
- 减少对 API Server 的 List 请求
- 通过 Informer 机制保持缓存与 API Server 一致
运行时状态缓存:
runtimeCacheRefreshPeriod= 3秒- 缓存容器运行时状态,减少与容器运行时交互
- Housekeeping 任务显式刷新缓存
镜像缓存:
- 预拉取常用镜像
- 本地镜像仓库加速拉取
代码位置:pkg/kubelet/pod/pod_manager.go、pkg/kubelet/container/runtime.go
7.3.3 批量操作优化
Pod 批量处理:
- Watch 事件触发时,批量处理多个 Pod 变化
- 减少同步次数和锁竞争
状态批量上报:
- 节点状态信息批量上报
- 使用 Node Lease 机制降低心跳频率
运行时操作批量化:
- 合并多个容器操作请求
- 减少与容器运行时的 RPC 调用次数
7.3.4 Evented PLEG 优化
Evented PLEG 是 Kubernetes 1.22 引入的重要优化,通过事件流机制替代传统轮询:
对比 Generic PLEG:
| 特性 | Generic PLEG | Evented PLEG |
|---|---|---|
| 轮询周期 | 1秒 | 300秒(作为后备) |
| CPU 占用 | 较高 | 极低 |
| 事件延迟 | ~1秒 | 实时 |
| 可靠性 | 高 | 依赖 CRI 流接口 |
启用方式:
- 需要容器运行时支持事件流接口(containerd 1.6+、CRI-O 等)
- 通过 Kubelet 配置
--evented-pleg-enabled=true启用 - 自动降级机制:当事件流断开时回退到 Generic PLEG
监控指标:
kubelet_evented_pleg_connection_success_count:成功建立事件流连接次数kubelet_evented_pleg_connection_error_count:事件流连接错误次数kubelet_evented_pleg_connection_latency_seconds:事件流连接延迟
7.4 性能调优参数
以下是影响 Kubelet 性能的关键配置参数:
7.4.1 Pod 调度相关
| 参数 | 默认值 | 说明 | 调优建议 |
|---|---|---|---|
--max-pods |
110 | 节点最大 Pod 数量 | 根据节点资源调整 |
--max-container-pods |
- | 最大容器数量(已废弃) | 使用 max-pods |
--pod-per-core |
- | 每个 CPU 核心的 Pod 限制 | 超大规模集群优化 |
7.4.2 资源管理相关
| 参数 | 默认值 | 说明 | 调优建议 |
|---|---|---|---|
--kube-reserved |
- | 为系统预留的资源 | 建议设置以保障系统稳定性 |
--system-reserved |
- | 为系统进程预留的资源 | 建议设置以避免资源耗尽 |
--eviction-hard |
memory.available<100Mi | 硬驱逐阈值 | 根据工作负载调整 |
7.4.3 运行时相关
| 参数 | 默认值 | 说明 | 调优建议 |
|---|---|---|---|
--runtime-request-timeout |
2m | 运行时请求超时 | 网络不稳定时增大 |
--image-pull-progress-deadline |
1m | 镜像拉取超时 | 大镜像时增大 |
--image-gc-high-threshold |
85% | 镜像垃圾回收阈值 | 根据磁盘空间调整 |
--image-gc-low-threshold |
80% | 镜像垃圾回收下限 | - |
7.5 性能问题诊断
7.5.1 高延迟问题排查
当发现 Pod 启动延迟较高时,可按以下步骤排查:
检查 PLEG 状态:
1
2
3
4
5# 查看 PLEG relist 延迟
kubectl get --raw "/api/v1/nodes/{node}/proxy/metrics" | grep pleg_relist_duration
# 查看 PLEG 最后活动时间
kubectl get --raw "/api/v1/nodes/{node}/proxy/metrics" | grep pleg_last_seen检查 Pod Worker 队列:
- 查看是否有大量 Pod 等待处理
- 检查 Pod Worker 耗时分布
检查运行时操作:
- 查看镜像拉取耗时
- 查看容器创建/启动耗时
检查资源压力:
- 查看节点 CPU、内存使用率
- 检查是否存在资源争用
7.5.2 高 CPU 占用排查
当 Kubelet CPU 占用较高时:
检查 PLEG 轮询频率:
- Generic PLEG 默认 1 秒轮询
- 确认是否启用了 Evented PLEG
检查 Housekeeping 任务:
- 每 2 秒执行一次清理任务
- 确认是否有大量 Pod 状态变化
检查探针执行:
- 大量探针会增加 CPU 负担
- 优化探针配置减少不必要的检查
检查镜像 GC:
- ImageGCPeriod = 5 分钟
- 确认镜像缓存策略是否合理
7.5.3 内存泄漏排查
可能导致内存泄漏的组件:
Pod 缓存:
- 大量历史 Pod 信息未清理
- 检查 pod_workers.go 中的缓存策略
容器状态缓存:
- 退出的容器状态未及时清理
- 检查 PLEG 的 tombstone 机制
运行时缓存:
- 容器运行时状态缓存未刷新
- 检查 runtimeCacheRefreshPeriod 设置
7.6 参考代码文件
| 文件路径 | 说明 |
|---|---|
pkg/kubelet/kubelet.go |
性能相关常量定义 |
pkg/kubelet/metrics/metrics.go |
性能指标定义 |
pkg/kubelet/pod_workers.go |
Pod Worker 并行处理 |
pkg/kubelet/pleg/generic.go |
Generic PLEG 实现 |
pkg/kubelet/pleg/evented.go |
Evented PLEG 实现 |
pkg/kubelet/pod/pod_manager.go |
Pod 缓存管理 |
pkg/kubelet/container/runtime.go |
运行时缓存 |
9. 面试题与详细解答
问题 1:Kubelet 的启动流程分为哪几个阶段?每个阶段主要做什么?
回答要点:
Kubelet 启动五个阶段:
1 | 1. 命令行解析 → 2. 依赖初始化 → 3. 运行时服务初始化 → 4. Kubelet 实例创建 → 5. 服务启动 |
详细说明:
| 阶段 | 主要工作 | 关键代码位置 |
|---|---|---|
| 命令行解析 | 解析参数、加载配置文件 | cmd/kubelet/app/server.go |
| 依赖初始化 | 获取主机名、构建 EventRecorder | RunKubelet() |
| 运行时服务初始化 | 连接 CRI Runtime 和 Image Service | PreInitRuntimeService() |
| Kubelet 实例创建 | 初始化所有 Manager(Pod/Status/Probe/Volume/Eviction/PLEG) | NewMainKubelet() |
| 服务启动 | 启动各 Manager 和主循环 syncLoop | k.Run() |
关键启动顺序:
1 | // pkg/kubelet/kubelet.go |
为什么这个顺序很重要:
- VolumeManager 先启动:确保卷可用
- RuntimeUp 先检查:确保容器运行时可用
- PLEG 在 syncLoop 前:确保能检测容器变化
- syncLoop 最后:开始处理 Pod 事件
问题 2:Kubelet 的 syncLoop 是如何工作的?它是如何保证 Pod 状态与期望一致的?
回答要点:
syncLoop 核心设计:
syncLoop 是 Kubelet 的主循环,采用事件驱动 + 定期同步的方式确保 Pod 状态与期望一致。
事件来源:
1 | // pkg/kubelet/kubelet.go |
事件处理流程:
1 | 事件入队 → Pod Worker 分发 → syncPod/syncTerminatingPod/syncTerminatedPod |
syncPod 核心逻辑:
1 | func (kl *Kubelet) syncPod(...) { |
状态一致性保障:
- 定期同步:Housekeeping 每 2 秒执行一次
- PLEG 事件:容器状态变化立即触发同步
- 幂等操作:syncPod 可重复执行
- 状态对比:只在实际状态变化时更新
关键代码位置:
- 主循环:
pkg/kubelet/kubelet.go:syncLoop() - Pod Worker:
pkg/kubelet/pod_workers.go - syncPod:
pkg/kubelet/kubelet.go:syncPod()
问题 3:PLEG(Pod Lifecycle Event Generator)的作用是什么?Generic PLEG 和 Evented PLEG 有什么区别?
回答要点:
PLEG 的作用:
- 检测容器状态变化:通过轮询或事件流获取容器状态
- 生成生命周期事件:ContainerStarted/ContainerDied/ContainerRemoved
- 触发 Pod 同步:将事件发送给 Pod Worker 处理
Generic PLEG vs Evented PLEG:
| 特性 | Generic PLEG | Evented PLEG |
|---|---|---|
| 实现方式 | 定期轮询(1秒) | 事件流订阅 |
| CPU 占用 | 较高 | 极低 |
| 事件延迟 | ~1秒 | 实时(毫秒级) |
| 可靠性 | 高 | 依赖 CRI 事件接口 |
| 回退机制 | 无 | 自动回退到 Generic |
Generic PLEG 工作原理:
1 | // pkg/kubelet/pleg/generic.go |
Evented PLEG 工作原理:
1 | // pkg/kubelet/pleg/evented.go |
启用 Evented PLEG:
1 | # kubelet 配置 |
降级机制:
1 | Evented PLEG 事件流断开 |
问题 4:Kubelet 如何处理 Pod 的优雅终止(Graceful Termination)?完整流程是什么?
回答要点:
优雅终止完整流程:
1 | 1. API Server 收到 DELETE 请求 |
三个终止状态:
1 | // pkg/kubelet/pod_workers.go |
SyncTerminatingPod 处理:
1 | func (kl *Kubelet) SyncTerminatingPod(...) { |
优雅终止时间计算:
1 | effectiveGracePeriod = min( |
最佳实践:
1 | # Pod 配置 |
preStop 钩子执行时机:
1 | 1. 收到终止请求 |
问题 5:Kubelet 的 ProbeManager 如何管理三种探针?它们的初始值和失败行为有什么区别?
回答要点:
三种探针对比:
| 探针类型 | 初始值 | 失败行为 | 用途 |
|---|---|---|---|
| Startup | Unknown | 容器重启 | 检测启动完成 |
| Liveness | Success | 容器重启 | 检测存活状态 |
| Readiness | Failure | 从 Service 移除 | 检测就绪状态 |
初始值差异的原因:
- Startup 初始 Unknown:启动完成前不知道状态
- Liveness 初始 Success:假设容器启动时是健康的,避免立即重启
- Readiness 初始 Failure:假设容器启动时未就绪,避免过早接收流量
探针执行流程:
1 | // pkg/kubelet/prober/worker.go |
探针类型影响:
1 | # Startup Probe - 保护慢启动应用 |
Startup Probe 屏蔽效果:
1 | Startup Probe 运行中 |
问题 6:Kubelet 的 StatusManager 是如何将 Pod 状态同步到 API Server 的?如何保证状态一致性?
回答要点:
StatusManager 核心功能:
- 缓存 Pod 状态:维护 podStatuses Map
- 去重更新:避免重复发送相同状态
- 版本控制:防止旧状态覆盖新状态
- 定期同步:每 10 秒全量同步一次
状态同步流程:
1 | // pkg/kubelet/status/status_manager.go |
状态去重机制:
1 | func isPodStatusByKubeletEqual(oldStatus, status *v1.PodStatus) bool { |
版本控制:
1 | func (m *manager) updateStatusInternal(pod *v1.Pod, status v1.PodStatus, forceUpdate bool) { |
一致性保障措施:
- 线程安全:使用 sync.RWMutex 保护缓存
- 版本递增:每次更新版本号 +1
- 定期同步:确保最终一致性
- 重试机制:API Server 更新失败时重试
关键代码位置:
- StatusManager:
pkg/kubelet/status/status_manager.go - 状态生成:
pkg/kubelet/kubelet.go:generateAPIPodStatus()
问题 7:如何调优 Kubelet 性能以支持高密度 Pod 部署?
回答要点:
性能调优维度:
- 并发控制
- 缓存优化
- PLEG 选择
- 资源限制
关键配置参数:
1 | # kubelet 配置 |
高密度部署最佳实践:
启用 Evented PLEG:
- 减少 CPU 占用
- 降低事件延迟
优化镜像拉取:
1
2
3registryPullQPS: 20
registryBurst: 50
serializeImagePulls: false增加资源预留:
1
2
3kubeReserved:
cpu: "1"
memory: "4Gi" # 高密度时需要更多调整驱逐阈值:
1
2evictionHard:
memory.available: "1Gi" # 留更多缓冲
性能监控指标:
1 | # Pod 启动延迟 |
调优验证:
1 | # 压测:快速创建大量 Pod |
性能问题排查:
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
| Pod 启动慢 | 镜像拉取慢 | 配置镜像预热、并行拉取 |
| CPU 占用高 | PLEG 轮询频繁 | 启用 Evented PLEG |
| 内存占用高 | Pod 数量多 | 增加资源预留 |
| 状态同步慢 | API Server 压力 | 检查网络和 API Server |
8. 最佳实践
本章提供 Kubelet 配置、调优和故障排查的最佳实践建议,帮助运维人员构建稳定、高性能的 Kubernetes 集群。
8.1 配置建议
正确的 Kubelet 配置是确保节点稳定运行的基础。以下是生产环境的推荐配置。
8.1.1 资源预留
生产环境中必须为系统组件和 Kubernetes 本身预留资源,避免因资源不足导致节点不稳定。
核心配置参数:
| 参数 | 说明 | 推荐值 |
|---|---|---|
--kube-reserved |
为 Kubernetes 系统组件预留的资源 | cpu=100m,memory=1Gi,ephemeral-storage=1Gi |
--system-reserved |
为系统进程预留的资源 | cpu=100m,memory=512Mi,ephemeral-storage=1Gi |
--allocatable |
节点可分配资源(自动计算) | kube-reserved + system-reserved |
配置示例:
1 | # kubelet-config.yaml |
资源预留计算原则:
CPU 预留:
- 小型集群(< 50 节点):每节点预留 0.1-0.2 核心
- 大型集群(> 50 节点):每节点预留 0.2-0.5 核心
内存预留:
- 基础系统:512Mi - 1Gi
- Kubelet 自身:1Gi(处理大量 Pod 时)
- 容器运行时:2-4Gi(containerd、CRI-O 等)
存储预留:
- 操作系统日志:5-10Gi
- 镜像存储:20-50Gi
- 临时存储:10-20Gi
8.1.2 驱逐阈值配置
驱逐(Eviction)是 Kubelet 保护节点的关键机制,当资源不足时主动回收资源。
硬驱逐 vs 软驱逐:
| 类型 | 触发条件 | 行为 |
|---|---|---|
| 硬驱逐 | 资源低于硬阈值 | 立即终止 Pod,无宽限期 |
| 软驱逐 | 资源低于软阈值持续一段时间 | 有宽限期,可等待 Pod 优雅终止 |
推荐驱逐配置:
1 | # 硬驱逐阈值 - 立即触发 |
驱逐优先级:
Kubelet 按以下顺序驱逐 Pod:
- BestEffort Pod(无资源请求)
- Burstable Pod(低于请求量的资源请求)
- Guaranteed Pod(完全指定资源请求)
最佳实践:
- 设置
memory.available至少为 500Mi-1Gi - 监控驱逐事件频率,如果频繁驱逐考虑扩容
- 使用 PriorityClass 控制关键 Pod 的驱逐顺序
8.1.3 日志配置
合理的日志配置对于问题排查和性能优化至关重要。
Kubelet 日志级别:
| 级别 | 数值 | 使用场景 |
|---|---|---|
| 0 | ERROR | 生产环境最小日志 |
| 1 | WARNING | 生产环境推荐 |
| 2 | INFO | 一般问题排查 |
| 4 | DEBUG | 深度调试 |
| 6 | TRACE | 详细跟踪 |
1 | # 生产环境推荐配置 |
日志输出格式:
1 | # 使用 JSON 格式便于日志收集 |
容器日志配置:
1 | # 容器日志轮转 |
日志收集建议:
- 将 Kubelet 日志输出到标准输出,由容器运行时管理
- 配置日志轮转,避免磁盘空间耗尽
- 使用集中式日志收集系统(如 Loki、ELK)
8.2 调优指南
针对不同工作负载场景,可以通过调整参数优化 Kubelet 性能。
8.2.1 并发参数
Pod 并发控制:
| 参数 | 默认值 | 说明 | 调整建议 |
|---|---|---|---|
--max-pods |
110 | 节点最大 Pod 数量 | 根据节点规格调整,高密度场景可增大 |
--pod-per-core |
- | 每 CPU 核心最大 Pod | 超大规模集群使用 |
--pods-per-core |
- | 每核心 Pod 限制 | 替代 pod-per-core |
1 | # 高密度部署示例 |
Pod Worker 配置:
| 参数 | 默认值 | 说明 |
|---|---|---|
--num-workers |
- | Pod Worker 数量,默认自动计算 |
--serialize-image-pulls |
true | 串行拉取镜像,建议保持 |
并发优化策略:
- Pod 启动并发:高频率 Pod 创建场景可提高并发度
- 镜像拉取:配置私有镜像仓库加速
- 卷挂载:使用 CSI 驱动优化
8.2.2 缓存大小
运行时缓存:
1 | # 运行时状态缓存刷新周期 |
镜像缓存配置:
1 | # 镜像垃圾回收阈值 |
内存优化:
--low-diskspace-threshold-mb:低磁盘空间阈值,默认 256MB- 增大缓存可减少与容器运行时和 API Server 的交互,但会增加内存使用
8.2.3 超时设置
运行时超时:
| 参数 | 默认值 | 说明 | 调整建议 |
|---|---|---|---|
--runtime-request-timeout |
2m | 运行时请求超时 | 网络不稳定时增大到 5m |
--image-pull-progress-deadline |
1m | 镜像拉取超时 | 大镜像时增大到 5m |
--default-timeout |
- | 默认容器操作超时 | - |
1 | # 大镜像场景 |
健康检查超时:
1 | # 健康检查配置 |
优雅终止:
1 | # Pod 优雅终止超时 |
8.3 故障排查指南
本节提供常见 Kubelet 问题的排查方法和调试技巧。
8.3.1 日志分析
关键日志关键词:
| 关键词 | 含义 | 排查方向 |
|---|---|---|
Failed to pull image |
镜像拉取失败 | 检查镜像地址、网络、凭证 |
network is not ready |
网络未就绪 | 检查 CNI 插件状态 |
Eviction manager: attempting to reclaim |
触发驱逐 | 检查节点资源 |
PLEG is not healthy |
PLEG 不健康 | 检查容器运行时 |
Failed to create pod sandbox |
沙箱创建失败 | 检查 Runtime 配置 |
日志查看方法:
1 | # 查看 Kubelet 日志(systemd) |
日志级别调试:
1 | # 临时提高日志级别 |
8.3.2 状态检查
节点状态检查:
1 | # 查看节点状态 |
Kubelet 端点检查:
1 | # 健康检查 |
关键指标检查:
1 | # PLEG 健康状态 |
8.3.3 调试技巧
Pod 调试流程:
确认 Pod 状态:
1
2kubectl get pod {pod-name} -o wide
kubectl describe pod {pod-name}查看 Pod 事件:
1
kubectl get events --field-selector involvedObject.name={pod-name}
检查容器日志:
1
2kubectl logs {pod-name} -c {container}
kubectl logs {pod-name} -c {container} --previous # 之前的容器进入容器调试:
1
kubectl exec -it {pod-name} -c {container} -- /bin/sh
Kubelet 问题诊断:
Kubelet 未启动:
1
2
3
4
5
6
7
8# 检查 Kubelet 服务状态
systemctl status kubelet
# 检查启动日志
journalctl -u kubelet -xe
# 检查配置文件
kubelet --config=/etc/kubernetes/kubelet.conf --validate节点 NotReady:
1
2
3
4
5
6
7
8
9
10# 检查 Kubelet 日志
journalctl -u kubelet -f | grep -i error
# 检查容器运行时
crictl info
crictl pods
# 检查 CNI 插件
ip link
cat /etc/cni/net.d/*Pod 无法启动:
1
2
3
4
5
6
7
8
9
10# 查看 Kubelet 中的 Pod 状态
curl -s localhost:10250/pods | jq
# 检查镜像是否可拉取
crictl images
crictl pull {image}
# 检查存储空间
df -h
docker system df驱逐频繁发生:
1
2
3
4
5
6
7
8
9
10# 查看驱逐事件
kubectl get events --field-selector reason=Eviction
# 检查节点资源
kubectl top nodes
kubectl describe nodes | grep -A 10 "Allocated resources"
# 检查磁盘使用
df -h /var/lib/containerd
df -h /var/lib/kubelet
调试工具:
crictl - 容器运行时调试:
1
2
3
4crictl info # 查看运行时信息
crictl ps -a # 查看所有容器
crictl logs {id} # 查看容器日志
crictl inspect {id} # 查看容器详情cAdvisor - 资源监控:
1
curl localhost:4194/metrics
Kubelet 性能分析:
1
2
3
4
5# 启用 pprof
curl -s localhost:10250/debug/pprof/
# 获取堆内存分析
curl -s localhost:10250/debug/pprof/heap > heap.prof
8.4 生产环境配置示例
以下是生产环境推荐的完整 Kubelet 配置:
1 | # /etc/kubernetes/kubelet-config.yaml |
8.5 参考资料
- Kubelet Configuration Reference
- Resource Management for Pods and Containers
- Configuring Out of Resource Handling
- Kubelet CRI Configuration

