Kubernetes Tips:Readiness / Liveness / Startup 探针怎么用
用对探针,避免 CrashLoop 与糟糕的发布;读懂 readiness、liveness、startup 的职责与调参方法。
探针(Probes)决定了:
- Pod 是否 接收流量(readiness)
- 容器是否会被 自动重启(liveness)
- 启动慢的服务是否会被 过早判死(startup)
很多“发布一上来就抖/经常重启”的问题,本质上不是业务逻辑崩,而是探针语义用错了。
心智模型(最重要的三句话)
- Readiness:现在适合接流量吗?
- Liveness:进程是不是卡死/不可恢复,需要重启吗?
- Startup:启动期给我额外时间,先别用 liveness 折腾我。
最常见的坑:用 liveness 充当“流量开关”
如果你把 liveness 当成“没 ready 就死给你看”,你会得到:
- 还在 warmup 或迁移的 Pod 被不断重启
- 发布速度变慢,错误率变高
- 甚至形成“重启风暴”
正确做法:
- 用 readiness 把 Pod 从 Service endpoints 中移除(不接流量)
- 用 startupProbe 保护启动期
- 用 liveness 只处理“重启能解决的卡死/异常”
一个靠谱的默认配置(HTTP 服务)
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30
periodSeconds: 2
readinessProbe:
httpGet:
path: /readyz
port: 8080
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 3
livenessProbe:
httpGet:
path: /livez
port: 8080
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
这套配置的核心是:启动期先过 startup,再谈 liveness;流量交给 readiness 控制。
/readyz、/livez 应该检查什么
/readyz:能否正确服务请求(“现在”)
readiness 应该在以下场景失败:
- 依赖还没连上(DB、缓存、上游服务)
- 迁移未完成(但你又不希望接流量)
- 预热未完成(关键缓存/配置未加载)
readiness 的本质是“业务可用性”,不是“进程是否活着”。
/livez:进程是否卡死/不可恢复(“重启能救”)
liveness 适合检查:
- 死锁/事件循环卡死
- 内部状态不可恢复(严重异常)
不推荐:把 DB 可用性作为 liveness 的硬条件。DB 抖一下你就把所有 Pod 重启一遍,往往会把事故扩大。
探针类型:HTTP / TCP / exec(用得越简单越好)
Kubernetes 支持:
httpGet:最推荐(语义清晰、可观测)tcpSocket:只检查端口是否能连(语义弱)exec:在容器里跑命令(灵活但可能昂贵)
建议:
- 优先 HTTP endpoints
- TCP 只作为无 HTTP 的兜底
- exec 慎用(集群大了会变成“定时在所有 Pod 内执行脚本”,成本和风险都更高)
startupProbe 比 initialDelaySeconds 更靠谱
传统做法用 initialDelaySeconds 延迟探针启动,但缺点明显:
- 延迟是固定值,启动快也要等
- 启动慢超过延迟就被误判
startupProbe 更智能:
- 在 startupProbe 成功之前,liveness 不会生效
- 启动快就快通过,启动慢就给时间
实用换算:
最长允许启动时间 ≈
failureThreshold * periodSeconds
例如希望给 60 秒启动窗口,可以:
periodSeconds: 2failureThreshold: 30
发布友好视角(Mermaid)
flowchart LR A[Pod 启动] --> B{startupProbe 通过?} B -- 否 --> A B -- 是 --> C{readiness 通过?} C -- 否 --> D[不接流量] C -- 是 --> E[接收流量] E --> F{liveness 通过?} F -- 否 --> A F -- 是 --> E与优雅下线配合(否则探针也救不了)
探针只决定“是否在 endpoints 里”,但如果 Pod 退出太粗暴,还是会有错误。
建议:
- 合理设置
terminationGracePeriodSeconds - 收到 SIGTERM 立即让 readiness 失败(快速摘流量)
- 等待 in-flight 请求完成后再退出
- 必要时加
preStop做 drain/flush
调参避免抖动(timeout/threshold/period)
常见问题:
timeoutSeconds太小(真实 p99 可能 > 1s)periodSeconds太密(探针负担大,反而影响业务)failureThreshold太小(一次抖动就摘流量/重启)
实践经验:
- readiness:
period 5s、timeout 1-2s、failureThreshold 3 - liveness:
period 10s、timeout 1-2s、failureThreshold 3 - startup:按启动最长时间换算
排查探针失败(必备命令)
kubectl describe pod <pod>
kubectl logs <pod> -c <container> --previous
kubectl get events -n <ns> --sort-by=.lastTimestamp
在容器内部验证:
kubectl exec -n <ns> -it <pod> -- sh
curl -sS -i http://127.0.0.1:8080/readyz
如果镜像是 distroless 没有工具,用 ephemeral container(kubectl debug)来跑 curl 更安全。
可观测性建议:让 readiness 失败有“原因”
即使探针只看状态码,你也应该在服务内部记录:
- 哪个依赖导致 readiness 失败
- 失败次数与持续时间
- 失败发生在发布/扩容/依赖抖动的哪个阶段
这样你才能把“探针失败”从现象变成可行动的根因。
Checklist
- 启动慢的服务启用 startupProbe
- readiness 控制接流量,liveness 控制重启
- liveness 不依赖外部依赖(避免重启风暴)
- timeouts/thresholds/period 贴近真实延迟分布
- 下线流程与 readiness 配合(优雅摘流量)
参考链接
FAQ
Q: readiness 和 liveness 要用同一个接口吗? A: 通常不建议。readiness 关注依赖与可接流量,liveness 只需要快速判断是否“卡死”。
Q: 启动很慢怎么办?
A: 用 startupProbe,或提高 failureThreshold/period,别用很长的 initialDelaySeconds。
Q: readiness 失败会重启吗? A: 不会。只有 liveness 失败才会触发重启。