结论先行
探针不是「加上就对了」,而是「加错了就是故障放大器」。liveness 决定重启、readiness 决定流量、startup 决定冷启动窗口——三者职责完全不同,最常见的事故是把 startup 该干的事丢给 liveness,结果冷启动慢的服务被反复杀 Pod。下面是我 3 年容器运维里反复验证过的默认值与踩坑清单。
一、职责边界(先记住再调参)
- startupProbe:只回答「进程有没有起来」。失败 = 还没活,继续等,最多等 failureThreshold × periodSeconds。
- livenessProbe:只回答「进程活着但还能不能正常工作」。失败 = 杀进程重启(不是杀 Pod,是杀容器)。
- readinessProbe:只回答「现在能不能接流量」。失败 = 摘流量,不重启。
一句话口诀:startup 管出生、liveness 管死亡、readiness 管接单。
二、默认值建议(Java / Go 两套)
Java / Spring Boot 应用(冷启动 20–90s 常见)
startupProbe:
httpGet: { path: /actuator/health/liveness, port: 8080 }
periodSeconds: 5
failureThreshold: 24 # 最长容忍 120s 冷启动
livenessProbe:
httpGet: { path: /actuator/health/liveness, port: 8080 }
periodSeconds: 10
failureThreshold: 3
timeoutSeconds: 3
readinessProbe:
httpGet: { path: /actuator/health/readiness, port: 8080 }
periodSeconds: 5
failureThreshold: 3
timeoutSeconds: 2
要点:
- 必须分开两个端点。Spring Boot 2.3+ 默认
/actuator/health/liveness与/actuator/health/readiness分离,别再用同一个/health,否则数据库抖动会引发雪崩式重启。 - startup 的 failureThreshold 别省。Java + 大堆 + 首次 JIT 未热身,冷启动 90s 很常见,failureThreshold=24、period=5s 给到 120s 余量是底线。
Go / 轻量服务(冷启动 < 5s)
livenessProbe:
tcpSocket: { port: 8080 }
initialDelaySeconds: 2
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet: { path: /ready, port: 8080 }
periodSeconds: 5
failureThreshold: 2
要点:Go 服务一般不需要 startup,liveness 用 tcpSocket 比 httpGet 便宜,readiness 才走 HTTP 检查真实依赖。
三、三个我真实踩过的坑
坑 1:liveness 检查了数据库
某次下游 MySQL 主从切换 30s,集群里 40+ Pod 全部被 liveness 杀掉 → 流量打到剩下 Pod → 连接池打爆 → 更多 Pod 被杀。雪崩 5 分钟。 修复:liveness 只检查进程心跳,DB 依赖放 readiness。这是铁律。
坑 2:timeoutSeconds 默认 1s 不够
K8s 默认探针超时 1s。一个 P99=800ms 的健康检查接口,在 GC 抖动时经常 >1s,被误判失败。 修复:timeoutSeconds 至少给到接口 P99 × 2,Java 应用建议 3s,Go 应用 2s 够用。
坑 3:initialDelaySeconds 和 startupProbe 同时配
startupProbe 一旦配置,initialDelaySeconds 被忽略。很多团队两套都配,以为双保险,其实 startupProbe 已经接管了,initialDelay 是死代码。 修复:二选一。冷启动 < 5s 用 initialDelay,> 5s 一律上 startupProbe。
四、一个反直觉的结论
探针不是越严越好,是越「窄」越好。
- 太严(period=1s、failureThreshold=1):GC 一次、网络抖一下就被杀。
- 太松(failureThreshold=100):真挂了 10 分钟才重启,故障放大。
- 正确姿势:职责窄(每个探针只检查一件事)、窗口宽(给冷启动和抖动留余量)、超时按 P99 定(不是按平均值)。
五、可执行的检查清单
发布前对照一遍:
- [ ] liveness 是否只检查进程自身?(不查 DB / 不查下游)
- [ ] readiness 是否覆盖了所有「接流量」前置条件?
- [ ] Java 服务是否配了 startupProbe?
- [ ] timeoutSeconds 是否 ≥ 健康接口 P99 × 2?
- [ ] 是否存在 initialDelaySeconds 与 startupProbe 同时配置的冗余?
- [ ] 探针端口是否走 Service 端口而非容器端口?(避免 sidecar 端口冲突)
探针写对了,K8s 的自愈才有意义;写错了,自愈会变成故障放大器。
参考
- Kubernetes 官方:Configure Liveness, Readiness and Startup Probes
- Spring Boot Actuator:Liveness and Readiness State
有踩过更离谱的探针坑欢迎评论区补充,尤其 Go / Rust 服务的实践。