Kubernetes 基础体系 · 第 61/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。
Kubernetes Node 生命周期:Condition、Lease、Taint、NotReady 和恢复
Kubernetes 中的 Node 不是“服务器在线/离线”的简单映射。一个节点同时包含:
- Node 对象:API Server 中描述节点身份、容量、可分配资源、条件和调度属性的 API 对象;
- Condition:节点当前健康状态的结构化判断;
- Lease:kubelet 面向控制面的轻量心跳;
- Taint:节点对 Pod 调度或驻留施加的排斥规则;
- Pod 状态:节点故障后,Pod 是否被标记、驱逐、重建;
- 节点控制器行为:根据心跳和 Condition 修改 Node、添加 Taint,并触发 Pod 处理。
因此,“Node 变成 NotReady”不是一个单独事件,而是一条状态传播链:
flowchart LR
K[kubelet] -->|更新 Lease| L[kube-node-lease Lease]
K -->|更新 NodeStatus| N[Node.status.conditions]
L --> NC[Node lifecycle controller]
N --> NC
NC -->|Ready=False/Unknown| C[节点 Condition]
NC -->|添加 not-ready/unreachable Taint| T[Taint]
T --> S[调度器]
T --> E[Pod NoExecute 处理/驱逐]
S --> P[新 Pod 调度]
E --> P
理解这条链路,才能区分以下几个经常被混淆的事实:
- Condition 是状态判断,不是调度规则。
- Lease 是心跳,不等于完整的 Node 状态。
- Taint 是排斥规则,不等于节点已经宕机。
- NotReady 可能来自 kubelet、运行时、网络、资源压力,也可能只是控制面收不到节点心跳。
- 节点恢复后,状态、Taint、Pod 和调度资格不一定同时恢复。
一、Node 对象的两个重要部分:Spec 与 Status
一个简化的 Node 对象如下:
apiVersion: v1
kind: Node
metadata:
name: worker-1
spec:
taints:
- key: node.kubernetes.io/maintenance
effect: NoSchedule
unschedulable: false
status:
conditions:
- type: Ready
status: "True"
reason: KubeletReady
message: kubelet is posting ready status
lastHeartbeatTime: "..."
lastTransitionTime: "..."
capacity:
cpu: "8"
memory: 32Gi
pods: "110"
allocatable:
cpu: "7800m"
memory: 30Gi
pods: "110"
1. spec 描述期望或调度属性
Node 的 spec 主要包含:
spec.unschedulable:是否禁止新的普通 Pod 调度到此节点;spec.taints:节点施加的 Taint;- 一些节点配置或提供商相关字段。
kubectl cordon 修改的就是:
spec:
unschedulable: true
它不表示节点故障,只表示调度器不应把新的、普通的 Pod 放到这里。
2. status 描述观察结果
Node 的 status 包含:
conditions:节点健康条件;capacity:节点理论容量;allocatable:扣除系统预留、 kubelet 预留等之后,可供 Pod 使用的容量;addresses:InternalIP、Hostname 等地址;nodeInfo:kubelet、容器运行时和操作系统信息。
kubelet 是 Node 状态的主要报告者,但最终看到的状态还会经过 API Server、Node 生命周期控制器和其他控制器处理。因此,kubectl get node 展示的是控制面当前存储和计算出的结果,不一定等于某一时刻主机上的瞬时状态。
二、Condition:节点健康状态的结构化表示
Node Condition 是一个数组,每个元素至少包含:
type: Ready
status: "True"
reason: KubeletReady
message: kubelet is posting ready status
lastHeartbeatTime: "2025-01-01T12:00:00Z"
lastTransitionTime: "2025-01-01T11:50:00Z"
其中:
type:条件类型,例如Ready;status:通常是"True"、"False"或"Unknown";reason:机器可读的原因标识;message:面向人的补充说明;lastHeartbeatTime:该条件最近一次被 kubelet 观察或报告的时间;lastTransitionTime:该条件的status最近一次发生变化的时间。
不同 Kubernetes 版本和 API 结构可能对时间字段的推荐方式存在变化。诊断时应以当前集群实际返回的 API 字段为准,不应依赖某个字段永远存在。
1. 常见 Condition 类型
Ready
表示节点是否可以运行 Pod:
True:节点被认为可用;False:kubelet 明确报告节点不可用;Unknown:控制面在规定时间内无法确认节点状态。
Ready=False 与 Ready=Unknown 的含义不同:
False更接近“节点明确告诉控制面:我不能正常工作”;Unknown更接近“控制面没有收到足够的新信息”。
MemoryPressure
表示节点内存压力。
它不一定意味着物理内存已经完全耗尽。kubelet 会基于可用内存、工作集、驱逐阈值等指标判断是否进入压力状态。压力出现时,kubelet 可能驱逐 Pod,节点控制器还可能为节点添加:
node.kubernetes.io/memory-pressure
DiskPressure
表示节点本地磁盘或 inode 压力。例如:
- 容器镜像占满磁盘;
- 容器日志持续增长;
emptyDir使用过多;- inode 耗尽。
常见相关 Taint 为:
node.kubernetes.io/disk-pressure
PIDPressure
表示节点可用进程 ID 接近上限。一个容器创建大量进程、僵尸进程未回收或系统进程限制过低,都可能导致这种压力。
相关 Taint 通常为:
node.kubernetes.io/pid-pressure
NetworkUnavailable
表示节点网络配置尚不可用。这个 Condition 常与云控制器、CNI 或节点网络初始化有关,并不等价于所有网络请求都失败。
常见相关 Taint 为:
node.kubernetes.io/network-unavailable
2. Condition 与 Ready 的关系
不能简单地写成:
任何一个 Condition 为 False,Ready 就一定为 False。
实际行为取决于 kubelet 的节点状态计算、网络插件、运行时和控制器实现。MemoryPressure=True 表示资源压力,不自动等价于 Ready=False;节点可能仍然能运行已有 Pod,但调度器会因压力相关 Taint 拒绝新 Pod。
一个节点可能呈现:
Ready True
MemoryPressure True
DiskPressure False
PIDPressure False
这表示节点还能工作,但处于内存压力状态。此时“节点在线”和“节点适合继续接收工作”是两个不同问题。
三、Lease:低成本心跳,不是 Node 状态的替代品
Kubernetes 使用 Lease 对象作为节点心跳。默认情况下,Lease 位于:
kube-node-lease
例如:
kubectl get lease -n kube-node-lease worker-1 -o yaml
可能看到:
apiVersion: coordination.k8s.io/v1
kind: Lease
metadata:
name: worker-1
namespace: kube-node-lease
spec:
holderIdentity: worker-1
leaseDurationSeconds: 40
renewTime: "2025-01-01T12:00:10Z"
关键字段含义:
holderIdentity:当前持有 Lease 的节点身份;leaseDurationSeconds:Lease 在多长时间没有续租后可被认为过期;renewTime:最近一次续租时间。
1. 为什么需要 Lease
完整更新 Node status 包含较多字段,例如地址、容量、Condition 和运行时信息。若所有节点频繁更新完整 Node 对象,大规模集群中会增加 API Server、etcd 和 Watch 流量。
Lease 只更新一个轻量对象:
kubelet → Lease.renewTime
因此控制面可以先通过 Lease 判断节点最近是否仍然有心跳,而不必每次都解析完整的 Node 状态。
2. Lease 与 NodeStatus 的区别
二者解决的问题不同:
| 对象 | 主要作用 | 能说明什么 | 不能单独说明什么 |
|---|---|---|---|
| Lease | 轻量心跳 | kubelet 最近仍能访问 API Server 并续租 | kubelet、运行时、网络和资源是否完全健康 |
| NodeStatus | 完整状态 | kubelet 对 Ready、压力、地址和容量的报告 | 控制面当前是否一定能继续收到新心跳 |
例如:
- kubelet 进程仍在运行,并且可以访问 API Server,因此 Lease 正常续租;
- 但容器运行时卡死,Pod 无法创建,kubelet 可能报告
Ready=False。
反过来:
- kubelet 进程可能仍然健康;
- 但节点到 API Server 的网络断开,Lease 无法续租;
- 控制面最终只能把节点状态视为
Unknown。
所以 Lease 正常不代表业务一定正常;Lease 过期也不一定代表主机已经断电。
3. Lease 的时间推导
设:
- :Lease 的最近续租时间;
- :控制器当前时间;
- :
leaseDurationSeconds; - :节点控制器允许的心跳宽限时间。
当:
或者实现依据等价的节点监控逻辑判断心跳超时后,节点就可能进入不可确认状态。
这里的公式表达的是“心跳已超过可接受窗口”的直觉。具体触发还受控制器同步周期、时钟偏差、API Server 延迟和版本实现影响,不能把它当作毫秒级 SLA。
Kubernetes 的默认参数在不同版本、发行版和部署方式中可能变化。诊断时应查看实际组件参数,例如:
kubectl -n kube-system get pods -l component=kube-controller-manager
kubectl -n kube-system describe pod <kube-controller-manager-pod>
自托管集群还可以查看控制器启动参数中的:
--node-monitor-grace-period
--node-monitor-period
云厂商托管控制面通常不允许直接查看或修改这些参数。
四、NotReady:明确不可用和无法确认不可用
kubectl get nodes 通常把 Node 的 Ready Condition 映射成简化状态:
kubectl get nodes
示例:
NAME STATUS ROLES AGE VERSION
worker-1 Ready <none> 20d v1.31.2
worker-2 NotReady <none> 20d v1.31.2
但 STATUS 列不是 Node 的完整状态模型。应使用以下命令获得真实原因:
kubectl describe node worker-2
或:
kubectl get node worker-2 \
-o jsonpath='{range .status.conditions[*]}{.type}{"\t"}{.status}{"\t"}{.reason}{"\t"}{.message}{"\n"}{end}'
1. Ready=False
典型情况是 kubelet 能够向 API Server 报告,但判断自身不能正常运行 Pod,例如:
- 容器运行时不可用;
- CNI 初始化失败;
- kubelet 无法与运行时通信;
- 节点磁盘、内存或 PID 达到严重阈值;
- kubelet 配置错误;
- kubelet 健康检查失败。
此时 Node 可能具有类似条件:
Ready False KubeletNotReady container runtime is down
这是一种“节点仍能说话,但明确报告自己不可用”的状态。
2. Ready=Unknown
典型情况是控制面收不到新状态:
- 主机断电;
- 节点到 API Server 的网络中断;
- kubelet 崩溃;
- API Server 或中间网络异常;
- 证书、认证或授权问题阻止 kubelet更新 Lease;
- 节点严重卡死。
此时控制面并不能证明节点一定已经关机,只能表示“没有足够的新心跳”。
例如:
Ready Unknown NodeStatusUnknown Kubelet stopped posting node status
3. NotReady 不等于 Pod 已经停止
这是最重要的边界之一。
当 Node 变成 NotReady 或 Unknown 时,控制面看到的是节点状态变化;节点上的进程是否真的停止,要看:
- kubelet 是否还活着;
- 容器运行时是否还能运行容器;
- 网络是否仍然可达;
- 进程是否仍在使用存储和外部系统。
如果主机已经与控制面隔离,但仍在运行业务进程,就可能形成“脑裂”风险:控制面把 Pod 重新调度到其他节点,而旧节点上的原进程尚未真正停止。
因此,不能因为 API Server 显示 NotReady 就立即认定旧 Pod 已经安全终止。
五、Taint:节点对 Pod 的排斥规则
Taint 位于:
spec:
taints:
- key: example.com/maintenance
value: "true"
effect: NoSchedule
Toleration 位于 Pod:
spec:
tolerations:
- key: example.com/maintenance
operator: Equal
value: "true"
effect: NoSchedule
二者的关系是:
- Taint 表示节点排斥某类 Pod;
- Toleration 表示 Pod 可以容忍某类 Taint;
- Toleration 不是强制调度规则,只是移除某个排斥条件;
- Pod 仍必须满足资源、亲和性、拓扑、节点选择器等其他条件。
1. 三种 Effect
NoSchedule
新的 Pod 如果不容忍该 Taint,则不能调度到节点。
已有 Pod 通常不会因为添加 NoSchedule 而被驱逐。
PreferNoSchedule
调度器尽量避免把不容忍的 Pod 放到该节点,但这不是绝对禁止。资源和其他约束不足时,仍可能调度过去。
NoExecute
不仅影响新的 Pod,还影响已经运行的 Pod:
- 不容忍该 Taint 的 Pod 会被驱逐;
- 带有
tolerationSeconds的 Pod,在容忍时间结束后被驱逐; - 没有时间限制的 Toleration 可以持续容忍。
示例:
tolerations:
- key: node.kubernetes.io/not-ready
operator: Exists
effect: NoExecute
tolerationSeconds: 300
它表示 Pod 可以在带有该 Taint 的节点上继续存在 300 秒,之后可能被驱逐。
2. 节点故障相关的内置 Taint
Node 生命周期控制器通常使用以下 Taint 表达节点问题:
node.kubernetes.io/not-ready:NoExecute
node.kubernetes.io/unreachable:NoExecute
node.kubernetes.io/memory-pressure:NoSchedule
node.kubernetes.io/disk-pressure:NoSchedule
node.kubernetes.io/pid-pressure:NoSchedule
node.kubernetes.io/network-unavailable:NoSchedule
其中:
not-ready通常对应节点明确处于Ready=False;unreachable通常对应控制面无法确认节点状态;- 压力类 Taint 通常阻止新的 Pod 调度,但不必然立即驱逐已有 Pod;
NoExecute才是直接影响已有 Pod 驻留的 Effect。
具体 Taint 是否由哪个控制器添加、何时删除,受 Kubernetes 版本和控制器配置影响。生产环境不要只根据一次命令输出推断完整时序,应同时检查 Condition、Lease、事件和控制器日志。
3. unschedulable 与 Taint 的区别
kubectl cordon worker-1:
worker-1 cordoned
其效果是设置:
spec:
unschedulable: true
这通常阻止新的普通 Pod 调度,但不会自动删除已有 Pod。
而:
kubectl taint nodes worker-1 example.com/maintenance=true:NoSchedule
是添加一个显式 Taint。只有不具备相应 Toleration 的 Pod 才会被阻止。
二者都可以影响调度,但表达的语义不同:
cordon:节点整体暂不接收新的普通调度;- Taint:节点排斥某一类不匹配的 Pod;
drain:主动迁移或删除节点上的可驱逐工作负载。
六、从心跳丢失到 Pod 处理:完整故障路径
考虑一个节点 worker-2,其 kubelet 在 12:00:00 后无法续租 Lease。
第一步:Lease 停止更新
12:00:00 最后一次 renewTime
12:00:10 预期续租,但没有成功
12:00:20 仍未成功
...
失败原因可能是:
- kubelet 停止;
- 节点网络中断;
- API Server 不可达;
- TLS 证书过期;
- ServiceAccount 或节点身份授权失败。
此时不能只看“节点机器是否 ping 得通”,因为 kubelet访问的是 API Server 的 HTTPS 接口,并且还需要正确的证书、身份和 RBAC 权限。
第二步:Node 控制器判断心跳超时
控制器比较当前时间与 Lease 的 renewTime。超过监控宽限后,Node 的 Ready 可能变成 Unknown。
示意状态:
Ready=True
↓ Lease 超时
Ready=Unknown
如果 kubelet恢复通信后明确报告自身不健康,则可能是:
Ready=True
↓ kubelet 报告运行时故障
Ready=False
这两个方向不完全相同。
第三步:添加故障 Taint
控制器可能添加:
node.kubernetes.io/unreachable:NoExecute
或者:
node.kubernetes.io/not-ready:NoExecute
这会开始影响不具备对应 Toleration 的 Pod。
第四步:Pod 是否被驱逐
假设 Pod 没有显式配置 Toleration,而集群准入行为为它添加了默认的故障容忍时间,则 Pod 可能在一段时间内保持运行状态,随后被标记驱逐。
但有几个重要例外:
- DaemonSet Pod 通常具有特殊的节点故障容忍行为;
- 静态 Pod 不由 API Server 中的普通 Pod 控制器管理;
- 宿主机网络、存储和业务副作用可能不会随着 API 对象删除立即消失;
- Pod 被删除后,Deployment、StatefulSet 等控制器才可能在其他节点创建替代副本;
- Pod 绑定的本地卷、拓扑约束和副本数会影响是否能成功重建。
第五步:调度器处理新 Pod
调度器在为新 Pod 选择节点时,会检查:
- 节点是否
unschedulable; - Pod 是否容忍节点 Taint;
- 节点是否有足够的
allocatable资源; - 节点选择器、亲和性、反亲和性是否匹配;
- 拓扑分布、卷绑定、端口和其他过滤条件是否满足。
因此,“去掉 Taint”并不意味着 Pod 一定能调度成功。
七、一个可复现的 Taint 与 Toleration 示例
创建一个临时节点 Taint:
kubectl taint node worker-1 demo=true:NoSchedule
预期结果:
node/worker-1 tainted
检查:
kubectl describe node worker-1 | grep -A5 -i taints
预期可以看到:
Taints: demo=true:NoSchedule
此时,一个没有对应 Toleration 的 Pod,即使资源充足,也不能调度到 worker-1。事件中通常会看到类似:
0/3 nodes are available: 1 node(s) had untolerated taint {demo: true}, ...
删除 Taint:
kubectl taint node worker-1 demo=true:NoSchedule-
注意最后的 - 是删除语法,不是给 Taint 设置空值。
再创建带 Toleration 的 Pod:
apiVersion: v1
kind: Pod
metadata:
name: tolerating-pod
spec:
tolerations:
- key: demo
operator: Equal
value: "true"
effect: NoSchedule
containers:
- name: pause
image: registry.k8s.io/pause:3.10
应用并检查:
kubectl apply -f tolerating-pod.yaml
kubectl get pod tolerating-pod -o wide
这个 Pod 只是“允许”进入带有 demo=true:NoSchedule 的节点,并不保证一定会被调度到该节点。若需要指定节点,还必须额外使用 nodeSelector、节点亲和性或其他调度约束。
八、诊断 NotReady 的正确顺序
诊断应先区分“节点明确不可用”与“控制面收不到心跳”,再检查节点本身。
1. 先获取三类信息
kubectl get node worker-2 -o wide
kubectl describe node worker-2
kubectl get lease -n kube-node-lease worker-2 -o yaml
重点观察:
Ready是False还是Unknown;reason和message;lastHeartbeatTime、lastTransitionTime;- Lease 的
renewTime; - Taints;
Conditions中是否有资源压力;- Events 中是否出现运行时、磁盘或网络错误。
2. 检查节点端 kubelet
在节点上执行:
sudo systemctl status kubelet
sudo journalctl -u kubelet --since "30 min ago" --no-pager
常见日志方向:
failed to update node status:更新 Node 状态失败;failed to renew lease:Lease 续租失败;container runtime is down:CRI 运行时不可用;PLEG is not healthy:Pod 生命周期事件生成器没有及时处理;network plugin is not ready:CNI 尚未准备好;- 证书、认证或授权错误。
PLEG 是 kubelet 用来感知容器状态变化的一部分。它异常时,kubelet 可能无法可靠地同步 Pod,但这不等于容器运行时进程一定已经退出。
3. 检查容器运行时
对于使用 CRI 的节点,可用:
sudo crictl info
sudo crictl ps -a
sudo systemctl status containerd
sudo journalctl -u containerd --since "30 min ago" --no-pager
crictl 需要正确配置 CRI endpoint。不同运行时、发行版和安装方式的 socket 路径可能不同,不应盲目假设 /run/containerd/containerd.sock 一定存在。
要区分:
- kubelet 到运行时的连接失败;
- 运行时到镜像仓库失败;
- CNI 配置失败;
- 单个容器或 Pod 的应用级失败。
4. 检查资源压力
free -h
df -h
df -ih
ps -eLf | wc -l
dmesg -T | grep -i -E 'oom|killed process|disk|filesystem'
重点检查:
- 根文件系统是否已满;
- kubelet 使用的目录是否单独挂载并已满;
- inode 是否耗尽;
- 内核是否触发 OOM;
- PID 上限是否接近;
- 容器日志是否无限增长。
df -h 正常而 df -ih 满,也足以造成磁盘相关故障。
5. 检查 API Server 可达性与身份
在节点上检查 kubelet 使用的配置和证书,确认:
- DNS 可以解析控制面地址;
- TCP/TLS 可以连接 API Server;
- 节点时钟没有严重偏差;
- kubelet 客户端证书没有过期;
- 节点身份仍具备更新 Node 和 Lease 的权限。
如果节点能访问 API Server,但身份无权限,表现可能与网络故障相似:Lease 和 NodeStatus 同样无法更新,但网络本身是通的。
九、恢复流程:修复原因、恢复心跳、验证工作负载
节点恢复不能简化为“执行 kubectl uncordon”。正确顺序是先证明节点工作正常,再决定是否恢复调度。
1. 先修复节点本身
根据故障类型采取对应动作:
- 重启或修复 kubelet;
- 修复 containerd 或其他 CRI 运行时;
- 修复 CNI 配置和网络;
- 清理或扩容磁盘;
- 处理内存和 PID 压力;
- 修复证书、时钟或 API Server 路由;
- 恢复云主机或物理机网络。
修复后先看 kubelet 日志:
sudo journalctl -u kubelet -f
再确认 Lease 更新:
watch -n 2 'kubectl get lease -n kube-node-lease worker-2 -o jsonpath="{.spec.renewTime}{"\n"}"'
如果 renewTime 持续变化,说明 kubelet 至少能够访问并更新 Lease。
2. 确认 Ready 恢复
kubectl get node worker-2
kubectl describe node worker-2
应确认:
Ready True
并检查其他条件:
kubectl get node worker-2 \
-o jsonpath='{range .status.conditions[*]}{.type}{"="}{.status}{" reason="}{.reason}{" message="}{.message}{"\n"}{end}'
仅仅看到 Ready=True 仍不够,还要确认:
MemoryPressure=False;DiskPressure=False;PIDPressure=False;- 网络插件已就绪;
allocatable资源符合预期;- 节点运行时可以创建和删除测试容器。
3. 检查故障 Taint 是否仍存在
kubectl get node worker-2 \
-o jsonpath='{range .spec.taints[*]}{.key}{"="}{.value}{":"}{.effect}{"\n"}{end}'
由节点生命周期控制器管理的 not-ready 或 unreachable Taint,通常会在节点恢复并满足控制器条件后被移除。但以下情况可能仍然存在:
- 运维手动添加的 Taint;
- 资源压力仍然存在;
- 节点仍然带有维护 Taint;
- 控制器尚未完成下一轮同步;
- 云厂商控制器或自定义控制器有额外逻辑。
不要在没有确认故障原因的情况下直接执行:
kubectl taint node worker-2 node.kubernetes.io/unreachable:NoExecute-
手动删除故障 Taint 可能让 Pod 在一个实际上仍不可达的节点上重新调度或继续驻留。
4. 如果此前执行过 cordon,再恢复调度
kubectl uncordon worker-2
预期输出类似:
node/worker-2 uncordoned
然后检查:
kubectl get node worker-2 \
-o jsonpath='{.spec.unschedulable}{"\n"}'
没有输出或输出 false,表示 unschedulable 已清除。
uncordon 只恢复节点的普通调度资格,不会:
- 修复 kubelet;
- 启动容器运行时;
- 删除残留 Taint;
- 恢复被驱逐的 Pod;
- 重新绑定已经失败的卷。
5. 验证实际工作负载
kubectl get pods -A -o wide --field-selector spec.nodeName=worker-2
kubectl get events -A --sort-by=.lastTimestamp
对 Deployment:
kubectl rollout status deployment/<name> -n <namespace>
对 StatefulSet:
kubectl rollout status statefulset/<name> -n <namespace>
还应检查:
- Pod 是否处于
Running且 Readiness 为True; - 是否有
CrashLoopBackOff、ImagePullBackOff; - 是否有 Pod 卡在
Terminating; - PVC 和挂载是否正常;
- Service、Ingress 和业务探针是否恢复;
- 节点上的 CNI、kube-proxy 或等价网络组件是否正常。
十、Node 维护与故障恢复不是同一个流程
主动维护时,推荐使用:
kubectl cordon worker-2
kubectl drain worker-2 --ignore-daemonsets --delete-emptydir-data
其中:
cordon:停止新的普通 Pod 调度;drain:尝试优雅地迁移或删除可驱逐 Pod;--ignore-daemonsets:忽略 DaemonSet Pod,因为它们由 DaemonSet 控制器维持;--delete-emptydir-data:允许删除使用emptyDir的 Pod 数据,存在数据丢失风险。
生产环境还可能需要:
kubectl drain worker-2 \
--ignore-daemonsets \
--delete-emptydir-data \
--grace-period=60 \
--timeout=10m
但参数不是越强制越好。--force、--disable-eviction 等选项可能绕过保护机制,造成:
- 单实例业务中断;
- PodDisruptionBudget 被绕过;
- 本地数据丢失;
- 卷卸载和再次挂载冲突。
故障状态下直接 drain 也未必成功,因为 kubelet 可能无法执行 Pod 删除确认。此时必须先判断旧节点是否仍可能运行工作负载,再决定是等待、隔离、修复节点,还是在确认风险后处理残留对象。
十一、常见误解与反例
误解一:Ready=True 就表示业务正常
反例:
Ready=True
MemoryPressure=False
但应用可能因为数据库连接池耗尽、证书过期或上游不可用而全部返回 500。
Node Ready 只描述节点层面的可运行性,不替代 Pod Readiness、应用探针和业务监控。
误解二:Lease 更新就表示容器运行正常
反例:
- kubelet 线程仍可续租;
- containerd 的某些操作已经卡死;
- Pod 创建、删除或状态收集持续超时。
此时 Lease 可能正常,而 Ready 最终变成 False,或者业务已经不可用。
误解三:Node NotReady 后旧 Pod 一定停止
反例:
- 主机与控制面网络隔离,但进程仍在运行;
- Pod 使用本地磁盘或外部系统写入;
- 控制面在其他节点创建替代副本;
- 原节点恢复后产生两个副本同时访问同一业务资源。
这就是节点脑裂和重复执行风险。对有状态服务、单主服务、外部副作用操作尤其危险。
误解四:删除 Taint 就能恢复节点
反例:
- kubelet 仍然无法续租;
- CRI 仍然不可用;
- CNI 尚未就绪;
- 节点仍然有磁盘压力;
- 节点被
unschedulable标记; - 调度器因资源或亲和性约束仍然拒绝 Pod。
Taint 只是调度和驻留的一层过滤条件,不是修复动作。
误解五:Unknown 就等于主机断电
Unknown 只表示控制面无法获得可信的新状态。API Server 故障、网络 ACL、证书、DNS、时钟和 kubelet 权限问题,都可能造成同样结果。
十二、版本、实现和生产风险边界
1. 当前稳定 API 与版本差异
本文使用的核心 API 为:
Node:v1;Lease:coordination.k8s.io/v1;- Pod Toleration、Node Taint:稳定 API 字段。
但以下内容可能随版本或发行版变化:
- kubelet 更新 NodeStatus 的频率;
- Lease 续租频率;
- Node 控制器检查周期和宽限期;
- 默认故障 Toleration;
- DaemonSet 自动添加的 Toleration;
- 云控制器对节点地址、生命周期和删除的处理;
- 发行版对 kubelet、CRI、CNI 和控制器参数的默认值。
应以实际集群版本对应的 Kubernetes 文档和组件启动参数为准。不要把某个托管 Kubernetes 服务的默认行为当作 Kubernetes API 的普遍保证。
2. 云厂商差异
云环境中还可能存在:
- 云实例被删除后,云控制器删除 Node;
- 云网络故障与主机故障表现不同;
- 节点自动修复或自动替换;
- 托管控制面隐藏 Node 控制器参数;
- 云盘挂载、拓扑区域和实例生命周期影响 Pod 恢复。
因此,节点恢复不仅要看 Kubernetes,还要结合云平台实例状态、网络、磁盘和负载均衡状态。
3. 恢复操作的最大风险:误判旧节点已停止
当 Ready=Unknown 且节点不可达时,最危险的操作不是等待,而是在没有确认旧工作负载已经停止的情况下,强制删除 Pod、解绑卷或让替代副本接管外部资源。
对于有状态工作负载,应额外确认:
- 旧进程是否确实停止;
- 存储系统是否完成 fencing;
- 卷是否允许安全地重新挂载;
- 应用是否具备主从切换或幂等能力;
- 外部任务是否可能重复执行。
Kubernetes 的 Node Condition、Lease 和 Taint 提供的是控制面状态传播机制,不是通用的分布式 fencing 方案。
十三、最终状态模型
可以把节点从正常到故障再到恢复抽象为:
Healthy
├─ Lease 持续续租
├─ Ready=True
├─ 无故障 Taint
└─ 可调度(除非主动 cordon)
│
├─ kubelet 明确报告故障
│ └─ Ready=False
│ └─ not-ready:NoExecute 等 Taint
│
└─ 控制面收不到心跳
└─ Ready=Unknown
└─ unreachable:NoExecute 等 Taint
修复节点与通信
│
├─ Lease 恢复更新
├─ kubelet 重新报告 NodeStatus
├─ Ready=True
├─ 检查并清理仍存在的 Taint
├─ 必要时 uncordon
└─ 验证 Pod、卷、网络和业务健康
这里的关键不是记住几个命令,而是区分状态来源和作用范围:
- Lease 回答“kubelet 最近是否还能向控制面续租”;
- Condition 回答“节点或 kubelet报告的健康状态是什么”;
- NotReady 是
ReadyCondition 的异常表现,可能是False或Unknown; - Taint 回答“哪些 Pod 可以调度到或继续留在该节点”;
- 恢复 必须同时验证心跳、节点健康、Taint、调度资格和工作负载,而不能只看
kubectl get node的一列状态。
系列导航与关联阅读
- 系列入口:Kubernetes 完整学习路线:从 Pod 与控制面到安全、运维和 Operator
- 上一篇:Kubernetes 节点自动扩缩容:Pending Pod、Node Group、缩容和成本
- 下一篇:Kubernetes 节点维护:Cordon、Drain、Eviction、DaemonSet 和恢复验证
- 延伸:Kubelet 与容器运行时:CRI、Pod Sync、PLEG、Probe 和资源状态
官方资料
本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。

评论
0 条讨论