Kubelet 状态与事件回传流程

目标

这篇文档只回答一个核心问题:kubelet 如何把节点侧观察到的 Pod 运行结果回传给控制面?

重点放在状态回传与事件上报两条通信链路的区别,以及控制器真正依赖的观察面是什么。

一句话摘要

kubelet 通过 statusManager 异步批量把 PodStatus 同步到 API Server,并通过事件记录器上报诊断性 Event;控制器主要观察的是 API Server 中资源对象的状态变化,而不是 kubelet 内部事件流本身。


1. 流程总览

从通信视角看,这条链路包含两条并行但职责不同的子链路:

  1. 状态回传链路:kubelet 把 Pod 运行状态写回 API Server,成为控制面的共享事实。
  2. 事件上报链路:kubelet 把诊断性 Event 写入 API Server,用于排障与审计。

这两条链路共享 API Server 作为存储,但消费方和语义完全不同。

Mermaid:总览图

flowchart LR
    A[Kubelet subsystems] --> B[statusManager]
    A --> C[EventRecorder]
    B --> D[API Server PodStatus]
    C --> E[API Server Events]
    D --> F[Controllers watch Pods]
    E --> G[Humans / monitoring tools]

状态与事件双链路架构图

flowchart TB
    subgraph 状态产生["状态产生源"]
        direction TB
        PW[podWorkers<br/>容器状态]
        PM[probeManager<br/>健康检查]
        PLEG[PLEG<br/>生命周期事件]
        VM[volumeManager<br/>卷状态]
    end

    subgraph 状态链路["状态回传链路 (控制面输入)"]
        direction TB
        SM[statusManager<br/>状态缓存]
        SYNC[syncBatch<br/>批量同步]
        PATCH[PATCH /status<br/>API 调用]
    end

    subgraph 事件链路["事件上报链路 (诊断面输入)"]
        direction TB
        ER[EventRecorder<br/>事件记录]
        EB[EventBroadcaster<br/>批量上报]
        POST[POST Events<br/>API 调用]
    end

    subgraph API层["API Server"]
        API[kube-apiserver]
        ETCD[(etcd)]
    end

    subgraph 消费者["消费方"]
        direction TB
        CTRL[Controllers<br/>驱动控制逻辑]
        KUBECTL[kubectl describe<br/>调试工具]
        MON[监控系统<br/>Prometheus/ELK]
    end

    PW --> SM
    PM --> SM
    PLEG --> SM
    VM --> SM

    PW -.->|异常事件| ER
    PM -.->|探针失败| ER
    PLEG -.->|容器死亡| ER

    SM --> SYNC
    SYNC --> PATCH
    PATCH --> API

    ER --> EB
    EB --> POST
    POST --> API

    API <--> ETCD

    API -->|watch Pod| CTRL
    API -->|读取 Event| KUBECTL
    API -->|读取 Event| MON

StatusManager 内部工作机制图

flowchart TD
    subgraph 输入["状态输入"]
        I1[SetPodStatus]
        I2[SetContainerReadiness]
        I3[TerminatePod]
    end

    subgraph 缓存层["本地缓存"]
        PS["podStatuses<br>map UID versionedPodStatus"]
        ASV["apiStatusVersions<br>已同步版本"]
        CH["podStatusChannel<br>变更通知"]
    end

    subgraph 同步循环["同步循环"]
        START[Start goroutine]
        BATCH[syncBatch]
        CHECK{需要同步}
        BUILD[构建 PATCH 请求]
        SEND[发送到 API Server]
        RETRY{成功}
        UPDATE[更新 apiStatusVersions]
        WAIT[等待下一周期]
    end

    I1 --> PS
    I2 --> PS
    I3 --> PS
    PS --> CH

    START --> BATCH
    CH --> BATCH
    BATCH --> CHECK
    CHECK -->|是| BUILD
    CHECK -->|否| WAIT
    BUILD --> SEND
    SEND --> RETRY
    RETRY -->|是| UPDATE
    RETRY -->|否| WAIT
    UPDATE --> WAIT
    WAIT --> BATCH

这张图想表达什么

  • 状态是控制面的输入,事件是诊断面的输入。
  • 控制器主要看对象状态,而不是事件流。
  • 状态同步是异步批量的,有本地缓存和版本控制机制。

2. 分阶段通信流程


阶段一:kubelet 内部产生状态变更

这个阶段的本质是:各个子系统把观察到的运行结果汇总到 statusManager。

步骤 1.1:podWorkers 完成一次 SyncPod

