4. Kubelet 心跳上报原理:NodeLease 机制、NodeStatus 更新与节点状态转换
Kubelet 通过两种并行的机制向 API Server 报告节点健康状态:NodeLease(轻量心跳) 和 NodeStatus(全量更新)。理解这两者的区别、协作方式以及超时阈值,是诊断“证书轮换导致心跳上报失败”这类故障的理论基础。
4.1 双轨心跳机制概览
Kubernetes 自 v1.13 起引入了 NodeLease API(coordination.k8s.io/v1),将心跳与节点状态更新解耦,大幅降低了大规模集群中频繁更新 NodeStatus 带来的 etcd 压力。
| 对比维度 | NodeLease(轻量心跳) | NodeStatus(全量更新) |
|---|---|---|
| API 资源 | Lease(coordination.k8s.io/v1) |
Node(v1) |
| 更新频率 | 默认每 10 秒(node-status-update-frequency 的 1/5) |
默认每 1 分钟(node-status-update-frequency) |
| 数据量 | 极小(仅含 Lease 对象、续约时间戳) | 大(包含节点条件、容量、镜像、IP 等全部状态) |
| 主要用途 | 快速检测节点存活(心跳) | 同步节点完整状态(调度决策依据) |
| 证书依赖 | 需要有效的 kubelet 客户端证书 | 需要有效的 kubelet 客户端证书 |
4.2 NodeLease 机制详解
每个节点在 kube-node-lease 命名空间中拥有一个同名的 Lease 对象。Kubelet 定期(默认 10s)通过 PATCH 请求更新该 Lease 的 spec.renewTime 字段,作为“我还活着”的信号。
# 查看节点 lease 对象示例
kubectl get lease -n kube-node-lease node-1 -o yaml
apiVersion: coordination.k8s.io/v1
kind: Lease
metadata:
name: node-1
namespace: kube-node-lease
spec:
holderIdentity: node-1
leaseDurationSeconds: 40 # 租约有效期,默认 40s
renewTime: "2025-03-20T08:15:30Z" # 最近一次续约时间
acquireTime: "2025-03-20T08:00:00Z"
leaseTransitions: 0
- leaseDurationSeconds:默认 40 秒。如果超过该时间未收到续约,控制器认为节点心跳丢失。
- renewTime:Kubelet 每次续约时更新此时间戳。
- 更新方式:Kubelet 使用
PATCH /apis/coordination.k8s.io/v1/namespaces/kube-node-lease/leases/node-1,仅更新renewTime字段,负载极小。
kube-node-lease 命名空间中对应节点的 Lease 对象的 renewTime 是否停滞。如果停滞时间超过 leaseDurationSeconds(默认 40s),说明心跳已中断。
4.3 NodeStatus 更新机制
NodeStatus 更新是 Kubelet 向 API Server 发送完整节点状态(包括 Ready、MemoryPressure、DiskPressure、PIDPressure 等条件)的 PUT 请求。默认每 60 秒执行一次。
# NodeStatus 中的核心条件字段
status:
conditions:
- type: Ready
status: "True" # True / False / Unknown
lastHeartbeatTime: "2025-03-20T08:15:30Z"
lastTransitionTime: "2025-03-20T08:00:00Z"
reason: KubeletReady
message: "kubelet is posting ready status"
- type: MemoryPressure
status: "False"
...
- 更新频率:由
--node-status-update-frequency控制,默认 60s。 - 更新方式:Kubelet 使用 PUT 请求替换整个
status子资源,数据量远大于 Lease。 - 证书要求:需要 kubelet 客户端证书对 API Server 进行身份认证。证书轮换失败时,PUT 请求会被 401/403 拒绝。
4.4 心跳超时阈值与节点状态转换
节点状态转换由 Node Controller(运行在 kube-controller-manager 中)驱动,它同时监控 Lease 和 NodeStatus 的更新情况。
节点状态转换的完整时间线如下:
- Lease 续约中断(证书失效后 10s 内):Kubelet 无法 PATCH Lease,
renewTime停滞。 - Lease 超时(约 40s 后):Node Controller 检测到 Lease 过期,将节点条件
Ready置为Unknown。 - NodeStatus 更新失败(约 60s 后):Kubelet 无法 PUT NodeStatus,控制器确认节点失联。
- 节点状态转为 NotReady(约 60s + 5s 控制器周期):Node Controller 将
Ready条件置为False,状态变为NotReady。 - Pod 驱逐开始(默认 5 分钟后):如果节点持续 NotReady,kube-controller-manager 开始驱逐该节点上的 Pod。
NodeHasSufficientMemory、NodeHasNoDiskPressure 等条件正常,但 Ready 条件变为 Unknown 或 False。同时 kubelet 日志中出现 Unauthorized 或 certificate has expired 错误。此时 Lease 对象的 renewTime 会停留在证书过期前的最后成功续约时间。
4.5 证书轮换对心跳路径的影响
Kubelet 使用 TLS 客户端证书 向 API Server 发起所有请求,包括 Lease 续约和 NodeStatus 更新。证书轮换流程如下:
- 正常轮换:Kubelet 在证书到期前向 API Server 发起 CSR,获取新证书,自动加载并继续使用新证书发送心跳。
- 轮换失败场景:
- CSR 被拒绝(RBAC 权限不足)
- 证书签名服务不可用(kube-controller-manager 故障)
- 新证书未及时下发(网络延迟或 etcd 性能问题)
- Kubelet 未正确加载新证书(文件权限或路径错误)
一旦证书失效,Kubelet 对 API Server 的所有请求都会返回 401 Unauthorized 或 403 Forbidden,导致 Lease 和 NodeStatus 更新同时失败。
kubectl get lease -n kube-node-lease <node-name> -o jsonpath='{.spec.renewTime}' — 检查最后续约时间kubectl get node <node-name> -o jsonpath='{.status.conditions[?(@.type=="Ready")]}' — 查看节点 Ready 条件journalctl -u kubelet -n 50 --no-pager | grep -i "certificate\|unauthorized\|renew" — 查看 kubelet 日志中的证书相关错误