K8s 节点 NotReady 排障:kubelet 与网络故障定位

一、先看清 “NotReady” 到底意味着什么

Kubernetes 集群里最让人心惊的一幕,就是 kubectl get nodes 里某个节点突然变成 NotReady。节点 NotReady 意味着 kubelet 心跳失联,控制面会把它标记为不可用,并触发 NoExecute 污点开始驱逐其上的 Pod,直接影响线上可用性。本文从 kubelet、容器运行时、网络三层,梳理节点 NotReady 的标准排障路径与可落地的修复命令,并附一段真实故障复盘。

要理解 NotReady,先要理解节点的”健康心跳”。每个节点上的 kubelet 会周期性地把自身状态(内存、磁盘、PID、网络可达性)上报给 API Server,频率由 nodeStatusUpdateFrequency 控制,默认 10 秒一次。节点上的 Ready 条件本质上是 kubelet 上报的”我还活着且健康”信号。如果 Controller Manager 在 node-monitor-grace-period(默认 40 秒)内收不到任何更新,就判定节点失联,把 Ready 条件翻成 Unknown,对外显示为 NotReady,并打上 node.kubernetes.io/unreachable 的 NoExecute 污点,把 Pod 驱逐到别的节点。

这里有个常见误区:NotReady 不等于节点”宕机”。节点可能物理上还在运行,只是 kubelet 进程崩了、容器运行时挂了,或者它到 API Server 的 6443 端口被防火墙掐断——这些都表现为 NotReady,但修复方式完全不同。所以在重启大法之前,先用一条命令把现场钉死。

kubectl get nodes -o wide
kubectl describe node <node-name> | sed -n '1,40p'        # 看 Conditions / Events / 资源压力
kubectl get events --field-selector reason=NodeNotReady -A

二、排障总览:三层定位法

经验上看,节点 NotReady 的根因几乎都落在这三层之一:kubelet 自身、容器运行时、节点网络。先按表对齐信号,再逐层下钻,能避免无头苍蝇式地狂重启节点。下面这张表是整套方法论的地图,后面三章逐一展开。

层级核心信号常用命令典型根因
kubelet 层进程退出 / 证书过期systemctl status kubelet证书过期、配置错、OOM
运行时层containerd 异常、DiskPressurecrictl ps / df -h磁盘写满、containerd 崩溃
网络层CNI 掉线、端口不通查 Calico/flannel、telnet 6443CNI 崩溃、防火墙、网卡 down

三、kubelet 自身异常(最常见)

kubelet 是节点上的”大管家”,它既负责把节点状态上报给控制面,也负责真正地把 Pod 跑起来。kubelet 一旦异常,节点立刻失声。这一层占了 NotReady 故障的半壁江山,尤其以”证书过期”和”被 OOM 杀掉”最典型。

3.1 看 kubelet 是否还活着

登录异常节点,第一件事是确认 kubelet 进程状态与最近日志。systemd 场景下如果 kubelet 反复重启,status 会显示 active (auto-restart),而 journalctl 最后几十行通常能直接点出病因——要么是配置文件语法错,要么连不上 API Server。

systemctl status kubelet -l
journalctl -u kubelet -n 100 --no-pager        # 重点看 x509 / failed / refuse

如果看到 failed to connect to API Server 类报错,先别急着怪 kubelet,那往往是网络层或 API Server 侧的问题,跳到第五章再查。若看到 invalid configuration 或 unable to load client CA,才说明是 kubelet 自身配置/证书损坏。

3.2 客户端证书过期(高频坑)

kubelet 访问 API Server 用的客户端证书默认放在 /var/lib/kubelet/pki,有效期 1 年。过期后 kubelet 无法完成 TLS 握手,节点随之 NotReady,日志里满屏 x509: certificate has expired or not yet valid。这是”每年一次的惊喜”头号来源,很多团队第一次踩都是在大半夜收到告警。

openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -enddate
# 过期则轮换(kubeadm 集群)
systemctl stop kubelet
rm -f /var/lib/kubelet/pki/kubelet-client-*.pem
kubeadm kubeconfig user --org system:nodes --client-name system:node:<node>
systemctl start kubelet

对 kubeadm 集群,更稳妥的做法是直接执行 kubeadm certs renew 后再重启 kubelet,它会按现有 CA 重新签发所有到期证书,避免手动删文件带来的权限与软链错乱。轮换完成后记得确认 kubelet 重新变为 Ready,并顺手把”证书有效期”纳入巡检,别让它明年再咬你一口。

3.3 资源耗尽导致 kubelet 被 OOM

节点内存紧张时,OOM Killer 会按打分杀进程,而 kubelet 这种常驻进程一旦被误杀,节点立即失联。用 dmesg -T | grep -i oom 看内核日志,若命中 kubelet,说明节点根本没有给系统组件预留内存。修复办法是在 kubelet 启动参数里加 --kube-reserved 和 --system-reserved,为 kubelet、containerd、sshd 等留出保底内存,避免被业务 Pod 挤占后自杀。

四、容器运行时故障

