Kubernetes 节点 NotReady 排障:从 kubelet 到 containerd 的完整检查路径
Kubernetes 节点 NotReady 排障:从 kubelet 到 containerd 的完整检查路径
NotReady 不是原因,而是结果
生产集群里最容易误判的告警之一,就是节点突然变成 NotReady。很多人第一反应是重启 kubelet,甚至直接重启节点。这样有时会恢复,但也很容易把真正原因盖掉:磁盘 inode 满了、containerd socket 权限异常、CNI 配置丢了、证书过期、节点时间漂移、或者 kubelet 和 containerd 之间的 CRI 调用失败。
NotReady 更准确的理解是:控制面认为这台 Node 的健康链路不可信。它不是单个组件的错误码,而是多个信号聚合后的结果。
本文的排障原则很简单:
-
先从 Kubernetes API 看到的事实开始,不要直接登录节点乱翻日志。
-
先判断故障层级,再决定处理动作。
-
每一步都要有命令、关注字段和下一步判断,不写“看情况”。
-
涉及重启 kubelet、containerd、改 CNI 或清理磁盘前,先确认影响范围和回滚方式。
一、先划清 NotReady 的链路边界
一台 Node 要保持 Ready,至少依赖下面几层都能正常工作:
| 层级 | 组件或对象 | 主要职责 | 异常时常见表现 |
|---|---|---|---|
| Kubernetes API 层 | Node 对象、Events、Conditions | 记录控制面看到的节点状态 | Ready=False、Unknown、事件持续刷新 |
| kubelet 层 | kubelet.service |
上报节点状态、管理 Pod 生命周期 | heartbeat 中断、PLEG 异常、CRI 调用失败 |
| CRI 层 | /run/containerd/containerd.sock、crictl |
kubelet 与容器运行时通信 | crictl info 失败、runtime not ready |
| containerd 层 | containerd.service |
拉镜像、创建容器、管理 sandbox | containerd 日志报错、shim 异常 |
| CNI 网络层 | /etc/cni/net.d/、/opt/cni/bin/ |
给 Pod 配网络 | NetworkUnavailable、sandbox 网络创建失败 |
| 系统资源层 | 磁盘、inode、内存、PID、时间、证书 | 提供节点基础运行条件 | Pressure、证书报错、系统服务异常 |
这张边界表很关键。NotReady 排障不是“把所有日志都看一遍”,而是沿着这条链路逐层收敛。