当 podWorkers 完成一次 SyncPod 后,它会得到容器的创建/启动结果。这些结果需要被反映到 PodStatus 中。

通信方向:podWorkers → statusManager(通过 SetPodStatus)

步骤 1.2:probe manager 执行健康检查

probeManager 定期执行 liveness/readiness/startup 探针。当探针结果变化时(例如从成功变失败),它会更新对应的 probe result manager。

通信方向:probeManager → livenessManager/readinessManager/startupManager

步骤 1.3:probe result 触发状态更新

当探针结果变化时,会触发 statusManager 的状态更新。例如,readiness 探针失败会导致 Pod 的 Ready 条件变为 False

通信方向:probe result manager → statusManager

步骤 1.4:PLEG 检测到容器变化

PLEG(Pod Lifecycle Event Generator)定期 relist 容器状态,检测到变化时会生成 Pod 生命周期事件,触发 Pod 同步,间接导致状态更新。

通信方向:PLEG → podWorkers → statusManager

步骤 1.5:statusManager 缓存新状态

statusManager.SetPodStatus(...) 把新状态写入本地缓存 podStatuses。此时状态还没有写入 API Server,只是 kubelet 内部视图更新了。

通信方向:statusManager 内部 map 更新


阶段二:statusManager 异步同步到 API Server

这个阶段的本质是:把本地缓存的状态批量写回控制面。

步骤 2.1:statusManager 启动同步循环

statusManager.Start() 启动一个 goroutine,定期或在收到状态变更信号时执行 syncBatch(...)

通信方向:statusManager 内部循环

步骤 2.2:判断是否需要同步

syncBatch 会检查每个 Pod 的状态是否有变化,以及是否到达同步间隔。只有需要同步的 Pod 才会被处理。

通信方向:statusManager 内部判断

步骤 2.3:构造 Patch/Update 请求

对于需要同步的 Pod,statusManager 会构造一个 Patch 或 Update 请求,只更新 status 子资源,而不是整个 Pod 对象。

通信方向:statusManager 内部构造

步骤 2.4:发送到 API Server

statusManager 通过 client-go 向 API Server 发送请求。API Server 校验权限后更新 Pod 的 status 字段。

通信方向:statusManager → API Server(HTTP PATCH/PUT)

步骤 2.5:处理同步失败

如果同步失败(例如网络问题、冲突),statusManager 会保留该 Pod 在缓存中,等待下一个周期重试。

通信方向:statusManager 内部错误处理

步骤 2.6:更新同步版本

同步成功后,statusManager 会记录当前的 apiStatusVersions,用于后续判断是否需要再次同步。

通信方向:statusManager 内部状态更新


阶段三:事件上报并行发生

这个阶段的本质是:除了状态,kubelet 还会并行上报诊断性事件。

步骤 3.1:子系统检测到需要记录的事件

当容器启动失败、探针失败、镜像拉取失败等情况发生时,相关子系统会调用 EventRecorder 记录事件。

通信方向:各子系统 → EventRecorder

步骤 3.2:EventRecorder 构造 Event 对象

EventRecorder 会构造一个 v1.Event 对象,包含:

  • 事件类型(Normal/Warning)
  • 原因(Reason)
  • 消息(Message)
  • 相关对象引用(InvolvedObject)

通信方向:EventRecorder 内部构造

步骤 3.3:EventBroadcaster 批量上报

EventBroadcaster 会收集多个事件,批量发送到 API Server。这样可以减少 API 调用次数。

通信方向:EventBroadcaster → API Server(HTTP POST)

步骤 3.4:事件存储在 API Server

API Server 收到事件后,会存储在 etcd 中。事件有 TTL(默认 1 小时),过期后自动清理。

通信方向:API Server → etcd

步骤 3.5:事件对控制器的影响

控制器通常不消费事件流作为控制输入。事件主要用于:

  • kubectl describe 显示
  • 日志收集系统
  • 告警系统

通信方向:事件是只读的诊断信息


小结:这条链路的 2 条通信链路

链路 目的 消费方 是否驱动控制逻辑
PodStatus 同步 把运行状态变成控制面事实 控制器
Event 上报 记录诊断信息 人类/监控系统 否(通常)

核心结论:控制器主要观察 API Server 中的对象状态,而不是消费事件流。


2.5 完整流程图

下面这张图把本文所有步骤串在一起,方便一眼看完整个通信链路:

