K8s 集群 “3 节点 2 个 NotReady“ 故障排查全记录 K8s 集群 “3 节点 2 个 NotReady” 故障排查全记录小刘运维的排错实战从飞书搭机器人到修好整个 K8s 集群再到产品化巡检工具上周接了一个运维咨询的单子客户有一套 3 节点的 K8s 集群v1.27.16运行了快一年最近发现node-2 和 node-3 双双 NotReady只有 node-1 还在坚守岗位。于是有了今天这场排错实战。整个过程从调试巡检脚本开始到发现根因并修复最后把巡检工具产品化——推送到 GitHub、对接飞书机器人实现自动推送。记录成文希望对同行有帮助。一、问题现象k8s集群3 个node节点2 个 NotReadykubectl get nodes二、排查过程全记录1: 看节点详情kubectl describenodenode-2|grep-A20Conditions输出显示所有 condition 全部显示Unknown最后一个心跳时间是 3 天前Ready Unknown ... Kubelet stopped posting node status. MemoryPressure Unknown ... Kubelet stopped posting node status. DiskPressure Unknown ... Kubelet stopped posting node status. PIDPressure Unknown ... Kubelet stopped posting node status.关键信息Kubelet stopped posting node status— kubelet 还在跑但连不上 API Server 了。2: 查 kubelet 日志journalctl-ukubelet --no-pager-n20日志里反复出现同一行错误dial tcp 192.168.91.254:6443: i/o timeoutkubelet 在尝试连接 API Server 的 VIP192.168.91.254:6443但全部超时。3: 测试 VIP 连通性从 master-1 测试[rootmaster-1 ~]# ping 192.168.91.25464bytes from192.168.91.254:icmp_seq1ttl64time0.414ms ✅[rootmaster-1 ~]# curl -sk https://192.168.91.254:6443/healthz{kind:Status,code:401}# 401 是正常的说明 API 可达 ✅从 node-2 测试[rootnode-2 ~]# ping 192.168.91.2543packets transmitted,0received,100% packet loss ❌[rootnode-2 ~]# curl -sk https://192.168.91.254:6443/healthz... 超时...有意思了master 能连 VIPworker 不能。4: ARP 探测发现关键线索# 从 master-1 看 VIP 的 MAC[rootmaster-1 ~]# ip neigh show 192.168.91.254192.168.91.254 dev ens32 lladdr 00:0c:29:86:74:65 REACHABLE# 从 node-2 看 VIP 的 MAC[rootnode-2 ~]# ip neigh show 192.168.91.254192.168.91.254 dev ens32 lladdr 00:50:56:ec:2f:c0 REACHABLE同一个 VIP不同的 MAC 地址这显然有问题。00:0c:29:86:74:65是谁的 MAC查一下[rootmaster-1 ~]# ssh root192.168.91.20 ip link show ens32 | grep etherlink/ether 00:0c:29:86:74:65原来是master-3192.168.91.20持有 VIP。而00:50:56:ec:2f:c0则是另一个未知设备在网络上冒领了 ARP 请求。根因找到了VIP 在 master-3 上但 worker 节点解析到的 MAC 地址是错的导致流量被引向一个不可达的设备。5: 看看 keepalived 怎么回事检查各 master 的 keepalived 状态ipaddr show ens32|grep254inet192.168.91.254/24 scope global secondary ens32 ← VIP 在 master-3 上keepalived 的 VRRP 协议用的是优先级选主三个 master 优先级都是 100。同优先级时 IP 大的胜出所以 VIP 总是在 master-3192.168.91.20上。而 master-3 和 node-2/3 之间的网络存在ARP 冲突——有一个设备在冒充 VIP 的 MAC 地址导致 node-2/3 的流量被误引。三、修复过程1.修复方案最简单的方案让 VIP 漂到一个所有节点都能访问的 master 上。停掉 master-3 的 keepalivedsystemctl stop keepalivedVIP 自动漂移到 master-2192.168.91.19ipa|grep2542.验证修复效果再次从 node-2 测试 VIPping192.168.91.25464bytes from192.168.91.254:icmp_seq2ttl64time0.465ms ✅[rootnode-2 ~]# curl -sk https://192.168.91.254:6443/healthz{kind:Status,code:401}✅节点状态检查Flannel 网络自动恢复之前因为连不上 ClusterIP 10.0.0.1 而 CrashLoopBackOffkubectl get pods-nkube-flannel至此集群全面恢复。四、复盘总结1.根因图谱keepalived VIP 在 master-3 上 ↓ 网络上存在 ARP 冲突不明设备抢答 VIP 的 MAC ↓ node-2/3 解析到错误的 MAC 地址 ↓ kubelet 连不上 API Serveri/o timeout ↓ NodeNotReady、Flannel CrashLoopBackOff2.为什么 node-1 没事node-1 的 IP 是 192.168.91.21恰好和 master-3 也有良好的网络连通性。所以同一个 ARP 问题只影响 node-2/3。3.教训与改进教训改进措施依赖 VIP 连接 API Server 有单点风险每个节点 kubelet 配置多个 master IP 做 fallbackkeepalived 主从策略不清晰明确设置优先级避免同优先级靠 IP 排序的隐性规则没有巡检机制部署巡检脚本天级别健康检查没有告警通知对接飞书出问题第一时间知道五、附巡检工具产品化既然修好了顺便做了产品化1. 巡检脚本 → GitHub 开源仓库地址https://github.com/liuxing141/k8s-health-check一键运行检查 9 大项节点健康、Pod 状态、资源使用、异常事件、安全基线、Helm Release、TLS 证书等。2. 对接飞书机器人写了个 Python 脚本跑完巡检自动推送到飞书python3 k8s-health-check-feishu.py报告直接飞到手机再也不用 SSH 上去看了。3. 定时任务309* * *cd~/k8s-health-check-repopython3 k8s-health-check-feishu.py每天早上 9 点半自动跑一遍有问题第一时间知道。六、总结这次排错虽然花了不少时间但收获很大VIP ≠ 高可用如果底层网络有问题VIP 反而带来更多麻烦巡检不是可有可无如果没有巡检报告出问题了只能被动等客户投诉工具要产品化同样的脚本能不能在下一个客户那直接用能的话就是一杆枪希望这篇文章对你的运维工作有帮助。有问题欢迎交流 作者一名不想上班的 SRE巡检工具https://github.com/liuxing141/k8s-health-check