kubelet 并不直接管容器,它通过 CRI(容器运行时接口)把活交给 containerd(或 docker、 cri-o)。如果运行时挂了,kubelet 既拉不起新 Pod,也上报不了节点状态,节点立刻 NotReady。这类故障在节点重启后 containerd 没起来、或镜像/日志把磁盘写满时最常见。

4.1 containerd 是否健康

systemctl status containerd -l
crictl info            # 正常应返回 runtime 信息
journalctl -u containerd -n 50 --no-pager

crictl info 直接打 CRI 套接字,若它都报错,基本可以确定是 containerd 进程或它的 socket(默认 /run/containerd/containerd.sock)出了问题。此时先 systemctl restart containerd,起不来就看 journald 里是不是 pause 镜像缺失或存储驱动损坏。

4.2 磁盘压力触发 NodePressure

当容器日志或镜像把节点的 /var/lib/containerd、/var/log 写满,kubelet 会进入 DiskPressure,节点被标记为 NotReady 或加上 NoSchedule 污点——Pod 既跑不起来也调度不进去,是比”进程崩”更隐蔽的一种失联。

df -h /var/lib/containerd /var/log
du -sh /var/log/containers/* 2>/dev/null | sort -rh | head
# 清理:containerd 垃圾回收 + journald 瘦身
crictl rmi --prune
journalctl --vacuum-size=500M

根治办法是给容器日志加 max-size / max-file 轮转,并配一个定时清理脚本,别等磁盘 100% 才救火。日志轮转这一项,能挡掉大量”莫名其妙 NotReady”。

五、网络与 CNI 失联

kubelet 到 API Server 走 6443 端口,节点之间 Pod 网络靠 CNI 插件(Calico、Flannel、Cilium 等)打通。任意一环断开,心跳就发不出,表现同样是 NotReady。这一层常被误判成”kubelet 坏了”,其实 kubelet 日志干干净净,问题出在它脚下的网络。

5.1 CNI 插件状态

kubectl get pods -n kube-system | grep -Ei 'calico|flannel|cilium'
# 以 Calico 为例,检查 bird/confd 与节点路由
ip route show | grep -i bird

CNI 的节点级组件(如 Calico 的 node DaemonSet、Flannel 的 flanneld)一旦崩溃,Pod 网络会逐步断裂,kubelet 虽能连上 API Server,却因网络不通被判定异常。此时重启对应 CNI 的节点 Pod 往往立竿见影。

5.2 连通性自检

# 从异常节点测 API Server 6443
timeout 3 bash -c 'cat < /dev/null > /dev/tcp/<api-server-ip>/6443' && echo "6443 OK" || echo "6443 FAIL"
# 同时确认防火墙/安全组放通 6443(kube-apiserver)与 10250(kubelet API)

云上集群尤其要查安全组:节点到 API Server 的 6443、控制面到节点的 10250 任一条被误删规则掐断,都会让节点集体 NotReady。这类问题往往发生在某次安全组”顺手改一下”之后。

六、实战速查清单

把上面的三层浓缩成一张速查表,排障时照着打勾即可,不必每次从零想。

症状先看修复
节点突然 NotReady、无 Pod 漂移kubelet 日志 x509轮换 kubelet 证书
NotReady + DiskPressuredf -h清理日志/镜像
仅个别节点 NotReadyCNI Pod / 网卡重启 CNI 节点 Pod / 查网卡
全集群节点 NotReady控制面/网络查 API Server、etcd、安全组

七、一次真实故障复盘

去年我们踩过最典型的一坑:凌晨两点三台工作节点同时 NotReady,但 API Server、etcd 都正常。按本文方法三层扫下来,kubelet 日志无报错、containerd 健康,问题落在网络层——ip route 里 Calico 的 bird 路由整片消失。根因是节点内核升级后某次重启触发了网卡的 offload 参数变更,把 VXLAN overlay 包给丢了。修复就是重启 Calico node DaemonSet 让它重新下发路由,并在节点内核参数里固化 offload 配置。复盘结论是:全集群同时 NotReady 九成是网络或控制面,别去一台台重启 worker。

八、把”被动救火”变成”提前预警”

节点 NotReady 最好在发生前就被告警。用 Prometheus 抓取 kube-state-metrics 的 kube_node_status_condition{condition="Ready",status="false"},配一条告警即可在节点失联瞬间通知你(告警接入可参考本站 Prometheus+Grafana 监控告警实战)。同时把 kubelet 证书过期、磁盘使用率纳入定期巡检,把”每年一次的惊喜”彻底消灭在告警里。想系统建立集群认知,可结合 Kubernetes 入门实战、K8s Pod 调度与故障排查 与 etcd 高可用运维 一起看。

节点 NotReady 看似吓人,本质逃不出 kubelet、运行时、网络三层的某一个。掌握这套三层定位法,配合监控预警,多数故障都能在分钟级定位并修复,把对线上的影响压到最低。

上一篇 Function Calling 实战:让大模型调用你的 API 与工具
下一篇 从零手写 AI Agent:ReAct 智能体实战
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]