flowchart TD
    subgraph 状态产生["状态产生"]
        A1[podWorkers SyncPod 完成] --> A2[容器状态]
        A2 --> A3[statusManager.SetPodStatus]
        A4[probeManager 探针] --> A5[liveness/readiness/startup 结果]
        A5 --> A6[probe result manager]
        A6 --> A3
        A7[PLEG relist] --> A8[Pod 生命周期事件]
        A8 --> A9[podWorkers]
        A9 --> A3
        A3 --> A10[本地缓存 podStatuses]
    end

    subgraph 状态同步["状态同步"]
        A10 --> B1[syncBatch 循环]
        B1 --> B2{需要同步?}
        B2 -->|是| B3[构造 Patch 请求]
        B2 -->|否| B4[等待下一周期]
        B3 --> B5[发送到 API Server]
        B5 --> B6{成功?}
        B6 -->|是| B7[更新 apiStatusVersions]
        B6 -->|否| B8[保留缓存等待重试]
    end

    subgraph 事件上报["事件上报"]
        C1[子系统检测事件] --> C2[EventRecorder.Eventf]
        C2 --> C3[构造 Event 对象]
        C3 --> C4[EventBroadcaster 批量]
        C4 --> C5[POST 到 API Server]
        C5 --> C6[存储到 etcd]
        C6 --> C7[kubectl describe 显示]
    end

    subgraph 控制器观察["控制器观察"]
        B5 --> D1[API Server Pod 更新]
        D1 --> D2[controller informer]
        D2 --> D3[本地缓存更新]
        D3 --> D4[触发 reconcile]
    end

2.6 与其他组件的交互

这条链路不只是 kubelet 内部的事,还涉及多个外部系统的协作:

与各类 Controller 的交互

不同控制器会消费 PodStatus 来驱动各自的收敛逻辑:

控制器 关注的 PodStatus 字段 做什么
ReplicaSet Controller phase, podIP 管理 Pod 生命周期
Deployment Controller conditions[Ready] 控制滚动更新
Service Controller podIP, ready 更新 Endpoint
Job Controller phase, containerStatuses 跟踪任务完成

关键理解:控制器通过 informer/watch 观察 Pod 对象,而不是被 kubelet 主动通知。

与监控系统的交互

Event 是监控系统的重要输入:

flowchart LR
    A[EventRecorder] --> B[API Server]
    B --> C[Event 对象]
    C --> D[Prometheus Event Exporter]
    C --> E[ELK/日志系统]
    C --> F[告警系统]
系统 如何消费 Event
Prometheus 通过 Event Exporter 转换为指标
ELK 直接索引 Event 对象
告警系统 监听 Warning 类型 Event

关键理解:Event 有 TTL(默认 1 小时),不适合作为长期存储。

与 kubectl describe 的交互

kubectl describe pod 会聚合显示 Pod 的状态和事件:

flowchart TD
    A[kubectl describe] --> B[读取 Pod 对象]
    B --> C[显示 PodStatus]
    A --> D[查询相关 Event]
    D --> E[按时间排序]
    E --> F[显示事件列表]

关键理解:Event 的 involvedObject 字段关联到具体 Pod,方便查询。

与 PLEG(Pod Lifecycle Event Generator)的交互

PLEG 是状态产生的重要来源:

flowchart LR
    A[PLEG] --> B[定期 relist 容器]
    B --> C[对比上次状态]
    C --> D{有变化?}
    D -->|是| E[生成 PodLifecycleEvent]
    E --> F[podWorkers]
    F --> G[触发 SyncPod]
    G --> H[statusManager]

关键理解:PLEG 不是直接写 API Server,而是通过触发 podWorkers 间接导致状态更新。

与探针系统的交互

探针结果直接影响 PodStatus 中的条件:

flowchart TD
    A[容器启动成功] --> B[probeManager]
    B --> C{liveness 探针}
    B --> D{readiness 探针}
    B --> E[startup 探针]
    C --> F[livenessManager]
    D --> G[readinessManager]
    E --> H[startupManager]
    F --> I[statusManager]
    G --> I
    H --> I
    I --> J[更新 Pod conditions]
探针类型 影响的条件 失败后果
liveness 无直接条件 容器重启
readiness Ready 从 Service 移除
startup Initialized 阻塞其他探针

关键理解:探针结果先写入 probe result manager,再通过 statusManager 同步到 API Server。

交互总结表

外部系统 交互方式 影响什么
各类 Controller informer/watch Pod 驱动控制逻辑
监控系统 读取 Event 指标、告警
kubectl describe 读取 Pod + Event 诊断信息
PLEG 生成事件触发 SyncPod 状态产生
探针系统 写入 probe result manager Pod 条件

3. 关键时序图

Mermaid:主时序图

