K8s-节点心跳与健康流程
节点心跳与健康状态通信流程
目标
这篇文档只回答一个核心问题:kubelet 如何把节点健康信号汇报给控制面,控制面又如何据此做出判断和后续动作?
重点放在两类心跳信号的协作关系,以及控制面如何综合判断节点健康。
一句话摘要
kubelet 周期性更新 NodeStatus 并续约 Lease;node lifecycle controller 通过 informer/lister 观察 Node 与 Lease,综合判断节点健康,再触发 taint、Pod not-ready 传播与后续驱逐流程。
1. 流程总览
从通信视角看,这条链路包含两类并行的心跳信号:
- NodeStatus 心跳:携带节点条件(Ready、MemoryPressure 等)和容量信息。
- Lease 心跳:轻量级的存活信号,用于快速检测节点失联。
控制面会综合这两类信号来判断节点健康状态。
Mermaid:总览图
flowchart LR
A[Kubelet] --> B[syncNodeStatus]
A --> C[nodeLeaseController]
B --> D[API Server NodeStatus]
C --> E[API Server Lease]
D --> F[NodeLifecycleController]
E --> F
F --> G[monitorNodeHealth]
G --> H[Ready / NotReady / Unknown]
H --> I[taints / pod not-ready / eviction]
双心跳机制架构图
flowchart TB
subgraph Kubelet侧["Kubelet 节点侧"]
direction TB
KL[Kubelet]
subgraph 心跳机制["双心跳机制"]
direction LR
NLC[nodeLeaseController]
SNS[syncNodeStatus]
end
subgraph 状态收集["状态收集"]
direction TB
CAD[cAdvisor<br/>资源监控]
EVI[EvictionManager<br/>压力检测]
PRO[ProbeManager<br/>健康检查]
end
CAD --> SNS
EVI --> SNS
PRO --> SNS
KL --> NLC
KL --> SNS
end
subgraph APIServer["控制面"]
direction TB
API[kube-apiserver]
ETCD[(etcd)]
subgraph 存储["两类对象"]
NODE[Node 对象<br/>详细状态]
LEASE[Lease 对象<br/>轻量心跳]
end
API <--> ETCD
NODE --> API
LEASE --> API
end
subgraph 控制器["NodeLifecycleController"]
direction TB
INF[Informer<br/>watch Node/Lease]
MNH[monitorNodeHealth<br/>健康判断]
TM[Taint Manager<br/>taint 驱逐]
INF --> MNH
MNH --> TM
end
subgraph 后果["后续动作"]
direction TB
TAINT[更新 Node taint]
PNR[Pod Ready=False]
EVICT[驱逐 Pod]
SCHED[Scheduler 感知]
end
NLC -->|PUT Lease| API
SNS -->|PATCH Node| API
API -->|watch| INF
TM --> TAINT
TM --> PNR
TM --> EVICT
TAINT --> SCHED
节点健康判断流程图
flowchart TB
subgraph 输入["心跳信号输入"]
L[Lease renewTime]
N[NodeStatus Ready 条件]
end
subgraph 判断逻辑["健康判断逻辑"]
direction TB
C1{Lease 超时?<br/>超过40s未续约}
C2{Ready == True?}
C3{其他条件正常?<br/>Memory/Disk/PID}
C1 -->|是| UNK[Unknown]
C1 -->|否| C2
C2 -->|否| NR[NotReady]
C2 -->|是| C3
C3 -->|否| NR
C3 -->|是| RDY[Ready]
end
subgraph 后果["状态后果"]
direction TB
UNK --> U1[NoExecute taint]
UNK --> U2[驱逐所有 Pod]
UNK --> U3[等待恢复]
NR --> N1[NoSchedule taint]
NR --> N2[Pod Ready=False]
NR --> N3[阻止新调度]
RDY --> R1[清理健康 taint]
RDY --> R2[允许正常调度]
end
L --> C1
N --> C2
这张图想表达什么
- NodeStatus 和 Lease 是两类不同的健康信号。
- 控制器综合两者做出判断,而不是只看其中一个。
2. 分阶段通信流程
阶段一:kubelet 上报两类心跳
这个阶段的本质是:kubelet 通过两种不同粒度的信号向控制面汇报自己还活着。
步骤 1.1:创建 nodeLeaseController
kubelet 在初始化时,会创建 nodeLeaseController。这个控制器负责定期续约节点的 Lease 对象。
通信方向:kubelet 启动时初始化
步骤 1.2:Lease 续约
nodeLeaseController 会定期(默认每 10 秒)向 API Server 更新 Lease 对象的 renewTime。Lease 对象非常轻量,只包含节点名称和续约时间。
通信方向:kubelet → API Server(HTTP PUT Lease)
步骤 1.3:启动 syncNodeStatus 循环
kubelet 同时启动一个循环,定期执行 syncNodeStatus()。这个循环的频率比 Lease 低(默认每 10 秒检查一次,但只在状态变化时才真正更新)。
通信方向:kubelet 内部循环
步骤 1.4:收集节点状态信息
在 syncNodeStatus() 中,kubelet 会收集:
- 节点条件(Ready、MemoryPressure、DiskPressure、PIDPressure、NetworkUnavailable)
- 节点容量和可分配资源
- 节点地址信息
- 节点镜像列表等
通信方向:kubelet → cAdvisor / cadvisor / 系统信息
步骤 1.5:判断是否需要更新
kubelet 会对比当前收集到的状态和上次上报的状态。只有状态变化或到达强制更新间隔时,才会调用 tryUpdateNodeStatus(...)。
通信方向:kubelet 内部判断
步骤 1.6:tryUpdateNodeStatus 更新 NodeStatus
tryUpdateNodeStatus(...) 会向 API Server 发送请求,更新 Node 对象的 status 字段。这个请求比 Lease 重得多。
通信方向:kubelet → API Server(HTTP PUT/PATCH Node status)
步骤 1.7:快速更新路径(启动时)
在节点刚启动时,kubelet 会通过 fastNodeStatusUpdate(...) 更快地把 Ready 状态推向控制面,减少启动延迟。
通信方向:kubelet → API Server(高频更新)
阶段二:控制面观察 Node 与 Lease
这个阶段的本质是:控制器通过 watch API Server 中的对象来判断节点健康。
步骤 2.1:NodeLifecycleController 注册 informer
NodeLifecycleController 在启动时,会注册对 Node、Lease、Pod 三个资源的 informer。这样当这些资源变化时,控制器会收到事件。
通信方向:controller → API Server(informer/watch)
步骤 2.2:收到 Node/Lease 更新事件
当 kubelet 更新 Node 或 Lease 时,API Server 会通过 watch 推送事件给控制器。控制器更新本地缓存。
通信方向:API Server → controller(watch 推送)
步骤 2.3:启动 monitorNodeHealth 循环
控制器启动一个定期循环,调用 monitorNodeHealth(...)。这个循环会遍历所有节点,逐一判断健康状态。
通信方向:controller 内部循环
步骤 2.4:检查 Lease 是否最近续约
对于每个节点,控制器会检查其 Lease 的 renewTime。如果超过 nodeMonitorGracePeriod(默认 40 秒)未续约,认为节点可能失联。
通信方向:controller 内部判断,读取本地缓存
步骤 2.5:检查 NodeStatus Ready 条件
控制器同时检查 NodeStatus 中的 Ready 条件。如果 Ready 为 False 或 Unknown,认为节点不健康。
通信方向:controller 内部判断,读取本地缓存
步骤 2.6:综合判断节点健康状态
控制器综合 Lease 和 NodeStatus 的信息,得出节点的最终状态:
- Ready:Lease 最近续约,且 Ready 条件为 True
- NotReady:Lease 正常,但 Ready 条件为 False
- Unknown:Lease 超时或 Ready 条件为 Unknown
通信方向:controller 内部计算
阶段三:触发后续动作
这个阶段的本质是:健康判断的后果不只是标记,还会影响 Pod 调度和驱逐。
步骤 3.1:更新节点 taint
如果节点状态变为 NotReady 或 Unknown,控制器可能会更新节点 taint:
- NoSchedule:阻止新 Pod 调度到该节点
- NoExecute:驱逐已运行的 Pod(取决于 Pod 的 toleration)
通信方向:controller → API Server(HTTP PATCH Node)
步骤 3.2:传播 Pod not-ready 状态
控制器会更新该节点上所有 Pod 的 Ready 条件为 False。这会影响依赖 Pod readiness 的服务发现。
通信方向:controller → API Server(HTTP PATCH Pod status)
步骤 3.3:触发 taint-based eviction
如果节点有 NoExecute taint,且 Pod 没有对应的 toleration,控制器会驱逐这些 Pod。
通信方向:controller → API Server(HTTP DELETE Pod)
步骤 3.4:节点恢复时清理 taint
如果节点恢复健康(从 NotReady/Unknown 变为 Ready),控制器会清理相关的 taint,允许 Pod 重新调度。
通信方向:controller → API Server(HTTP PATCH Node)
小结:这条链路的 2 类心跳信号
| 信号类型 | 目的 | 频率 | 携带信息 |
|---|---|---|---|
| Lease | 快速检测节点存活 | 高(默认 10 秒) | 只有续约时间 |
| NodeStatus | 详细节点状态 | 低(状态变化时) | 条件、容量、地址等 |
核心结论:控制面综合两类信号判断节点健康,而不是只看其中一个。
2.5 完整流程图
下面这张图把本文所有步骤串在一起,方便一眼看完整个通信链路:
flowchart TD
subgraph 心跳上报["心跳上报"]
A1[kubelet 启动] --> A2[nodeLeaseController]
A1 --> A3[syncNodeStatus 循环]
A2 --> A4[定期续约 Lease]
A4 --> A5[PUT Lease 到 API Server]
A3 --> A6[收集节点状态]
A6 --> A7{状态变化?}
A7 -->|是| A8[tryUpdateNodeStatus]
A7 -->|否| A9[等待下一周期]
A8 --> A10[PUT/PATCH Node status]
end
subgraph 健康判断["控制面健康判断"]
B1[NodeLifecycleController] --> B2[注册 informer]
B2 --> B3[watch Node/Lease]
B3 --> B4[本地缓存更新]
B4 --> B5[monitorNodeHealth 循环]
B5 --> B6[检查 Lease renewTime]
B5 --> B7[检查 NodeStatus Ready]
B6 --> B8{Lease 超时?}
B7 --> B9{Ready == True?}
B8 -->|是| B10[标记 Unknown]
B9 -->|否| B11[标记 NotReady]
B8 -->|否| B9
B9 -->|是| B12[标记 Ready]
end
subgraph 后续动作["后续动作"]
B10 --> C1[NoExecute taint]
B11 --> C2[NoSchedule taint]
B11 --> C3[Pod Ready = False]
B10 --> C4[驱逐 Pod]
B12 --> C5[清理 taint]
end
2.6 与其他组件的交互
这条链路不只是 kubelet 和 node controller 的事,还涉及多个系统的协作:
与 Scheduler 的交互
节点健康状态直接影响调度决策:
flowchart TD
A[Node NotReady] --> B[Node 有 NoSchedule taint]
B --> C[scheduler Filter 插件]
C --> D[排除该节点]
E[Node Unknown] --> F[Node 有 NoExecute taint]
F --> G[scheduler 不调度新 Pod]
| 节点状态 | taint 影响 | 调度行为 |
|---|---|---|
| Ready | 无相关 taint | 正常调度 |
| NotReady | NoSchedule | 不调度新 Pod |
| Unknown | NoExecute | 不调度新 Pod + 驱逐现有 Pod |
关键理解:scheduler 通过 informer watch Node 对象,实时感知 taint 变化。
与 Taint Manager 的交互
Taint Manager 是 NodeLifecycleController 的一部分,负责执行 taint-based eviction:
flowchart TD
A[Node 有 NoExecute taint] --> B[Taint Manager 检查 Pod]
B --> C{Pod 有 toleration?}
C -->|有| D[保留 Pod]
C -->|无| E{超过 tolerationSeconds?}
E -->|是| F[驱逐 Pod]
E -->|否| G[等待超时]
| taint 类型 | Pod 需要 | 不满足时后果 |
|---|---|---|
| NoSchedule | toleration | 不调度 |
| NoExecute | toleration + tolerationSeconds | 驱逐 |
关键理解:Taint Manager 是独立于 scheduler 的驱逐机制。
与 Service/Endpoint 的交互
节点 NotReady 会影响该节点上 Pod 的服务发现:
flowchart LR
A[Node NotReady] --> B[Pod Ready = False]
B --> C[Endpoint Controller]
C --> D[从 EndpointSlice 移除 Pod IP]
D --> E[Service 不再转发流量]
关键理解:这是通过 Pod status 间接影响 Service,而不是直接修改 Endpoint。
与集群自动扩缩容的交互
节点健康状态会影响 Cluster Autoscaler 的决策:
flowchart TD
A[多个 Node Unknown] --> B[可用容量减少]
B --> C[Pending Pod 增加]
C --> D[Cluster Autoscaler]
D --> E{需要扩容?}
E -->|是| F[创建新 Node]
E -->|否| G[继续观察]
| 场景 | Autoscaler 行为 |
|---|---|
| 节点 NotReady 但未删除 | 不自动替换,等待恢复 |
| 节点被删除 | 可能触发扩容 |
| 长期 Unknown | 取决于云 provider |
关键理解:Cluster Autoscaler 观察的是 Node 对象状态,而不是直接探测节点。
与云 provider 的交互
云 provider 的 controller 会参与节点生命周期:
flowchart LR
A[云 provider] --> B[Node Controller]
B --> C[设置 Node providerID]
B --> D[处理云实例事件]
D --> E[实例终止时删除 Node]
| 云 provider 行为 | 影响 |
|---|---|
| 实例终止 | Node 被删除 |
| 实例健康检查失败 | 可能标记 Node 问题 |
| 实例迁移 | Node 可能短暂 NotReady |
关键理解:kubelet 的心跳是集群内视角,云 provider 提供集群外视角。
交互总结表
| 外部系统 | 交互方式 | 影响什么 |
|---|---|---|
| Scheduler | watch Node taint | 调度过滤 |
| Taint Manager | watch Node + Pod | 驱逐 Pod |
| Endpoint Controller | watch Pod status | Service 流量 |
| Cluster Autoscaler | watch Node + Pod | 扩缩容 |
| 云 provider | 云 API | Node 生命周期 |
3. 关键时序图
Mermaid:主时序图
sequenceDiagram
participant Kubelet
participant APIServer
participant Controller as NodeLifecycleController
loop every lease interval
Kubelet->>APIServer: renew Lease
end
loop every node status interval
Kubelet->>APIServer: update NodeStatus
end
APIServer-->>Controller: watch Node / Lease changes
Controller->>Controller: monitorNodeHealth()
Controller->>Controller: compute Ready / NotReady / Unknown
alt node unhealthy
Controller->>APIServer: update taints
Controller->>APIServer: mark pods NotReady
end
时序图里的 3 个关键点
- Lease 和 NodeStatus 是两条独立的写入路径。
- 控制器通过 watch 观察,而不是主动轮询 kubelet。
- 健康判断的结果会触发 taint 和 Pod 状态传播。
4. 异常与分支路径
Mermaid:异常/分支路径图
flowchart TD
A[Kubelet sends heartbeat] --> B{Lease recent?}
B -->|No| C[mark node Unknown]
B -->|Yes| D{NodeStatus Ready == True?}
D -->|No| E[mark node NotReady]
D -->|Yes| F[mark node Ready]
C --> G[NoExecute taint / pod eviction]
E --> H[NoSchedule taint / pod not-ready]
F --> I[clear health-related taints]
J[grace period exceeded] --> K[definitely mark Unknown]
K --> G
读这张图时要关注什么
- Lease 超时:即使 NodeStatus 未更新,也会触发 Unknown 判断。
- NodeStatus NotReady:即使 Lease 正常,也会触发 NotReady 判断。
- grace period:控制面会等待一定时间后才做出最终判断,避免抖动。
5. 这个流程里谁负责什么
| 组件 | 主要职责 | 不负责什么 |
|---|---|---|
kubelet |
上报 NodeStatus 和 Lease | 不判断自己是否健康 |
nodeLeaseController |
续约 Lease | 不携带详细节点条件 |
NodeLifecycleController |
综合判断节点健康并触发后续动作 | 不主动探测 kubelet |
kube-apiserver |
持久化 Node 和 Lease | 不执行健康判断逻辑 |
6. 常见误解
误解 1:Lease 和 NodeStatus 是同一个心跳
不是。它们是两类不同但互补的健康信号。
误解 2:node controller 主动探测 kubelet
不是。controller 观察的是 API Server 中的 Node / Lease。
误解 3:只看 Lease 就够了
不是。现代节点健康判断仍结合 NodeStatus 与节点条件。
误解 4:节点异常只是标记 NotReady
不是。还包括 taint、Pod 状态传播和驱逐链路。
7. 版本差异说明
这条链路在 Kubernetes 1.27/1.28/1.29 中有一些值得注意的变化:
1.27 → 1.28 变化
| 变化点 | 说明 |
|---|---|
| Node 按需状态更新 | kubelet 只在状态变化时更新 NodeStatus,减少 API Server 压力 |
| Swap 支持 (Alpha) | 节点可以配置使用 swap,影响 MemoryPressure 条件判断 |
| Node Graceful Shutdown 改进 | 节点关闭时的状态传播更可靠 |
1.28 → 1.29 变化
| 变化点 | 说明 |
|---|---|
| Windows UUID 获取方式改变 | 停止使用 wmic,改用更现代的方式获取节点 UUID |
| Lease 续约可靠性改进 | 在高负载情况下 Lease 续约更稳定 |
| Node Monitor Grace Period 行为 | 对 grace period 的判断逻辑有细微修复 |
向后兼容性
| 版本 | 兼容性注意 |
|---|---|
| 1.27+ | Node 按需状态更新减少 API 调用,可能影响依赖固定频率更新的监控 |
| 1.28+ | Swap 支持需要操作系统和容器运行时配合 |
| 1.29+ | Windows 节点升级后可能看到节点 ID 变化 |
需要关注的废弃
| 废弃项 | 替代方案 | 移除版本 |
|---|---|---|
--enable-taint-manager flag |
Taint Manager 默认启用,flag 已无效 | 1.29 已移除 |
Node volumesAttached/volumesInUse |
使用 CSI 相关字段 | 计划移除 |
7. 与后续文档的关系
建议按这个顺序阅读:
docs/kubelet-status-and-event-feedback-flow.md- 本文:理解节点健康信号如何影响控制面判断
docs/kubelet-deep-dive.md:补充 kubelet 子系统背景
8. 代码入口(精简版)
如果读者想从流程跳回实现,可从下面几个入口开始:
- kubelet 创建 Lease controller:
pkg/kubelet/kubelet.go:892nodeLeaseController = lease.NewController(...)
- kubelet 同步节点状态:
pkg/kubelet/kubelet_node_status.go:518syncNodeStatus()
- node lifecycle controller 健康监控:
pkg/controller/nodelifecycle/node_lifecycle_controller.go:658monitorNodeHealth(...)
9. 详细时序图
9.1 正常心跳流程时序图
sequenceDiagram
autonumber
participant KL as Kubelet
participant NLC as nodeLeaseController
participant SNS as syncNodeStatus
participant API as kube-apiserver
participant ETCD as etcd
participant NLCtrl as NodeLifecycleController
participant TM as TaintManager
Note over KL: Kubelet 启动初始化
loop 每 10 秒 (默认)
NLC->>API: PUT /apis/coordination.k8s.io/v1/namespaces/kube-node-lease/leases/<node-name>
API->>ETCD: 更新 Lease.renewTime
ETCD-->>API: 确认
API-->>NLC: 200 OK
end
loop 每 10 秒检查,状态变化时更新
SNS->>SNS: 收集节点条件 (Ready, MemoryPressure, etc.)
SNS->>SNS: 对比上次状态
alt 状态有变化
SNS->>API: PATCH /api/v1/nodes/<node-name>/status
API->>ETCD: 更新 Node.status
ETCD-->>API: 确认
API-->>SNS: 200 OK
else 无变化
SNS->>SNS: 跳过更新
end
end
API-->>NLCtrl: watch 推送 Node/Lease 变化
NLCtrl->>NLCtrl: monitorNodeHealth()
NLCtrl->>NLCtrl: 检查 Lease.renewTime
NLCtrl->>NLCtrl: 检查 Node.status.conditions[Ready]
alt 节点健康
NLCtrl->>NLCtrl: 标记 Ready
opt 有健康相关 taint
NLCtrl->>API: PATCH 移除 taint
end
else 节点不健康
NLCtrl->>API: PATCH 添加 taint
TM->>API: PATCH Pod status (Ready=False)
opt NoExecute taint 且 Pod 无 toleration
TM->>API: DELETE Pod
end
end
9.2 节点故障检测时序图
sequenceDiagram
autonumber
participant KL as Kubelet
participant API as kube-apiserver
participant NLCtrl as NodeLifecycleController
participant TM as TaintManager
participant SCHED as Scheduler
Note over KL: 节点网络故障/宕机
loop 正常心跳
KL->>API: Lease 续约
KL->>API: NodeStatus 更新
end
Note over KL: ❌ 心跳停止
API-->>NLCtrl: watch 无新事件
Note over NLCtrl: 等待 nodeMonitorGracePeriod (默认 40s)
NLCtrl->>NLCtrl: 检查 Lease.renewTime
NLCtrl->>NLCtrl: 超时! 标记 Unknown
NLCtrl->>API: PATCH Node (添加 node.kubernetes.io/unreachable:NoExecute)
par 并行处理
TM->>API: PATCH 所有 Pod (Ready=False)
and
TM->>TM: 检查每个 Pod 的 toleration
loop 每个 Pod
alt 有 toleration for unreachable
TM->>TM: 保留 Pod
else 无 toleration
TM->>API: DELETE Pod (驱逐)
end
end
and
API-->>SCHED: watch Node taint
SCHED->>SCHED: 过滤该节点 (不调度新 Pod)
end
9.3 节点恢复时序图
sequenceDiagram
autonumber
participant KL as Kubelet
participant API as kube-apiserver
participant NLCtrl as NodeLifecycleController
participant SCHED as Scheduler
Note over KL: 节点恢复,网络重新连通
KL->>API: PUT Lease (续约恢复)
API-->>NLCtrl: watch 收到 Lease 更新
KL->>KL: 收集节点状态
KL->>API: PATCH NodeStatus (Ready=True)
API-->>NLCtrl: watch 收到 Node 更新
NLCtrl->>NLCtrl: monitorNodeHealth()
NLCtrl->>NLCtrl: Lease 正常 + Ready=True
NLCtrl->>NLCtrl: 标记 Ready
NLCtrl->>API: PATCH Node (移除 unreachable taint)
API-->>SCHED: watch 收到 taint 移除
SCHED->>SCHED: 节点重新可用于调度
Note over KL: 新 Pod 可以调度到此节点
10. 故障排查指南
10.1 常见问题与诊断方法
问题 1:节点状态一直是 NotReady
症状:
1 | kubectl get nodes |
排查步骤:
flowchart TD
A[节点 NotReady] --> B{检查 kubelet 是否运行}
B -->|未运行| B1[启动 kubelet 服务]
B -->|运行中| C{检查 kubelet 日志}
C --> C1[查看是否有 Lease 续约错误]
C1 --> C2{有网络错误?}
C2 -->|是| C3[检查网络连通性<br/>检查 API Server 可达性]
C2 -->|否| C4[检查 kubelet 配置]
C --> D[检查 Node 对象 conditions]
D --> D1{哪个条件为 False?}
D1 -->|Ready| E1[检查容器运行时状态]
D1 -->|MemoryPressure| E2[检查节点内存使用]
D1 -->|DiskPressure| E3[检查磁盘空间]
D1 -->|PIDPressure| E4[检查进程数]
E1 --> F[重启 containerd/cri-o]
E2 --> G[清理内存或扩容]
E3 --> H[清理磁盘空间]
E4 --> I[检查是否有进程泄漏]
诊断命令:
1 | # 1. 检查 kubelet 状态 |
问题 2:节点频繁在 Ready/NotReady 之间切换
可能原因:
- 网络不稳定导致心跳丢包
- API Server 负载过高导致请求超时
- kubelet 资源不足导致心跳延迟
- 时钟不同步
排查步骤:
1 | # 检查 kubelet 心跳频率和延迟 |
问题 3:Pod 被意外驱逐
症状:节点上的 Pod 突然被删除
排查步骤:
flowchart TD
A[Pod 被驱逐] --> B{检查节点状态}
B -->|Unknown/NotReady| C[节点健康问题]
B -->|Ready| D{检查驱逐原因}
C --> C1[参考问题1排查]
D --> D1{检查 Node taint}
D1 -->|有 NoExecute| D2[Pod 缺少 toleration]
D1 -->|无异常 taint| D3[检查资源压力]
D2 --> E[为 Pod 添加 toleration]
D3 --> D4{检查 EvictionManager}
D4 -->|资源超限| F[节点资源不足导致驱逐]
D4 -->|正常| G[检查 Descheduler 等组件]
诊断命令:
1 | # 检查 Pod 事件 |
10.2 关键日志关键词
| 场景 | 日志关键词 | 含义 |
|---|---|---|
| Lease 续约成功 | successfully renewed lease |
心跳正常 |
| Lease 续约失败 | failed to renew lease |
心跳异常 |
| NodeStatus 更新 | Updating node status |
状态同步 |
| 节点标记 NotReady | node is not ready |
健康检查失败 |
| Pod 驱逐 | evicting pod |
Taint Manager 驱逐 |
| 资源压力 | eviction manager |
资源不足 |
10.3 配置参数参考
| 参数 | 默认值 | 说明 |
|---|---|---|
--node-lease-duration-seconds |
40 | Lease 有效期 |
--node-renew-period-seconds |
10 | Lease 续约间隔 |
node-monitor-grace-period |
40s | 节点不响应后标记 Unknown 的等待时间 |
pod-eviction-timeout |
5m | NotReady 节点上 Pod 驱逐超时 |
--enable-taint-manager |
true (1.29 已移除) | 启用 Taint Manager |
10.4 健康检查命令速查
1 | # 一键检查节点健康状态 |
11. 面试题与详细解答
11.1 基础概念题
Q1: Kubernetes 节点心跳机制是如何工作的?
答案:
Kubernetes 使用双心跳机制来监控节点健康:
1. Lease 心跳(轻量级)
- 频率:每 10 秒
- 对象:
Lease对象在kube-node-leasenamespace - 内容:仅包含
renewTime时间戳 - 目的:快速检测节点是否存活
2. NodeStatus 心跳(详细)
- 频率:每 10 秒检查,状态变化时才更新
- 对象:
Node对象的status字段 - 内容:Conditions(Ready/MemoryPressure 等)、容量、地址等
- 目的:报告详细的节点健康状态
1 | # Lease 对象示例 |
为什么需要两个心跳:
- Lease 轻量,减少 API Server 负载
- NodeStatus 详细,但更新成本高
- 分离关注点:存活检测 vs 健康状态报告
Q2: 节点的 Ready/NotReady/Unknown 状态是如何判断的?
答案:
判断逻辑:
flowchart TD
A[检查 Lease] --> B{renewTime 超时?}
B -->|是, >40s| C[标记 Unknown]
B -->|否| D[检查 NodeStatus]
D --> E{Ready condition?}
E -->|True| F[标记 Ready]
E -->|False| G[标记 NotReady]
E -->|Unknown| C
C --> H[触发 NoExecute taint]
F --> I[清理健康相关 taint]
G --> J[触发 NoSchedule taint]
代码实现:
1 | // pkg/controller/nodelifecycle/node_lifecycle_controller.go:658 |
状态含义:
| 状态 | 含义 | 后果 |
|---|---|---|
| Ready | 节点健康,可以调度 Pod | 正常调度 |
| NotReady | 节点不健康(如磁盘满、内存压力) | NoSchedule taint,不调度新 Pod |
| Unknown | 节点失联(网络问题或宕机) | NoExecute taint,驱逐 Pod |
11.2 进阶原理题
Q3: Taint Manager 是如何工作的?
答案:
Taint Manager 是 NodeLifecycleController 的一部分,负责执行基于 taint 的驱逐。
工作流程:
sequenceDiagram
participant NLC as NodeLifecycleController
participant TM as TaintManager
participant API as kube-apiserver
participant KL as Kubelet
Note over NLC: 检测到节点 NotReady
NLC->>API: 添加 taint: node.kubernetes.io/unreachable:NoExecute
API-->>TM: watch taint 变化
TM->>TM: 获取该节点上所有 Pod
loop 每个 Pod
TM->>TM: 检查 Pod tolerations
alt 有匹配的 toleration
TM->>TM: 检查 tolerationSeconds
alt tolerationSeconds 未超时
TM->>TM: 保留 Pod
else tolerationSeconds 超时
TM->>API: DELETE Pod
end
else 无匹配 toleration
TM->>API: DELETE Pod (立即驱逐)
end
end
API-->>KL: watch Pod DELETE
KL->>KL: 优雅终止 Pod
Toleration 配置示例:
1 | tolerations: |
代码入口:
1 | // pkg/controller/nodelifecycle/scheduler/taint_manager.go |
Q4: nodeMonitorGracePeriod 和 podEvictionTimeout 有什么区别?
答案:
这两个参数作用于不同的阶段:
timeline
title 节点故障时间线
t0: 节点故障/网络中断
t1: 40s (nodeMonitorGracePeriod)
: 控制面标记节点 Unknown
: 添加 NoExecute taint
t2: 5m (podEvictionTimeout)
: 开始驱逐 NotReady 节点上的 Pod
: 仅对无 toleration 的 Pod
参数对比:
| 参数 | 位置 | 默认值 | 作用 |
|---|---|---|---|
node-monitor-grace-period |
kube-controller-manager | 40s | 节点多久无响应后标记 Unknown |
pod-eviction-timeout |
kube-controller-manager | 5m | NotReady 节点上 Pod 开始驱逐的时间 |
配置方式:
1 | # kube-controller-manager 启动参数 |
注意事项:
pod-eviction-timeout只对NotReady状态生效Unknown状态立即触发 taint-based eviction- 如果 Pod 有 toleration,驱逐时间由
tolerationSeconds决定
11.3 故障排查题
Q5: 节点一直显示 NotReady,如何排查?
答案:
排查流程:
flowchart TD
A[节点 NotReady] --> B{检查 kubelet}
B -->|未运行| B1[启动 kubelet 服务]
B -->|运行中| C{检查 Conditions}
C --> C1{哪个 Condition 异常?}
C1 -->|Ready=False| D[检查容器运行时]
D --> D1[systemctl status containerd]
D --> D2[检查 CRI 连接]
C1 -->|MemoryPressure| E[检查内存]
E --> E1[free -m]
E --> E2[检查内存泄漏]
C1 -->|DiskPressure| F[检查磁盘]
F --> F1[df -h]
F --> F2[清理镜像/日志]
C1 -->|NetworkUnavailable| G[检查网络]
G --> G1[检查 CNI 配置]
G --> G2[检查网络连通性]
B --> H{检查日志}
H --> H1[journalctl -u kubelet]
H --> H2[检查心跳相关错误]
诊断命令:
1 | # 1. 检查节点详细状态 |
常见原因及解决:
| 原因 | 症状 | 解决方案 |
|---|---|---|
| kubelet 未运行 | 服务停止 | systemctl start kubelet |
| 容器运行时故障 | CRI 连接失败 | 重启 containerd |
| 内存不足 | MemoryPressure=True | 增加内存/清理进程 |
| 磁盘满 | DiskPressure=True | 清理镜像/日志 |
| 网络问题 | 心跳超时 | 检查网络配置 |
11.4 设计思想题
Q6: 为什么使用 Lease API 而不是直接更新 Node 对象来发送心跳?
答案:
设计考虑:
| 方面 | 直接更新 Node | 使用 Lease API |
|---|---|---|
| 对象大小 | Node 对象较大(KB 级别) | Lease 对象很小(~100 字节) |
| etcd 负载 | 高(每次心跳写入大量数据) | 低(仅写入时间戳) |
| watch 流量 | 高(所有组件都 watch Node) | 低(只有 controller watch Lease) |
| 历史数据 | 需要保留完整历史 | 仅需最新时间戳 |
演进历史:
- v1.13 之前:只有 NodeStatus 心跳,etcd 负载高
- v1.14:引入 Lease API(Alpha)
- v1.17:Lease 心跳成为默认
代码实现:
1 | // pkg/kubelet/node_lease.go |
NodeStatus 按需更新:
1 | // pkg/kubelet/kubelet_node_status.go:518 |
结论:Lease API 将高频轻量心跳与低频详细状态更新分离,显著降低了控制面负载。

