两天前生产集群一个节点在 01:30 宿主机冻死,kubelet 心跳仍正常、进程存活、端口在听,但底层 NFS 链路 hang 住导致 containerd 卡死,52 个实例网关全部下线。此后数小时,K8s Dashboard 一片绿灯、零告警,用户反馈才被发现。问题不是故障本身可逆与否,而是无法被看见、无法自愈。这次复盘的产出是一套三层补课。
穿透式健康检测
关键观察:进程存活、端口在听、心跳在跳,这些都不意味着节点或实例真能工作。这次故障里,kubelet 心跳仍在跳、端口仍能三次握手、进程列表里 gateway 仍然存在,但业务已经完全瘫痪。
补课第一层是不只检查进程和心跳,要探测业务路径能否通。系统需要向每个实例网关发真实请求(打开 panel、走通工具调用),看它是否真的能响应。这不是 TCP 握手——即使网关卡死也能握手,而是一个完整的业务操作。
Health-watchdog cron 在 cpa-api 里新增,每 60 秒遍历一轮全部实例。它通过实际的业务操作检测,而不是仅看心跳和进程。具体做三件事:
- 节点卡死检测:统计每节点「卡在 ContainerCreating/Terminating 超过 5 分钟」的 Pod 数,同时逐实例探测网关失败率。某节点卡滞 Pod ≥ 3 或网关失败率 ≥ 50%(且实例数 ≥ 5)→ 判定节点卡死。
- 实例网关健康探测:并发 12 轮转覆盖全部 RUNNING 实例,连续失败 3 次标记异常。探测路径:GET http://pod:8080/panel/ 或 exec 探 18789 端口。区分 DOWN(TCP 拒)vs HUNG(adapter 504)vs 配置损坏(JSON 解析错)。
- DB 与实际对账:DB=ERROR/PROVISIONING 但 pod Running 且网关 UP,或反之——放任不管会在下次启动时踩坑。
自愈分级
补课第二层是自愈不能一刀切。发现问题后盲目重启或隔离只会加重故障。
低风险自愈(自动执行):单实例网关 HUNG(反复重启)自动重启 supervisord 进程,或重建 Pod 触发 entrypoint 的 doctor 和 heal 逻辑。单实例网关 DOWN(配置拒启)自动重建 Pod。这两种场景影响就一个用户,失败的代价可控。冷却时间 10 分钟,防止自愈本身陷入反复。
中等风险自愈(立即告警,人工确认后执行):节点卡死。上面可能有 52 个实例,隔离或驱逐是爆炸半径极大的操作。自动 cordon 该节点(停止新调度)+ 立即告警,人工审视确认后再 drain 到其他节点。
自愈的铁律:① 动作前必须复测确认(防网络抖动的假阳性)② 全程带审计日志 ③ 失败只 warn 不抛(绝不让健康守卫自己把系统拖崩)④ 高风险动作默认人工,通过环境变量 flag 才开自动。
告警的分级与去噪
补课第三层是三渠道分级告警。诊断日志里 kubelet liveness 警告早就有,但埋在 INFO 级噪音里每秒数百条。需要分级:某个条件从 0 跳到 3 是事件,连续 N 轮检测都 ≥ 3 才是真故障症状,才推送告警。
集群内埋点:health-watchdog 每 60 秒产生检测结果,节点卡死、实例自愈失败、自愈触发次数突增时直接标记待告警。这比原始监控数据更接近业务。
云平台告警规则:天翼云控制台直接配——存储写延迟 > 10ms、IOPS 突增、节点 NotReady、CPU/内存断崖式下跌。
Canary 探针:常驻实例每隔一段时间完整登录自己的 panel,走通网关→adapter→登录全流程。失败立即告警。注意:canary 本身仍属集群内资源,素材中暂无真正的"集群外"探针设计,所以这是"伪外部"视角——覆盖了端到端业务路径,但仍依赖集群内的网络和计算资源。
落地阶段
补课分四期。P1 最紧急:NFS 挂载参数从 hard 改成 soft,timeo=100,retrans=3,intr(防节点冻),同时上线 health-watchdog 检测和告警。P2 是自愈动作和实例打散。P3 是节点自动 drain。
待定决策:告警通道(钉钉 webhook / 短信)、是否开启节点自动 drain、NFS 参数改动是否接受滚动重启。
核心逻辑是可见性 → 可控性 → 自动化。先让故障可见(检测),再让它可控(自愈和告警),最后才是逐步提升自动化程度。跳过前两步直奔自动化,就是这次故障的代价。
■