sequenceDiagram
    participant Subsys as Kubelet Subsystems
    participant StatusMgr as statusManager
    participant APIServer
    participant EventRecorder
    participant Controller

    Subsys->>StatusMgr: SetPodStatus(...)
    StatusMgr->>StatusMgr: cache new status
    StatusMgr->>APIServer: syncBatch patch/update PodStatus
    Subsys->>EventRecorder: Eventf(...)
    EventRecorder->>APIServer: create Event object
    APIServer-->>Controller: watch Pod status changes
    Controller->>Controller: reconcile based on object state

时序图里的 3 个关键点

  • statusManager 有本地缓存,状态同步是异步批量的。
  • Event 与 PodStatus 是两条独立的写入路径。
  • 控制器通过 watch 对象状态来驱动收敛,而不是消费 Event 流。

4. 异常与分支路径

Mermaid:异常/分支路径图

flowchart TD
    A[status change observed] --> B[SetPodStatus to local cache]
    B --> C{syncBatch triggered?}
    C -->|Yes| D[patch/update PodStatus to API Server]
    D --> E{write succeeds?}
    E -->|No| F[retry in next syncBatch cycle]
    E -->|Yes| G[controllers observe new state]
    C -->|No| H[wait for next tick or event]
    I[probe failure / container crash] --> J[EventRecorder.Eventf]
    J --> K[write Event to API Server]
    K --> L[visible in kubectl describe / logs]

读这张图时要关注什么

  • 状态同步失败:会在下一个 syncBatch 周期重试,而不是立即丢弃。
  • Event 写入失败:通常不会影响控制逻辑,只是诊断信息丢失。
  • 控制器观察的是对象状态:即使 Event 丢失,控制器仍然可以根据 PodStatus 做出正确决策。

5. 这个流程里谁负责什么

组件 主要职责 不负责什么
statusManager 缓存 PodStatus 并异步同步到 API Server 不直接驱动控制逻辑
EventRecorder 上报诊断性 Event 不是控制面的主输入
kube-apiserver 持久化 PodStatus 和 Event 不执行控制器逻辑
controllers 通过 informer/watch 观察对象状态 不消费 Event 作为主控制输入

6. 常见误解

误解 1:控制器主要靠 Event 驱动控制逻辑

不是。Event 更偏向诊断信号,主观察面通常是 Pod/Node 等对象状态。

误解 2:每次状态变化都同步阻塞写 API Server

不是。statusManager 有本地缓存与批量异步同步机制。

误解 3:kubelet 内部事件流 = 控制面对象状态

不是。对外一致性语义主要体现为 API Server 中的对象状态,而不是 kubelet 内部事件流。


7. 版本差异说明

这条链路在 Kubernetes 1.27/1.28/1.29 中有一些值得注意的变化:

1.27 → 1.28 变化

变化点 说明
Job Pod Failure Policy (Beta) Job 控制器可以更精细地根据 Pod 失败条件决定是否重试
Indexed Job 改进 Indexed Job 的状态回传更准确
Event API 改进 Event 的 regarding 字段支持更多对象类型

1.28 → 1.29 变化

变化点 说明
终止 Pod IP 字段修复 修复了终止态 Pod 的 PodIP 字段可能被临时移除的问题
探针超时处理改进 startup 探针超时后的状态传播更准确
Event TTL 可配置 通过 --event-ttl 可以配置 Event 保留时间

向后兼容性

版本 兼容性注意
1.27+ Job Pod Failure Policy 需要更新 Job API 使用方式
1.28+ Event API 变化对老客户端透明
1.29+ 终止 Pod IP 修复可能影响依赖该行为的逻辑

需要关注的废弃

废弃项 替代方案 移除版本
--event-burst 默认值变更 调整为 10000 1.28 已变更
Event firstTimestamp/lastTimestamp 使用 eventTimeseries 计划移除

7. 与后续文档的关系

建议按这个顺序阅读:

  1. docs/pod-on-node-realization-flow.md
  2. 本文:理解运行结果如何回到控制面
  3. docs/node-heartbeat-and-health-flow.md:理解节点健康信号如何影响控制面判断

8. 代码入口(精简版)

如果读者想从流程跳回实现,可从下面几个入口开始:


9. 详细时序图

9.1 状态同步完整时序图