二、第一层:看 Node Conditions,先判断告警入口
先不要登录节点,先从控制面拿到 Node 状态。
kubectl get nodes -o wide
用途:确认是哪台节点 NotReady,以及它的角色、版本、内网 IP、容器运行时版本。 判断标准:如果只有单台节点异常,优先按节点本地问题查;如果一批节点同时异常,要先怀疑控制面、网络、证书、镜像仓库或统一配置变更。
kubectl describe node <node-name>
用途:查看 Conditions、Events、Taints、资源容量和最近状态变化。 判断标准:重点看 Ready、MemoryPressure、DiskPressure、PIDPressure、NetworkUnavailable 的 Status、Reason、Message 和 LastTransitionTime。
Node Conditions 可以按下面这张表判断:
| Condition | 正常状态 | 异常现象 | 优先下一步 |
|---|---|---|---|
Ready |
True |
False 或 Unknown |
看 kubelet 日志和节点 heartbeat |
MemoryPressure |
False |
True |
查内存、驱逐事件、异常 Pod |
DiskPressure |
False |
True |
查磁盘、inode、imagefs、containerfs |
PIDPressure |
False |
True |
查进程数、fork 失败、异常进程 |
NetworkUnavailable |
False |
True |
查 CNI 配置、CNI Pod、节点路由 |
如果 Ready=Unknown,通常说明控制面已经收不到 kubelet 心跳;如果 Ready=False,通常说明 kubelet 还能上报,但它认为节点运行条件不满足。
继续看事件:
kubectl get events -A --field-selector involvedObject.kind=Node,involvedObject.name=<node-name> --sort-by=.lastTimestamp
用途:按时间顺序查看这台节点相关事件。 判断标准:如果事件里出现 NodeNotReady、NodeHasDiskPressure、ContainerGCFailed、ImageGCFailed、KubeletNotReady,不要跳过,后面要按对应层级验证。
三、第二层:查 kubelet 服务是否还能工作
登录异常节点后,先看 kubelet 服务状态。
systemctl status kubelet --no-pager
用途:确认 kubelet 进程是否在运行。 判断标准:active (running) 只是说明进程还在,不代表节点健康;如果是 failed、频繁重启、activating,先看本机日志。
journalctl -u kubelet -n 200 --no-pager
用途:查看最近 200 行 kubelet 日志,快速判断是否有明显错误。 判断标准:重点搜索 PLEG is not healthy、container runtime is down、CRI、cgroup、certificate、NetworkPluginNotReady、eviction manager。
journalctl -u kubelet --since "30 min ago" --no-pager
用途:把观察窗口收窄到故障发生前后,避免被旧日志干扰。 判断标准:如果最近 30 分钟内持续出现同类错误,按错误关键词进入对应层级;如果没有新日志,要查 kubelet 是否已无法启动或 journald 是否异常。
常见 kubelet 日志可以这样归类:
| 日志关键词 | 可能原因 | 下一条命令 | 处理方向 |
|---|---|---|---|
container runtime is down |
kubelet 连不上 containerd 或 CRI 不健康 | crictl info |
查 socket、endpoint、containerd 状态 |
PLEG is not healthy |
runtime 响应慢、Pod 状态同步卡住 | crictl ps -a |
查 containerd、异常容器、节点压力 |
NetworkPluginNotReady |
CNI 配置或插件异常 | ls -l /etc/cni/net.d /opt/cni/bin |
查 CNI 配置和 CNI DaemonSet |
eviction manager |
磁盘、内存、inode 压力 | df -h && df -i |
先判断是否触发驱逐 |
x509 或 certificate |
证书过期、时间漂移、CA 不一致 | date -R |
查证书有效期和节点时间 |
cgroup |
kubelet/containerd cgroup driver 或 cgroup v2 异常 | cat /var/lib/kubelet/config.yaml |
核对 cgroup driver |
不要看到 kubelet 日志里有 container runtime is down 就立刻重启 kubelet。kubelet 是发现问题的一层,containerd 或 CRI socket 才可能是实际断点。
四、第三层:查 CRI endpoint 和 containerd socket
Kubernetes 1.24 之后,很多生产集群已经没有 Docker shim,节点排障要习惯用 crictl 看 CRI。
先看 crictl 配置:
cat /etc/crictl.yaml
用途:确认 crictl 连接的 runtime endpoint。 判断标准:生产 containerd 节点通常应指向 unix:///run/containerd/containerd.sock;如果文件不存在,crictl 可能会尝试多个默认 endpoint,排障输出会变慢且容易误导。
建议配置示例:
runtime-endpoint: unix:///run/containerd/containerd.sock image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 debug: false
再看 socket 是否存在:
ls -l /run/containerd/containerd.sock
用途:确认 containerd CRI socket 文件是否存在、权限是否合理。 判断标准:如果文件不存在,先查 containerd 是否启动;如果权限异常,确认执行用户是否有权限,kubelet 通常以 root 运行,但手工 crictl 可能受用户权限影响。
执行 CRI 连通性检查:
crictl info
用途:确认 CRI 能否返回 runtime 基本信息。 判断标准:能返回 runtimeType、runtimeVersion、config,说明 CRI 基本可用;如果报 connection refused、deadline exceeded、context deadline exceeded,继续查 containerd。
crictl pods
用途:确认 Pod sandbox 列表能否读取。 判断标准:如果 crictl info 成功但 crictl pods 卡住,可能是 runtime 响应慢、sandbox 数据异常或节点资源压力。
crictl ps -a
用途:查看容器列表,包括已退出容器。 判断标准:大量 Exited、Unknown、频繁重启的容器,可能会放大 kubelet PLEG 或 GC 压力。
五、第四层:查 containerd 服务和日志
如果 CRI 不通,接下来查 containerd。
systemctl status containerd --no-pager
用途:确认 containerd 是否运行。 判断标准:active (running) 只能说明主进程还在;如果频繁重启或启动失败,马上看日志。
journalctl -u containerd -n 200 --no-pager
用途:查看 containerd 最近错误。 判断标准:重点看 failed to load cni、failed to create shim task、snapshotter、no space left on device、permission denied、failed to pull and unpack image。
ctr -n k8s.io containers list
用途:从 containerd 自身视角查看 k8s 命名空间下容器。 判断标准:如果 ctr 可用而 crictl 不通,问题更可能在 CRI 插件或 endpoint;如果 ctr 也卡住,containerd 本身或系统资源更可疑。
生产提醒:不要在没有确认影响面的情况下直接 systemctl restart containerd。重启 containerd 可能影响该节点上已有 Pod 的状态感知和后续管理。建议先做三件事:
-
保存日志:
journalctl -u containerd --since "2 hours ago" --no-pager > /tmp/containerd-notready.log -
确认节点上业务 Pod:
kubectl get pod -A -o wide --field-selector spec.nodeName=<node-name> -
能驱逐则先
kubectl drain,不能驱逐则和业务负责人确认窗口。
六、第五层:查 CNI 配置和网络插件
如果 Node Conditions 或 kubelet 日志出现 NetworkUnavailable、NetworkPluginNotReady、cni config uninitialized,进入 CNI 层。
ls -l /etc/cni/net.d/
用途:确认节点上是否有 CNI 配置文件。 判断标准:目录为空、配置文件后缀不对、最近被误删,都会导致 kubelet 认为网络插件未就绪。
ls -l /opt/cni/bin/
用途:确认 CNI 插件二进制是否存在。 判断标准:配置里引用的插件,例如 calico、flannel、bridge、loopback,必须能在该目录找到对应二进制。
kubectl get pod -A -o wide | grep -E 'calico|flannel|cilium|weave|kube-router'
用途:查看常见 CNI 组件 Pod 是否正常。 判断标准:如果同一节点上的 CNI DaemonSet Pod 不健康,先看该 Pod 日志和 Events;如果全局 CNI 都异常,要看网络插件控制面。
ip route
用途:查看节点路由是否异常。 判断标准:默认路由丢失、Pod CIDR 路由缺失、CNI 网卡不存在,都可能导致节点网络不可用。
七、第六层:查磁盘、inode、内存、PID、时间和证书
很多 NotReady 最后不是 kubelet 或 containerd 的 bug,而是系统基础条件坏了。
df -h
用途:检查磁盘空间。 判断标准:根分区、/var/lib/containerd、/var/lib/kubelet 所在分区如果超过 85%-90%,要关注 kubelet eviction 和 image GC。
df -i
用途:检查 inode。 判断标准:磁盘容量没满但 inode 100%,一样会导致容器、日志、临时文件创建失败。
free -h
用途:检查内存压力。 判断标准:可用内存极低且 kubelet 日志出现 eviction,要继续查高内存 Pod 和系统进程。
ps -eLf | wc -l
用途:粗略检查线程或进程数量。 判断标准:数量接近系统限制,且 Node Condition 出现 PIDPressure=True,需要查异常进程。
date -R
用途:确认节点时间。 判断标准:如果时间明显漂移,证书校验和控制面通信都可能异常;先修 NTP/chrony,再判断 kubelet 是否恢复。
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates
用途:检查 kubelet client 证书有效期。 判断标准:如果证书过期或不在有效期内,要按集群证书轮转方案处理,不要手工随意替换。
资源压力判断表:
| 现象 | 检查命令 | 异常判断 | 处理方向 |
|---|---|---|---|
DiskPressure=True |
df -h |
kubelet/containerd 分区接近满 | 清理无用镜像、日志,必要时扩容 |
DiskPressure=True 但容量未满 |
df -i |
inode 接近 100% | 清理小文件、异常日志目录 |
MemoryPressure=True |
free -h |
可用内存极低 | 查异常 Pod,必要时驱逐或扩容 |
PIDPressure=True |
ps -eLf \| wc -l |
线程/进程数异常 | 查 runaway 进程,调大限制前先定位来源 |
| 证书相关报错 | openssl x509 ... -dates |
过期或未生效 | 按集群证书轮转流程恢复 |
| 大量 x509/timeout | date -R |
时间漂移 | 恢复 NTP/chrony,再重试 kubelet |
八、把排障路径串起来:一张最小流程图

