我不会起名字322 · 后端 / 算法 / 数据库
文章目录
- [节点 NotReady:kubelet 与容器运行时的 4 步排查](#节点 NotReady:kubelet 与容器运行时的 4 步排查)
-
- [一、NotReady 意味着什么](#一、NotReady 意味着什么)
- [二、第 1 步:看 Conditions 定位方向](#二、第 1 步:看 Conditions 定位方向)
- [三、第 2 步:到节点上看 kubelet](#三、第 2 步:到节点上看 kubelet)
- [四、第 3 步:看节点资源](#四、第 3 步:看节点资源)
- [五、第 4 步:看网络插件和 CNI](#五、第 4 步:看网络插件和 CNI)
- 六、恢复之后要做什么
- 七、预防比排查重要
- 八、带走物
节点 NotReady:kubelet 与容器运行时的 4 步排查
一、NotReady 意味着什么
节点 Ready=False 时,控制面会做两件事:
- 自动给节点打上污点
node.kubernetes.io/not-ready:NoSchedule,新 Pod 不再调度上去; - 默认 5 分钟后 (由
--pod-eviction-timeout控制)开始驱逐该节点上的 Pod。
所以 NotReady 不是"节点慢",而是节点上的工作负载正在被迁移。这就是为什么一个节点抖动会引发一片 Pod 重启。
bash
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# node-3 NotReady <none> 30d v1.28.2
第一步永远是:
bash
kubectl describe node node-3
看 Conditions 和 Events,那里通常已经写了原因。
二、第 1 步:看 Conditions 定位方向
bash
kubectl describe node node-3 | grep -A 20 Conditions
text
Conditions:
Type Status LastHeartbeatTime Reason Message
---- ------ ----------------- ------ -------
MemoryPressure False Mon, 13 Oct 2026 00:15:01 KubeletHasSufficientMemory kubelet has sufficient memory available
DiskPressure False Mon, 13 Oct 2026 00:15:01 KubeletHasNoDiskPressure kubelet has no disk pressure
PIDPressure False Mon, 13 Oct 2026 00:15:01 KubeletHasSufficientPID kubelet has sufficient PID available
Ready False Mon, 13 Oct 2026 00:12:33 KubeletNotReady container runtime is not ready
四个 Condition 各有含义:
| Condition | True 意味着 | 常见原因 |
|---|---|---|
MemoryPressure |
可用内存低于阈值 | 节点内存真的不够 / 有 Pod 内存泄漏 |
DiskPressure |
剩余磁盘/镜像空间不足 | 容器日志、镜像层、emptyDir 堆积 |
PIDPressure |
进程数接近上限 | 某个 Pod 疯起线程 |
Ready |
------ | kubelet 挂了 / 容器运行时挂了 / 网络插件没起来 |
Ready=False 的 Reason 字段是最直接的线索:
KubeletNotReady+container runtime is not ready→ 容器运行时(containerd/docker)有问题;KubeletNotReady+PLEG is not healthy→ PLEG 超时(老版本常见,通常也是运行时卡了);KubeletNotReady+ 无更多说明 → kubelet 进程本身或网络插件问题;- 心跳时间(
LastHeartbeatTime)停更 → kubelet 挂了或者和 API Server 失联。
三、第 2 步:到节点上看 kubelet
这一步必须 SSH 到节点上,控制面看不到节点内部的状况。
bash
# 1. kubelet 进程活着吗
systemctl status kubelet
# 2. 看 kubelet 日志(最重要)
journalctl -u kubelet -n 100 --no-pager
# 3. 关键错误关键字
journalctl -u kubelet -n 500 --no-pager | grep -iE "error|failed|not ready|PLEG|runtime"
常见错误与含义
错误 1:PLEG is not healthy: pleg was last seen active ... ago
PLEG(Pod Lifecycle Event Generator)负责检测 Pod 状态变化。它超时通常意味着:
- 容器运行时响应慢(节点负载过高);
- 节点上运行了太多 Pod;
- 运行时有 goroutine 卡死。
bash
# 看运行时状态
systemctl status containerd
journalctl -u containerd -n 100 --no-pager
错误 2:container runtime is not ready
bash
# 检查 containerd
systemctl status containerd
ctr version
# 老版本用 docker
systemctl status docker
docker info
错误 3:证书过期
text
x509: certificate has expired or is not yet valid
bash
# 看 kubelet 客户端证书
ls -la /var/lib/kubelet/pki/
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates
错误 4:与 API Server 失联
bash
# 从节点上测试
curl -k https://<apiserver>:6443/healthz
网络插件崩了也会导致这个(Pod 网络不通,kubelet 无法上报)。
四、第 3 步:看节点资源
即使 Ready 是因为别的原因,资源耗尽的排查也值得单独做:
bash
# 内存
free -h
# 磁盘(DiskPressure 最常见的原因)
df -h
df -i # inode 也可能满
# 看谁占了磁盘
du -sh /var/lib/containerd/* 2>/dev/null | sort -rh | head
du -sh /var/log/pods/* 2>/dev/null | sort -rh | head
磁盘是最常见的 NotReady 诱因,三个主要来源:
| 来源 | 清理方式 |
|---|---|
| 容器标准输出日志 | 配 logrotate,或者用 emptyDir 大小限制 |
| 未清理的镜像层 | crictl rmi --pruned / 配 imageGCHighThresholdPercent |
| emptyDir / 宿主路径卷堆积 | 查 du,清业务数据 |
kubelet 自带的垃圾回收参数:
text
--image-gc-high-threshold=85 # 磁盘用到 85% 开始清镜像(默认 85)
--image-gc-low-threshold=80 # 清到 80% 停(默认 80)
默认是开启的,但清理有延迟,突发的写盘可能来不及。
另一个高频:inode 耗尽 。很多小文件(比如某个应用疯狂写小日志)会把 inode 吃光,df -h 看着还有空间但 df -i 是 100%。
五、第 4 步:看网络插件和 CNI
如果 kubelet 正常、运行时正常、资源也够,但节点还是 NotReady,八成是 CNI。
bash
# 看 CNI 插件的 Pod(以 calico 为例)
kubectl get pod -n kube-system -o wide | grep -iE "calico|flannel|cilium"
# 到节点上看 CNI 配置文件
ls /etc/cni/net.d/
# 应该是有效的配置,空的或者残留 .conf 都会导致失败
# CNI 二进制在不在
ls /opt/cni/bin/
常见症状:
cni plugin not initialized→ CNI 配置没就绪;- CNI Pod 在该节点上 CrashLoop → 网络插件自己挂了;
- 节点重启后 CNI 配置丢失。
六、恢复之后要做什么
节点恢复 Ready 后,还有几件事:
bash
# 1. 确认污点已自动移除
kubectl describe node node-3 | grep -i taint
# 2. 确认节点可以调度(不是被 cordon 了)
kubectl get nodes | grep SchedulingDisabled
kubectl uncordon node-3 # 如果是被手动 cordon 的
# 3. 看有没有 Pod 卡在 Terminating(节点失联期间常出现)
kubectl get pods -A --field-selector=spec.nodeName=node-3 | grep Terminating
# 4. 强制清理卡住的 Pod(谨慎,确认 Pod 真的已经不在运行)
kubectl delete pod <pod> -n <ns> --force --grace-period=0
第 4 步要谨慎:强制删除后,如果容器其实还在某个节点上跑着,会出现"两个同名 Pod 同时运行"的情况,对有状态服务是灾难。先确认容器真的已经停了。
七、预防比排查重要
三条建议:
1. 配 node-problem-detector。 它能在节点真正 NotReady 之前发现问题(内核死锁、OOM、文件系统损坏等),并打上 Event/污点。
bash
kubectl get pod -n kube-system | grep node-problem-detector
2. 给关键服务配 PodDisruptionBudget,避免节点驱逐时一次性把副本全赶走:
yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
spec:
minAvailable: 2
selector:
matchLabels:
app: api
3. 监控节点 Conditions,别等 Pod 被驱逐了才发现:
promql
kube_node_status_condition{condition="Ready",status="true"} == 0
kube_node_status_condition{condition="MemoryPressure",status="true"} == 1
kube_node_status_condition{condition="DiskPressure",status="true"} == 1
八、带走物
排查顺序:
bash
# 1. 控制面:看 Conditions 和 Reason
kubectl describe node <node> | grep -A 20 Conditions
# 2. 节点:kubelet 活着吗,日志说什么
systemctl status kubelet
journalctl -u kubelet -n 100 --no-pager | grep -iE "error|failed|PLEG|runtime"
# 3. 节点:是不是资源压力
free -h; df -h; df -i
du -sh /var/log/pods/* | sort -rh | head
# 4. CNI:网络插件正常吗
kubectl get pod -n kube-system -o wide | grep -iE "calico|flannel|cilium"
ls /etc/cni/net.d/
对应处理:
| Reason | 处理 |
|---|---|
container runtime is not ready |
重启/修复 containerd;查节点负载 |
PLEG is not healthy |
运行时卡了,重启 containerd;节点负载过高要扩容 |
DiskPressure |
清日志/镜像层/emptyDir;查 inode |
MemoryPressure |
查内存泄漏的 Pod;或节点规格不够 |
| 证书过期 | 续签 kubelet 客户端证书 |
| CNI 未初始化 | 查 CNI 配置和插件 Pod |
一句话结论:NotReady 的排查核心是 describe node 里的 Reason 字段 + 节点上的 journalctl -u kubelet;前者告诉你方向,后者告诉你具体原因,两者缺一不可。