sequenceDiagram
    autonumber
    participant PW as podWorkers
    participant PR as probeManager
    participant PLEG as PLEG
    participant SM as statusManager
    participant CACHE as 本地缓存
    participant API as kube-apiserver
    participant CTRL as Controller

    Note over PW,SM: 状态产生源

    par 并行状态更新
        PW->>SM: SetPodStatus(pod, status)
        PW->>CACHE: 写入 podStatuses[UID]

        PR->>PR: 执行探针
        PR->>SM: SetContainerReadiness(pod, containerID, ready)

        PLEG->>PLEG: relist 容器
        PLEG->>PW: PodLifecycleEvent
        PW->>SM: SetPodStatus(pod, status)
    end

    Note over SM: 同步循环

    loop 每 syncBatch 周期
        SM->>CACHE: 遍历 podStatuses
        SM->>SM: 检查是否需要同步

        alt 状态有变化 或 到达同步间隔
            SM->>SM: 构造 PATCH 请求
            SM->>API: PATCH /api/v1/namespaces/<ns>/pods/<pod>/status

            alt 同步成功
                API-->>SM: 200 OK
                SM->>SM: 更新 apiStatusVersions
            else 同步失败 (冲突)
                API-->>SM: 409 Conflict
                SM->>SM: 保留在缓存,下周期重试
            else 同步失败 (网络)
                API-->>SM: Error
                SM->>SM: 保留在缓存,下周期重试
            end
        else 无需同步
            SM->>SM: 跳过
        end
    end

    Note over API,CTRL: Controller 感知状态变化

    API-->>CTRL: watch 推送 Pod UPDATE
    CTRL->>CTRL: 更新本地缓存
    CTRL->>CTRL: 触发 reconcile 逻辑

9.2 事件上报时序图

sequenceDiagram
    autonumber
    participant SUBSYS as Kubelet 子系统
    participant ER as EventRecorder
    participant EB as EventBroadcaster
    participant API as kube-apiserver
    participant ETCD as etcd
    participant USER as 用户/监控

    Note over SUBSYS: 检测到需要记录的事件

    SUBSYS->>ER: Eventf(regarding, related, eventtype, reason, message)

    ER->>ER: 构造 Event 对象
    Note over ER: Event{<br/>  Type: Normal/Warning,<br/>  Reason: "Started",<br/>  Message: "...",<br/>  InvolvedObject: Pod<br/>}

    ER->>EB: Action(events channel)

    Note over EB: 批量聚合

    loop 批量发送
        EB->>API: POST /api/v1/namespaces/<ns>/events
        API->>ETCD: 存储 Event
        ETCD-->>API: 确认
        API-->>EB: 201 Created
    end

    Note over API,USER: 事件消费

    par 并行消费
        USER->>API: kubectl describe pod
        API-->>USER: 返回 Pod + Events
    and
        USER->>API: kubectl get events
        API-->>USER: 返回 Event 列表
    and
        MONITOR->>API: watch events
        API-->>MONITOR: 实时推送 Events
    end

9.3 探针状态传播时序图

sequenceDiagram
    autonumber
    participant CTR as 容器
    participant PR as probeManager
    participant LM as livenessManager
    participant RM as readinessManager
    participant SM as statusManager
    participant API as kube-apiserver
    participant SVC as Service/Endpoint

    Note over PR: 定期执行探针

    loop 每个探针周期
        PR->>CTR: HTTP GET /healthz (或 TCP/Exec)

        alt 探针成功
            CTR-->>PR: 200 OK
            PR->>RM: Set(containerID, results.Success)
        else 探针失败
            CTR-->>PR: Timeout/Error
            PR->>RM: Set(containerID, results.Failure)
        end
    end

    Note over RM: 探针结果变化

    RM->>SM: 触发状态更新
    SM->>SM: 更新 Pod conditions[Ready]

    alt Ready=True
        SM->>API: PATCH Pod status
        API-->>SVC: watch 触发
        SVC->>SVC: 添加 Pod IP 到 Endpoint
    else Ready=False
        SM->>API: PATCH Pod status
        API-->>SVC: watch 触发
        SVC->>SVC: 从 Endpoint 移除 Pod IP
    end

    Note over LM: Liveness 失败处理

    alt Liveness 探针持续失败
        LM->>LM: 记录失败次数
        LM->>LM: 超过阈值
        LM->>SM: 触发容器重启
        SM->>SM: 更新 Pod status
    end

10. 故障排查指南

10.1 常见问题与诊断方法

问题 1:Pod 状态与实际容器状态不一致

症状kubectl get pod 显示 Running,但容器实际已退出

排查流程图