可以把现场排障压缩成下面这个顺序:
-
kubectl get nodes -o wide:确认异常范围,是单节点还是批量节点。 -
kubectl describe node <node-name>:看 Conditions、Events、Pressure 和时间点。 -
journalctl -u kubelet -n 200 --no-pager:从 kubelet 日志找到入口关键词。 -
cat /etc/crictl.yaml:确认 CRI endpoint 是否指向 containerd socket。 -
ls -l /run/containerd/containerd.sock:确认 socket 是否存在。 -
crictl info:验证 kubelet 到 runtime 这条链路是否通。 -
systemctl status containerd --no-pager:确认 containerd 服务状态。 -
journalctl -u containerd -n 200 --no-pager:查 runtime 侧具体错误。 -
ls -l /etc/cni/net.d/ /opt/cni/bin/:确认 CNI 配置和插件。 -
df -h && df -i && free -h:确认资源压力。 -
date -R和证书检查:排除时间和证书问题。 -
处理前先确认是否需要
cordon、drain、备份日志和保留回滚路径。
九、生产处理建议:先隔离,再修复,再验证
如果节点已经 NotReady,先判断是否还承载业务 Pod。
kubectl get pod -A -o wide --field-selector spec.nodeName=<node-name>
用途:确认这台节点上还有哪些 Pod。 判断标准:如果还有关键业务 Pod,不要直接重启 containerd 或节点;先确认业务是否已经被副本接管。
必要时先禁止新 Pod 调度:
kubectl cordon <node-name>
用途:防止新的 Pod 调度到异常节点。 判断标准:kubectl get node <node-name> 显示 SchedulingDisabled,说明已生效。
如果节点还能被 kubelet 正常管理,且业务允许迁移,再考虑驱逐:
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
用途:把普通 Pod 从节点迁走,为维修留窗口。 判断标准:如果被 PDB 阻塞,不要强行加 --force;先确认业务可用性。
修复后验证:
kubectl uncordon <node-name>
用途:恢复节点调度。 判断标准:只在 Ready=True、关键 DaemonSet 正常、crictl info 正常、CNI Pod 正常后执行。
最终确认:
kubectl get node <node-name> -o wide kubectl describe node <node-name> | sed -n '/Conditions:/,/Addresses:/p'
用途:确认节点状态和 Conditions 已恢复。 判断标准:Ready=True,Pressure 类 Condition 为 False,Events 不再持续刷同类错误。
十、一页式 Checklist
| 检查项 | 命令 | 通过标准 |
|---|---|---|
| 异常范围 | kubectl get nodes -o wide |
只影响预期节点,或已确认批量故障原因 |
| Conditions | kubectl describe node <node-name> |
Ready/Pressure/NetworkUnavailable 有明确判断 |
| Node Events | kubectl get events -A --field-selector involvedObject.kind=Node,involvedObject.name=<node-name> --sort-by=.lastTimestamp |
事件指向明确层级 |
| kubelet 状态 | systemctl status kubelet --no-pager |
服务运行或失败原因明确 |
| kubelet 日志 | journalctl -u kubelet -n 200 --no-pager |
关键词已归类 |
| CRI 配置 | cat /etc/crictl.yaml |
endpoint 指向正确 socket |
| CRI 连通性 | crictl info |
能返回 runtime 信息 |
| containerd 状态 | systemctl status containerd --no-pager |
服务运行且无持续重启 |
| containerd 日志 | journalctl -u containerd -n 200 --no-pager |
无持续 no space、permission、shim 错误 |
| CNI 配置 | ls -l /etc/cni/net.d/ /opt/cni/bin/ |
配置和插件二进制存在 |
| 磁盘与 inode | df -h && df -i |
关键分区未触发压力 |
| 时间与证书 | date -R、openssl x509 ... -dates |
时间正确,证书有效 |
总结
节点 NotReady 的关键不是“先重启哪个服务”,而是先回答三个问题:
-
控制面看到的异常入口是什么?看 Node Conditions 和 Events。
-
kubelet 还能不能正常工作?看 kubelet 服务、日志和关键词。
-
kubelet 往下依赖的 runtime、CNI、系统资源哪一层断了?用 CRI、containerd、CNI 和系统命令逐层验证。
生产排障里,最值钱的是稳定的路径。路径稳定,处理动作才不会变成碰运气。
参考资料
-
Kubernetes 官方文档:Nodes、Node status、Node pressure eviction
-
Kubernetes 官方文档:Debugging Kubernetes nodes
-
Kubernetes 官方文档:Container Runtime Interface
-
containerd 官方文档与 GitHub 项目说明
-
CRI tools 项目文档:
crictl配置与常用命令
更多推荐




所有评论(0)