节点 NotReady:kubelet 与容器运行时的 4 步排查

我不会起名字322 · 后端 / 算法 / 数据库

📘 技术栈 | 🧩 力扣 Hot100 | 🐹 Go 项目 | 🟥 Redis | 🐬 MySQL

文章目录

  • [节点 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 时,控制面会做两件事:

  1. 自动给节点打上污点 node.kubernetes.io/not-ready:NoSchedule,新 Pod 不再调度上去;
  2. 默认 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;前者告诉你方向,后者告诉你具体原因,两者缺一不可。

相关推荐
闲云自留地3 小时前
K8s Service 全解:服务发现、四种类型、会话保持、金丝雀发布一篇通
容器·kubernetes·服务发现
分布式存储与RustFS3 小时前
GPU 在空转等数据?PyTorch 训练集进 RustFS 的三种接法
云原生·开源·对象存储·分布式存储·s3·rustfs·性能基准
时间售卖铺6 小时前
MySQL 备份
运维·数据库·mysql·云原生
对象存储与RustFS7 小时前
GPU 在空转等数据?PyTorch 训练集进 RustFS 的三种接法
后端·docker·云原生
fb_123459 小时前
K8s 网络通关手册:Ingress-Nginx 生产级路由 + Calico 三种隧道模式 + NetworkPolicy 白名单实战
网络·nginx·kubernetes
KIDULT°10 小时前
K8s 网络进阶实战:Ingress-Nginx、Calico 与 NetworkPolicy 实验 + 排坑记录
网络·nginx·云原生·kubernetes
fengkai454510 小时前
7、Kubernetes的Ingress&Network&Scheduler
运维·kubernetes·k8s·ingress·network·scheduler
孔令飞1 天前
知己知彼,百战不殆:AI SKILL 实现原理剖析
云原生·go·ai编程
分布式存储与RustFS1 天前
把 LSM 引擎直接架在对象存储上:slatedb 能不能跑在 RustFS 上?
运维·云原生·开源·对象存储·分布式存储·s3·性能基准