flowchart TD
    A[状态不一致] --> B{检查 PLEG}

    B --> B1[PLEG relist 正常?]
    B1 -->|否| C[PLEG 问题]
    C --> C1[检查 relist 错误日志]
    C --> C2[检查 CRI 连接]

    B1 -->|是| D{检查 statusManager}
    D --> D1[检查 syncBatch 是否执行]
    D1 --> D2[检查 API 同步错误]

    D --> E{检查网络}
    E --> E1[检查与 API Server 连接]
    E --> E2[检查权限]

    A --> F{手动触发同步}
    F --> F1[删除 Pod 触发重建]
    F --> F2[重启 kubelet]

诊断命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 检查 Pod 实际状态
kubectl get pod <pod-name> -n <namespace> -o yaml

# 检查节点上容器状态
crictl ps -a | grep <pod-name>
crictl inspect <container-id>

# 检查 kubelet 状态同步日志
journalctl -u kubelet | grep -E "(status|syncBatch)"

# 检查 PLEG relist
journalctl -u kubelet | grep -i pleg

# 检查 API Server 连接
curl -k https://<api-server>:6443/api/v1/namespaces/<ns>/pods/<pod>

问题 2:事件丢失或延迟

症状kubectl describe pod 看不到预期的事件

排查流程图

flowchart TD
    A[事件丢失/延迟] --> B{检查 Event 对象}

    B --> B1[检查 Event 是否存在]
    B1 -->|存在| C[显示问题]
    C --> C1[检查 kubectl 权限]
    C --> C2[检查 Event TTL]

    B1 -->|不存在| D{检查事件上报}
    D --> D1[检查 EventRecorder 日志]
    D --> D2[检查 EventBroadcaster]

    D --> E{检查 API Server}
    E --> E1[检查 API Server 负载]
    E --> E2[检查 etcd 性能]

    A --> F{检查事件量}
    F --> F1[事件过多被限流]
    F --> F2[调整 event-burst]

诊断命令

1
2
3
4
5
6
7
8
9
10
11
# 查看 Pod 相关事件
kubectl get events -n <namespace> --field-selector involvedObject.name=<pod-name>

# 查看所有 Warning 事件
kubectl get events -A --field-selector type=Warning

# 检查事件 TTL
kubectl get events -A -o jsonpath='{.items[*].metadata.annotations}'

# 检查 kubelet 事件上报
journalctl -u kubelet | grep -i event

问题 3:探针状态未正确传播

症状:容器健康但 Pod Ready=False

排查流程图

flowchart TD
    A[Ready 状态异常] --> B{检查探针配置}

    B -->|无 readinessProbe| C[无探针,检查默认行为]
    B -->|有探针| D{检查探针执行}

    D --> D1[检查探针日志]
    D1 --> D2{探针成功?}

    D2 -->|否| E[探针失败]
    E --> E1[检查探针端口]
    E --> E2[检查探针路径]
    E --> E3[检查容器网络]

    D2 -->|是| F{检查状态传播}
    F --> F1[检查 readinessManager]
    F --> F2[检查 statusManager]

    A --> G{检查容器状态}
    G --> G1[容器是否 Running?]
    G --> G2[容器是否 Ready?]

诊断命令

1
2
3
4
5
6
7
8
9
10
11
# 检查探针配置
kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.spec.containers[*].readinessProbe}'

# 检查容器 Ready 状态
kubectl get pod <pod-name> -n <namespace> -o jsonpath='{.status.containerStatuses[*].ready}'

# 检查探针日志
journalctl -u kubelet | grep -i probe

# 手动测试探针 (在节点上)
curl http://<pod-ip>:<probe-port>/<probe-path>

10.2 关键日志关键词

场景 日志关键词 含义
状态同步 syncBatch 批量同步
状态写入 SetPodStatus 状态写入缓存
API 同步 PatchPodStatus API 更新
探针执行 Probe succeeded/failed 探针结果
事件上报 Event 事件记录
PLEG relist 容器状态列表

10.3 配置参数参考

参数 默认值 说明
--event-burst 10000 事件突发量
--event-qps 5 每秒事件数
--sync-frequency 1m 同步频率
--status-update-frequency 10s 状态更新频率
--minimum-container-ttl-duration 0s 容器 TTL

10.4 一键诊断命令

1
2
3
4
5
6
7
8
9
10
11
12
13
# 状态同步诊断脚本
POD_NAME=${1:-"<pod-name>"}
NAMESPACE=${2:-"default"}
NODE_NAME=$(kubectl get pod $POD_NAME -n $NAMESPACE -o jsonpath='{.spec.nodeName}')

