
前阵子凌晨两点多值班手机连续弹出告警——不是 K8s 的节点告警而是业务监控里的访问失败率突破阈值。等我睡眼惺忪打开kubectl get nodesmaster-01 那一行 STATUS 栏明晃晃写着 Ready脑袋一下就清醒了。再仔细查这套集群里 master-01 的物理网卡早就掉线交换机侧链路都 down 了可 Kubernetes 死活不把它标成 NotReady。业务血崩节点却喊 Ready这个场景让我当时花了大半夜才彻底搞明白。这篇文章就把“master-01 网卡宕机后不显示 NotReady”这件事拆开讲先还原现场再讲 K8s 节点状态判定机制为什么和物理网卡状况脱钩然后给出一套从网卡到集群状态的排查流程最后聊聊怎么靠告警和探测手段让这类假死节点尽早现形。适合正在维护生产集群、又经常被网络故障折磨的运维和 SRE 同学参考内容不依赖特定发行版麒麟、CentOS、Ubuntu 系的思路通用。1. 故障现场业务已经血崩节点却还喊 Ready1.1 那次凌晨的处理记录起初我以为是自己看错或者kubectl连到了别的上下文。先用kubectl config current-context确认无误后开始挨个排查kubectl get nodes -o wide确认 master-01 的 INTERNAL-IP 是 192.168.10.11。kubectl describe node master-01看 Conditions所有条件都是 False 或正常ReadyTrueMemoryPressureFalseDiskPressureFalsePIDPressureFalse。控制面确实认为这个节点健康。登录节点看ip addr发现 eth1 状态是 DOWNcarrier 为 0而 kubelet 上报用的地址是 eth0 上的 192.168.10.11eth0 链路正常。看到这里的第一反应是这是一台双网卡机器。eth0 是管理口用于 kubelet 和 API Server 通信eth1 是业务口承载业务流量。业务网卡断了但控制面链路完好所以节点在 Kubernetes 眼里依然是健康的。这个现象本身不复杂但如果你想当然地认为“节点状态掉线一定等于网络异常”就很容易把自己带进沟里。1.2 先弄清楚“网卡宕机”到底指哪一层做运维这些年“网卡宕机”这个词在不同人口中指的是完全不同的事。我把它分成三层每一层对 Kubernetes 的可见性都不一样故障层面典型现象系统视角Kubernetes 能否感知物理链路层网线松动、光模块故障、交换机端口 downip link显示 state DOWNcarrier 0ethtool的 Link detected: no通常不会除非心跳路径也断设备驱动层驱动死循环、固件异常接口可能显示 UP但 dmesg 出现 soft lockup / NIC Link is Down收发包几乎为零基本无法感知心跳可能正常到达或延迟系统配置层NetworkManager/network 服务异常、ONBOOTno、残留 bond 配置接口 up 但 IP 缺失或重启后网卡不启动如果 API Server 地址配不上节点会 NotReady否则影响不大这个表格里的驱动层最坑人ip命令看到接口 UP不代表网络真的可用。很多初级排查只看到ip link显示 UP 就以为网卡没问题结果实际抓包抓不到任何报文。dmesg 和/proc/net/dev里的累计错误计数才是更靠谱的判断依据。明确了这层分类再回头看 NotReady 没有出现的原因就顺理成章Kubernetes 的节点健康状态本质上是“控制面对 kubelet 心跳的可达性”它既不知道物理链路状态也不知道驱动是否在空转。2. NotReady 为什么没出现K8s节点健康机制与网卡故障的错位2.1 kubelet心跳、node controller判定与40秒规则先把官方机制复习一遍因为所有判断都建立在这之上。以当前主流版本v1.26的默认值为例kubelet 的--node-status-update-frequency默认 10s每 10s 向 API Server 上报一次节点状态kube-controller-manager 的--node-monitor-period默认 5s循环检查节点心跳时间戳--node-monitor-grace-period默认 40s超过 40s 没有收到节点的心跳更新开始把节点标记为 NotReadyCondition 变为 Unknown 或 False--pod-eviction-timeout默认 5 分钟再往后开始驱逐该节点上的 Pod。正常情况下物理网卡彻底断掉、心跳完全断了之后最多 40 秒加一个检查周期kubectl get nodes里就会看到 NotReady。这不是玄学node controller 就是干这个的。但问题在于心跳完全断掉只是网卡故障的一种结果而不是必然结果。2.2 网卡掉线但心跳未断的三条典型路径第一条多网卡/多平面。管理面走 eth0业务面走 eth1。eth1 掉了eth0 照常。节点继续上报心跳K8s 层面永远认为节点健康。这个场景在物理机集群和部分虚拟机集群里非常常见也是我个人踩坑最多的地方。第二条bond 主备但备链路没真正可用。比如 bond0 由 eth0eth1 组成eth0 主链路断了bond 把流量切到 eth1。从ip link看 bond0 是 UP 的kubelet 上报心跳也正常。但如果 eth1 连的交换机端口 VLAN 没放通、或者网线质量差业务流量就会大面积丢包。这时候节点状态正常业务却在慢慢血崩。第三条驱动假死。网卡驱动在内核里 soft lockup接口从外部看可能还是 UP内核报错也未必立刻影响 kubelet 的本地端口监听。kubelet 自己在 10250 端口监听本机回环仍然通到 API Server 的上报报文如果在驱动队列里被卡死心跳延迟会变得很不稳定但只要单次延迟小于 40s节点状态就不会被翻转为 NotReady。这三条路径背后共同的结论是节点 Ready 不代表节点上的网络链路可用只代表 kubelet 和 API Server 的链路可用而且这个“可用”还非常宽泛丢包率达到 20% 也算“可用”。2.3 /healthz 的盲区kubelet活着不等于网络健康很多人习惯性把节点状态和 kubelet 健康划等号实际上 kubelet 的/healthz只是本机 HTTP 端口。curl http://127.0.0.1:10248/healthz返回 ok只能说明 kubelet 进程活着、它在处理本机请求完全不依赖外部网络收发数据。类似地节点上的 MemoryPressure、DiskPressure、PIDPressure 这些条件都由 kubelet 通过 cAdvisor 从本地文件系统采集也不需要外部网络。换句话说Kubernetes 对节点健康的判定本质上是一个“进程存活 控制面链路可达”的判定。它从来没有承诺过“节点健康 节点对外网络完全正常”。这两者的错位是“网卡宕机后不显示 NotReady”这类问题最根本的原因。3. 从状态到链路一套完整的网卡故障排查链路3.1 用 kubectl 做“两侧对照”第一步是确认控制面的判断是否准确别上来就登录节点瞎折腾。我习惯按这个顺序来kubectl get nodes -o wide kubectl describe node master-01 | sed -n /Conditions:/,$p kubectl get pod -n kube-system -o wide | grep master-01先看节点的 INTERNAL-IP 和当前 Pod 分布。然后从另一台正常节点去探测 master-01ping -c 4 192.168.10.11 nc -zv 192.168.10.11 10250如果 ping 和 kubelet 端口都通说明控制面链路确实没问题这时候再发现业务端口比如 NodePort 30000-32767、或业务网卡 IP 上的端口不通基本可以断定故障发生在业务网卡或业务链路上。这一步做完你心里就有了一张“哪些链路通、哪些链路断”的对照图而不是拿着一个笼统的“节点状态异常/业务异常”乱猜。3.2 系统层排查从接口状态到驱动日志登录节点后我按四个层级往下查每层都有自己的关键命令接口层ip addr、ip link show eth1、ip route链路物理层ethtool eth1重点看 Speed、Duplex 和 Link detected统计层ip -s link或cat /proc/net/dev看 RX/TX errors、dropped、overruns 是否异常增长驱动/内核层dmesg -T | grep -i -E eth1|ixgbe|e1000e|bond|link|watchdog|soft lockup举个例子。有一次排查ip link show eth0显示 UP但业务就是断的。ethtool eth0给出的 Link detected: yesSpeed 却显示 Unknown!。dmesg里连续刷ixgbe 0000:02:00.0 eth0: NIC Link is Down和NIC Link is Up。这说明链路在反复抖动物理层或者光模块已经有问题。只看ip命令的人完全发现不了这层问题。再比如 soft lockup。dmesg里出现BUG: soft lockup - CPU#6 stuck for 22s!对应网卡中断被长时间占用驱动基本已经卡死。这时候网卡型号如果是 ixgbe、i40e、realtek 这些优先怀疑驱动或固件先用ethtool -i eth1看 driver version 和 firmware-version再决定是重装驱动、升级固件还是干脆换网卡。3.3 麒麟v10上重启后网卡不启动的几个经典坑这次故障之后我翻了大量相关经验帖发现“麒麟 v10 命令重启后为什么网卡不启动”是很多人遇到的高频问题而且同样会引发节点假死。顺手总结最常见的几个坑ONBOOTno。/etc/sysconfig/network-scripts/ifcfg-eth1里默认是ONBOOTno系统重启后接口不会自动拉起。解决办法是改成 yes然后ifup eth1。NetworkManager 和 network 服务冲突。银河麒麟默认启用 NetworkManager你手动systemctl restart network之后NetworkManager 可能又把配置改回去。建议用nmcli device status和nmcli connection show先看清楚当前由谁管理。接口名漂移。内核枚举顺序一变原来的 eth0 可能变成 ens32f0 或 eth2脚本和监控里写死的接口名全部失效。尤其要注意/etc/udev/rules.d/70-persistent-net.rules是否有残留规则。残留 bond 配置。nmcli connection show里能看到一堆旧 bond 配置文件加新网卡时 ifup 会读取到错误的 master 关系导致 IP 起不来。麒麟 v10 下的排查命令序列我一般这么跑nmcli device status nmcli connection show cat /etc/sysconfig/network-scripts/ifcfg-eth1 systemctl status NetworkManager journalctl -u NetworkManager --since 10 minutes ago | tail -n 50记住一个原则先确认“谁在管网卡”再动手改配置否则永远在被两套系统互相覆盖配置的坑里打转。3.4 bond/多网卡场景探活绕过了故障链路回到这次故障本身。eth0 管控制面eth1 管业务。默认路由怎么走ip route看一下default via 192.168.10.1 dev eth0 proto static metric 100 192.168.20.0/24 dev eth1 proto kernel scope link src 192.168.20.11这种情况下所有到 API Server192.168.10.x的流量都走 eth0业务网卡 eth1 断了完全不影响节点状态上报。如果集群是 bond 模式还要看/proc/net/bonding/bond0的 MII Status并用ip -s link观察每个从属接口的错误计数。这个场景告诉我们探活一定要按链路维度做而不是按节点维度做。一个节点可能同时存在多条链路其中任何一条断了都可能导致特定业务受影响但节点状态一点变化都没有。把“节点”当监控的最小单位会漏掉大量真实故障。4. 不再依赖 NotReady让假死节点主动现形的三套手段4.1 收紧 kubelet 上报与 node controller 判定参数先明确一个前提调整参数只能加快“心跳完全中断被标记 NotReady”的速度对“心跳仍然通但链路劣化”的场景没有直接帮助。如果你决定调整参数如下# kubelet --node-status-update-frequency5s # kube-controller-manager --node-monitor-period5s --node-monitor-grace-period30s --pod-eviction-timeout300s调整之后心跳中断的暴露时间从默认的 40 秒左右缩短到 30 秒左右。注意几个副作用kubelet 上报频率提高会放大 API Server 和 etcd 的写入压力。几百节点规模问题不大几千节点要谨慎。grace period 太小网络抖动一次就可能触发 NotReady 和 Pod 驱逐引发更大范围故障。--pod-eviction-timeout建议保持默认5 分钟是比较稳妥的驱逐窗口。这类参数属于控制面变更生产环境务必先在测试集群跑半小时再灰度到生产。提示如果 master-01 是双层网卡架构就算把 grace period 调到 10s只要管理卡没断节点照样不会 NotReady。参数调优不能替代链路探活。4.2 node-problem-detector把内核日志与网卡事件纳入节点状态如果你不想只靠心跳来判断节点健康node-problem-detectorNPD是官方生态里最合适的一层补充。它的作用就是把节点上额外的异常信息内核日志、文件系统问题、运行时问题转成 Node Condition让kubectl get nodes的状态列真正反映更多维度的问题。部署方式很简单用 helm 装或者直接应用官方 DaemonSet项目地址是kubernetes/node-problem-detector。核心配置在/etc/NodeProblemDetector/config/kernel-monitor.json规则示意如下[ { type: NetworkUnavailable, reason: NetworkInterfaceIsDown, pattern: NIC Link is Down.*|eth.*link is down|BUG: soft lockup.*, condition: True } ]我把网卡相关的日志关键字NIC Link is Down、eth.*link is down、Cannot allocate memory、BUG: soft lockup都加进了 kernel-monitor 的 rules。这样一旦 dmesg 里出现对应记录NPD 就会把节点标记为异常控制面能看到真实状态。要提醒的是NPD 依赖系统日志。物理链路断开如果完全没留下日志比如网线被拔掉驱动没有触发中断事件NPD 也抓不到。所以它更适合覆盖驱动层、内核层故障物理链路层还是要靠探活兜底。4.3 端到端探活与告警设计盯住 API Server 到节点的真实链路对于“链路劣化但心跳没断”这一类问题最直接的手段是端到端探活。我现在会在每个节点上部署一个轻量探活脚本用 systemd timer 每 30 秒跑一次逻辑很简单但非常管用#!/bin/bash # /usr/local/bin/netprobe.sh # TARGETS 按实际情况替换为网关、对端节点、APIServer 地址 TARGETS(192.168.10.1 192.168.20.1) LOGFILE/var/log/netprobe.log for gw in ${TARGETS[]}; do if ! ping -c 2 -W 2 $gw /dev/null 21; then echo $(date %F %T) GW_DOWN $gw $LOGFILE fi done if ! nc -z -w 3 APISERVER_IP 6443; then echo $(date %F %T) APISERVER_UNREACHABLE $LOGFILE fi配合 Prometheus 的node_network_up、node_network_carrier指标可以做出两条很实用的告警一是“网卡 down 超过 2 分钟”二是“到业务网关丢包率超过 5% 持续 1 分钟”。告警比单纯依赖 NotReady 靠谱得多因为它监控的才是业务真正依赖的链路。我个人现在的监控矩阵是三个维度控制面状态NotReady/Ready一个维度节点到网关/API Server 的端到端连通性一个维度业务端口可用性一个维度。任何一个维度告警都说明链路有了真实变化而不是等 K8s 状态列自己翻红。5. 故障恢复与复盘清单把这次事故变成资产5.1 网卡故障的系统恢复命令序列以 RHEL/麒麟系为例完整恢复序列如下先备份当前配置cp /etc/sysconfig/network-scripts/ifcfg-eth1 /root/ifcfg-eth1.bak确认故障原因。如果是链路层网线/光模块拔插或更换硬件如果是驱动层尝试重启驱动模块modprobe -r ixgbe modprobe ixgbe如果是 ONBOOTno改配置后执行ifup eth1vi /etc/sysconfig/network-scripts/ifcfg-eth1 # 确保 ONBOOTyes ifup eth1验证链路ip addr show eth1 ethtool eth1 | grep -E Speed|Link detected ping -c 3 网关地址确认 K8s 状态kubectl get nodes kubectl describe node master-01 | sed -n /Conditions:/,$p5.2 复盘时间线为什么告警里没有“节点异常”这一条故障复盘时我拉了一下完整时间线00:15 左右业务网线/模块故障eth1 物理链路中断业务监控从 00:17 开始出现大量访问失败00:30 我们接到告警kubectl get nodes显示 master-01 为 Ready00:45 登录节点才从 ethtool 和 dmesg 确认业务网卡故障。问题就出在监控设计上当时我们的告警项只有“Pod CrashLoopBackOff”“节点 NotReady”没有任何关于链路质量、网卡状态、到业务网关连通性的监控。所以链路已经断了近半个小时集群状态这个维度却是一路绿灯。这不是 Kubernetes 的锅而是“健康检查维度错位”造成的盲区。K8s 只承诺它能看到的心跳和资源条件网卡物理层、驱动层的故障必须靠我们自己去补监控。5.3 三条写在运维手册里的检查项最后我把这次事故的沉淀写成了三条检查项建议有类似架构的集群也加进运维手册双网卡场景必须做链路维度告警。管理网和业务网分开部署的机器不要只监控 kubelet 心跳要分别监控两块网卡的 carrier 和到各自网关的连通性。定期巡检/proc/net/dev和 dmesg 的错误计数。很多网卡故障在发生前就有前兆报错计数上涨、链路抖动、偶发 soft lockup。巡检脚本每周跑一次把异常计数做成趋势图。任何网卡配置变更先验证“重启后自动拉起”。ONBOOTyes、NetworkManager 接管状态、接口名是否固定这三项必须写进变更验收清单避免一次重启后整个节点失联。我自己现在维护集群已经默认kubectl get nodes的 STATUS 列只是最低要求而不是最终结论。真正可靠的健康度得靠端到端探活、节点问题探测器和链路质量监控来兜底。那次 master-01 的经历让我彻底改掉了只看节点状态的坏习惯也希望这篇内容能帮你少走一个通宵的弯路。