当K8s节点“失联”:一次完整的故障排查与根因分析实录

深夜,告警铃声划破了宁静。监控大屏上,一个Kubernetes工作节点(Node)的状态从“Ready”跳变成了“NotReady”。集群虽然还在运行,但部分Pod已经飘红,服务开始出现零星超时。作为一名运维老兵,我知道,一次标准的“节点失联”排障之旅开始了。这不仅仅是敲几个命令,更是一次对集群健康状况的深度体检。下面,就是我这次排查的完整记录,希望能为你下次遇到类似问题提供清晰的路径。
第一步:确认问题与初步定位
首先,别慌。登录到Kubernetes控制平面节点,用最直接的命令看看集群节点状态。
kubectl get nodes
输出清晰地显示,名为 `node-worker-02` 的节点状态是 `NotReady`。`kubectl describe node node-worker-02` 命令能提供更多细节,但有时节点失联会导致这个命令卡住或返回超时。这时,我的习惯是转向集群的“上帝视角”——查看控制平面组件的日志。
# 查看kube-controller-manager日志,它负责节点状态管理
kubectl logs -n kube-system kube-controller-manager-master-node --tail=50 | grep -i node-worker-02
# 或者查看kubelet自己上报的状态(如果apiserver还能收到信息)
kubectl describe node node-worker-02 | grep -A 10 -B 5 Conditions
在 `describe` 命令的输出中,我重点关注 `Conditions` 字段。如果看到 `Ready` 状态为 `False`,并且 `Reason` 显示 `NodeStatusUnknown` 或 `KubeletStop`,这通常意味着控制平面(apiserver)已经有一段时间(默认40秒的心跳超时时间,加上5分钟的容忍窗口)没有收到来自该节点上 `kubelet` 的心跳了。
第二步:登陆问题节点,检查Kubelet
既然控制平面联系不上节点,那就直接SSH到 `node-worker-02` 上。这是最关键的一步,大部分根因都在这里。
1. 检查kubelet进程:
systemctl status kubelet
果然!服务处于 `inactive (dead)` 状态。尝试启动它:`sudo systemctl start kubelet`。如果启动失败,立刻查看日志:
sudo journalctl -u kubelet --since="1 hour ago" -f
踩坑提示: 我遇到过几次因为节点磁盘空间满(`/var` 或 `/` 分区)导致 kubelet 无法写入证书或日志而崩溃的情况。所以,启动前先顺手检查一下磁盘:
df -h
第三步:深入挖掘:资源与依赖
如果kubelet能启动但节点状态依然无法恢复,或者kubelet反复崩溃,就需要深入了。
2. 检查容器运行时: Kubelet 依赖容器运行时(如 containerd 或 docker)。
systemctl status containerd # 或用 docker,取决于你的CRI
sudo ctr images list # 检查containerd是否正常响应
有一次,一个异常的容器进程耗尽了PID,导致 containerd 无法创建新的容器,间接拖死了 kubelet。
3. 检查网络插件: 节点失联也可能是网络问题。Calico、Flannel等插件的Pod是否在该节点上运行正常?
# 在控制平面节点查看该节点上的系统Pod
kubectl get pods -n kube-system -o wide | grep node-worker-02
如果网络插件Pod是 `CrashLoopBackOff` 状态,需要进一步排查该节点的网络配置(如网卡、路由、防火墙)。实战经验: 我就曾被一台物理服务器上的防火墙规则坑过,它清除了CNI插件所需的iptables链。
第四步:根因浮现与恢复
经过一番排查,这次的问题根源是:系统内存不足,触发OOM Killer,杀掉了kubelet进程。 在 `journalctl` 日志中看到了清晰的 `Out of memory` 记录。
查看内存使用情况:
free -h
top
发现是一个部署在该节点上的应用Pod发生了内存泄漏,吃光了资源。临时解决方案是重启该Pod(或从控制平面驱逐节点上的Pod):
# 从控制平面驱逐节点上的所有Pod(会触发在其他健康节点重建)
kubectl drain node-worker-02 --ignore-daemonsets --delete-emptydir-data
注意: `drain` 命令在节点恢复通信后才能成功。如果节点完全失联,你可能需要强制删除Pod:`kubectl delete pod
在清理了问题Pod并确认kubelet稳定运行后,将节点重新加回集群调度:
kubectl uncordon node-worker-02
最后,通过 `kubectl get nodes` 确认节点状态恢复为 `Ready`。
总结:构建你的排查清单
一次“节点失联”事件,其实就是对以下清单的依次检查:
- 控制平面视角: `kubectl get/describe node` 查看状态和事件。
- 节点基础服务: SSH登录,检查 `kubelet`、`容器运行时` 进程与日志。
- 系统资源: 磁盘空间、内存、CPU、PID数是否耗尽?
- 网络与依赖: 网络插件Pod是否正常?节点网络连通性(到apiserver)是否完好?
- 底层基础设施: 如果是云主机,检查云提供商控制台;物理机则检查硬件(如NIC、RAID卡)状态。
每次排查都是一次学习。建议将关键的检查命令写成脚本,或在监控系统中为节点资源(内存、磁盘、kubelet心跳)设置更灵敏的预警,这样才能在“失联”演变为“灾难”前,优雅地将其化解。希望这篇实录能让你在下一次告警响起时,更加从容。

这情况我也遇到过,内存爆了真的头疼