echo "=== Pod Status ===" && \
kubectl get pod $POD_NAME -n $NAMESPACE -o yaml | grep -A 30 "status:" && \
echo -e "\n=== Pod Events ===" && \
kubectl get events -n $NAMESPACE --field-selector involvedObject.name=$POD_NAME --sort-by='.lastTimestamp' && \
echo -e "\n=== Container Status on Node ===" && \
echo "Run on node $NODE_NAME: crictl ps -a | grep $POD_NAME" && \
echo -e "\n=== Probe Status ===" && \
kubectl get pod $POD_NAME -n $NAMESPACE -o jsonpath='{.status.containerStatuses}' | jq '.[] | {name, ready, state}'

11. 面试题与详细解答

11.1 基础概念题

Q1: StatusManager 是如何工作的?

答案

StatusManager 负责将 kubelet 内部的 Pod 状态同步到 API Server。

架构设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌─────────────────────────────────────────────────────┐
│ kubelet 内部 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │podWorkers│ │ probeMgr │ │ PLEG │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ └─────────────┼──────────────┘ │
│ ▼ │
│ ┌────────────────┐ │
│ │ statusManager │ │
│ │ ┌──────────┐ │ │
│ │ │podStatuses│ │ 本地缓存 │
│ │ │ (map) │ │ │
│ │ └──────────┘ │ │
│ └───────┬────────┘ │
└────────────────────┼────────────────────────────────┘
│ 异步批量同步

┌────────────────┐
│ API Server │
│ PATCH /status │
└────────────────┘

关键特性

  1. 本地缓存podStatuses map 缓存每个 Pod 的最新状态
  2. 版本控制version 字段防止旧状态覆盖新状态
  3. 异步同步syncBatch 定期批量同步,不阻塞主流程
  4. 失败重试:同步失败的状态保留在缓存,下次重试

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// pkg/kubelet/status/status_manager.go:283
func (m *manager) SetPodStatus(pod *v1.Pod, status v1.PodStatus) {
m.podStatusesLock.Lock()
defer m.podStatusesLock.Unlock()

// 版本检查,防止旧状态覆盖
oldStatus, exists := m.podStatuses[pod.UID]
if exists && oldStatus.version > version {
return // 旧状态,忽略
}

// 更新缓存
m.podStatuses[pod.UID] = versionedPodStatus{
version: version,
status: status,
podName: pod.Name,
podNamespace: pod.Namespace,
}
}

Q2: Event 和 Status 有什么区别?

答案

维度 Status Event
目的 驱动控制逻辑 诊断和审计
消费方 Controllers 人类/监控系统
存储 持久化,Pod 对象的一部分 TTL 限制(默认 1 小时)
更新方式 PATCH /status POST 创建新对象
格式 结构化状态数据 事件描述文本
是否影响调度 是(Ready condition)

使用场景示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Status - 用于控制决策
status:
phase: Running
conditions:
- type: Ready
status: "True" # Controller 根据此判断 Pod 是否可用
containerStatuses:
- name: app
ready: true
state:
running:
startedAt: "2024-01-15T10:00:00Z"

---
# Event - 用于诊断问题
apiVersion: v1
kind: Event
metadata:
name: app-pod.17a2b3c4
message: "Started container app"
reason: Started
type: Normal

关键理解:控制器主要 watch Pod status,而不是 Event 流。


11.2 进阶原理题

Q3: 探针(Liveness/Readiness/Startup)如何影响 Pod 状态?

答案

三种探针对状态的不同影响

探针类型 失败后果 影响的状态字段
liveness 容器重启 无直接 condition
readiness 从 Service 移除 conditions[Ready]
startup 阻塞其他探针 conditions[Initialized]

Readiness 探针影响 Ready Condition 的流程

sequenceDiagram
    participant PM as ProbeManager
    participant RM as readinessManager
    participant SM as statusManager
    participant API as kube-apiserver
    participant EP as Endpoint Controller

    PM->>PM: 执行 readiness probe

    alt 探针成功
        PM->>RM: Set(containerID, Success)
        RM->>SM: 更新 Ready=True
        SM->>API: PATCH Pod status
        API-->>EP: watch Pod update
        EP->>EP: 添加 Pod IP 到 Endpoint
    else 探针失败
        PM->>RM: Set(containerID, Failure)
        RM->>SM: 更新 Ready=False
        SM->>API: PATCH Pod status
        API-->>EP: watch Pod update
        EP->>EP: 从 Endpoint 移除 Pod IP
    end

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// pkg/kubelet/prober/prober.go
func (pb *prober) runProbe(probeType probeType, p *v1.Probe, ...) (probe.Result, error) {
// 执行探针
result, err := pb.probeManager.Probe(ctx, probeSpec)

// 更新对应的状态管理器
switch probeType {
case readiness:
pb.readinessManager.Set(containerID, result)
case liveness:
pb.livenessManager.Set(containerID, result)
case startup:
pb.startupManager.Set(containerID, result)
}

return result, nil
}

