问题背景
微服务架构下,单个服务依赖 5-20 个下游,任何一环的启动/停止都可能引发雪崩。健康检查(health check)是容器编排、负载均衡、自动扩缩容的基石,但业界普遍存在「探活只配 liveness、依赖不等就绪就放流量、优雅下线只睡 30 秒」三大误区。一次核心支付服务因探活配置不当导致的 45 分钟全站雪崩,暴露了健康检查设计的系统性缺失。
故障现象
2026 年 7 月 29 日 21:17,支付核心服务发布后:
- 新 Pod 进入 Running 后 8 秒即被 liveness 探针杀死,CrashLoopBackOff 循环;
- 旧 Pod 停止后 30 秒仍有 12% 请求打到已关闭的 JVM 进程,报 Connection reset by peer;
- 依赖的订单服务未就绪,新 Pod 启动即开始接收流量,首查订单接口 100% 超时;
- 滚动更新第 3 批时,50% Pod 同时处于 Terminating + 新 Pod Pending,双端同时受损。
最终靠手动 scale 到 0 再 scale up 止血,全站支付能力中断 45 分钟。
排查过程
1. 探活语义错位
|
|
/health只检查 JVM 存活,不检查数据库连接池、Redis、Kafka;- 真实就绪需要 25-40 秒,探针 5 秒即判定失败;
- readiness 未配,Pod 一 Running 就进 Endpoints,流量瞬间打满。
2. 依赖就绪检查缺失
支付服务启动日志显示:
|
|
启动脚本直接 java -jar,未等下游就绪就暴露 8080。
3. 优雅下线不完整
|
|
JVM shutdown hook 需要 45 秒完成事务回滚和连接释放,sleep 30 秒即被 SIGKILL,部分请求在途。
4. 探活依赖自身健康
/health 端点硬编码返回 200,不做真实依赖探测,导致「假活」。
解决方案
四类健康检查 Checklist(生产必配)
| 检查类型 | 目的 | 推荐配置 | 常见陷阱 | 验证命令 |
|---|---|---|---|---|
| Startup Probe | 防止启动慢应用被 liveness 误杀 | failureThreshold: 30, periodSeconds: 5(最多 150s) |
initialDelay 写死 | kubectl describe pod 看 Started |
| Liveness Probe | 杀死真正死掉的进程 | 只检查本地 JVM/进程存活,路径 /livez,不查依赖 |
查下游导致级联 | curl -I http://localhost:8080/livez |
| Readiness Probe | 决定是否接收流量 | 真实检查所有下游(DB、Redis、Kafka、订单服务) | readiness = liveness | kubectl get endpoints 观察就绪数 |
| PreStop Hook | 优雅下线 | sleep 时长 ≥ JVM shutdown hook + 连接释放 + 负载均衡移除时间 |
固定 30s | kubectl logs 看 shutdown hook 完成时间 |
参考配置(Spring Boot + Kubernetes)
|
|
Java 端点实现:
|
|
依赖就绪保障
启动脚本使用 wait-for-it.sh 或 Sidecar:
|
|
优雅下线三件套
preStopsleep 时长 = JVM hook 时间 + 负载均衡健康检查间隔 × 2terminationGracePeriodSeconds≥ preStop sleep + 10s- 负载均衡配置(Ingress/Nginx/AWS ALB)健康检查间隔 ≤ 5s,unhealthy 阈值 ≤ 2 次
根因分析
健康检查设计本质是「状态机一致性」问题:
- Startup/Liveness/Readiness 三种探针语义被混淆为单一
/health; - 探活本应是「本地存活」,却被做成「依赖存活」,形成级联失败;
- 优雅下线缺少「负载均衡已摘除 → 连接已释放 → 进程可杀」三阶段确认;
- 配置漂移:开发环境 liveness = readiness,生产直接复制粘贴。
预防措施
- Checklist 强制落地:所有新服务发布前必须通过「健康检查 Checklist」评审,缺少任意一项即阻断。
- 探活分层:
/livez(本地)+/readyz(依赖)+/startupz(启动),三端点分离。 - 依赖就绪可视化:在 Grafana 增加「服务就绪率」面板,低于 100% 触发告警。
- 优雅下线演练:每季度执行一次「滚动更新 + 强制杀进程」演练,记录实际 hook 时间。
- 配置即代码审查:CI 中增加
kubectl explain deployment.spec.template.spec.containers.livenessProbe静态检查,杜绝 readiness=null。
总结
健康检查不是「配一个 /health 就完事」,而是覆盖「启动 → 就绪 → 存活 → 下线」全生命周期的状态机。一次支付服务雪崩的根源是探活语义错位、依赖就绪缺失、优雅下线不足三者叠加。通过四类探针分离 + Checklist 强制落地,可将类似故障概率降低 90% 以上。建议所有核心服务在下一次发布窗口统一按本文 Checklist 改造。