Q4: 为什么 statusManager 采用异步批量同步而不是实时同步?

答案

设计原因

  1. 性能优化

    • 减少 API Server 压力
    • 合并多次小更新为一次批量更新
    • 避免频繁的 HTTP 请求
  2. 容错能力

    • 网络抖动不影响本地状态
    • 失败自动重试
    • 最终一致性保证
  3. 资源效率

    • 减少网络带宽
    • 减少 etcd 写入
    • 降低系统负载

实现细节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// pkg/kubelet/status/status_manager.go:766
func (m *manager) syncBatch(all bool) int {
// 遍历需要同步的 Pod
for uid, status := range m.podStatuses {
// 检查是否需要同步
if !m.needsSync(uid, status) {
continue
}

// 构建 PATCH 请求
patch := m.generatePatch(status)

// 发送到 API Server
_, err := m.kubeClient.CoreV1().Pods(status.podNamespace).
Patch(ctx, status.podName, types.MergePatchType, patch, metav1.PatchOptions{}, "status")

if err != nil {
// 失败保留在缓存,下次重试
continue
}

// 成功更新版本号
m.apiStatusVersions[uid] = status.version
}
}

同步策略

  • 状态变化时立即触发
  • 定期全量同步(默认 10 秒)
  • 终止状态的 Pod 优先同步

11.3 故障排查题

Q5: Pod 状态与实际容器状态不一致,如何排查?

答案

排查流程

flowchart TD
    A[状态不一致] --> B{确认不一致类型}

    B -->|Pod 显示 Running, 容器已退出| C[PLEG 问题]
    C --> C1[检查 PLEG relist 日志]
    C --> C2[检查容器运行时连接]

    B -->|Pod 显示 NotReady, 容器正常| D[探针问题]
    D --> D1[检查探针配置]
    D --> D2[手动测试探针端点]

    B -->|状态更新延迟| E[statusManager 问题]
    E --> E1[检查 syncBatch 日志]
    E --> E2[检查 API Server 连接]

    A --> F{验证步骤}
    F --> F1[kubectl get pod -o yaml]
    F --> F2[crictl ps -a]
    F --> F3[对比两者状态]

诊断命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 对比 Pod status 和容器状态
kubectl get pod <pod> -o jsonpath='{.status.containerStatuses}'
crictl ps -a | grep <pod>

# 2. 检查 PLEG 状态
journalctl -u kubelet | grep -i pleg

# 3. 检查 statusManager 日志
journalctl -u kubelet | grep -i "sync.*status"

# 4. 强制触发状态同步
curl -X POST http://<kubelet-ip>:10250/pods

# 5. 检查探针结果
kubectl describe pod <pod> | grep -A 5 "Liveness\|Readiness"

常见原因

原因 症状 解决方案
PLEG relist 失败 状态不更新 检查 CRI 连接
探针超时 Ready=False 调整探针超时时间
statusManager 同步失败 状态延迟 检查 API Server 连接
容器快速重启 状态跳动 检查应用崩溃原因

11.4 设计思想题

Q6: 为什么 Kubernetes 将状态和事件分开存储?

答案

分离设计的优势

  1. 职责分离

    • Status:控制面输入,驱动 Controller 决策
    • Event:诊断信息,用于调试和审计
  2. 性能优化

    • Status 是高频更新,需要持久化
    • Event 是低频诊断,可以 TTL 过期
  3. 存储效率

    • Status:每个 Pod 一个 status 对象
    • Event:每次事件一个 Event 对象,但有过期清理
  4. 访问模式不同

    • Status:Controllers watch,需要实时
    • Event:人类查询,可以延迟

数据流对比

1
2
3
4
5
6
7
8
9
10
11
Status 流:
kubelet → statusManager → API Server → etcd → Controller watch

持久化存储

Event 流:
kubelet → EventRecorder → API Server → etcd

TTL 过期清理

kubectl describe / 监控系统

Event 的设计考量

1
2
3
4
5
6
7
8
9
10
11
12
// Event 有 TTL,默认 1 小时
// staging/src/k8s.io/client-go/tools/record/event.go
const (
maxLruCacheEntries = 4096
// 事件聚合,相同事件不会无限创建
)

// 事件去重和聚合
func (e *eventBroadcasterImpl) recordToSink(event *v1.Event) {
// 相同事件会更新 count 和 lastTimestamp
// 而不是创建新事件
}