慢启动不等于进程已死:readiness 负责遮挡流量,liveness 只负责让进程活着。把两者搞混是最常见的健康检查错误。团队把同一个接口同时接进两种探针,于是每个变慢的依赖都变成重启循环——应用需要时间,存活探针偏不给,Pod 恰恰死在本来再忍一会儿就能活的关口。两种探针并存,因为"没就绪"和"已死"要的是相反的动作:一个要时间,一个要重启。

防混的心智模型是医院分诊。就绪探针是分诊护士,决定谁现在能看医生;存活探针是抢救车,只在病人心跳停了才推出来。没人会因为坐在候诊区就给人除颤。记住这个画面,探针配置几乎不用想就写对了。

Readiness 把流量挡在门外

连续 readiness 失败达到阈值,会让 Pod 失去普通 Service 路由资格,但 Pod 仍在运行,好让进程完成热身或自行恢复。端点条件与数据路径在失败、恢复两个方向都异步收敛;已有连接和 Pod 直连需要分别考虑。就绪失败不直接触发探针重启,却可能移除全部服务容量,不能无条件称为安全:缓存可以慢慢热,迁移可以跑完,下游依赖可以回来,全程不丢 Pod 的内存状态,也不付重启的代价。

就绪接口要围绕真正的可服务性设计,而不是进程存在性。检查那些决定这个 Pod 此刻能否正确回答的东西:结构迁移是否应用、必需的缓存是否装满、关键下游是否可达。只检查"服务能响应"的就绪探针,会把注定失败每个请求的 Pod 放进流量——比不检查还糟,因为负载均衡器是信任它的。但也别把所有依赖都塞进去:非关键的推荐服务挂了,就绪失败等于为一件装饰品扔掉主干流量。

Liveness 守护进程本身

liveness 探针连续失败达到阈值会终止容器,是否重启遵循重启策略,这是结束僵死状态的最后一招。它要处理的该是"进程卡死"这类问题,而不是容器只是暂时还没就绪。合格的目标很窄:死锁——此后任何请求都不会再成功;协程或线程泄漏到只有新进程能清;内存状态损坏到任何重试都救不回来。每种情况的共同点是不可逆:不重启就不可能恢复,而不只是恢复得慢。

这个门槛意味着很多服务应该只配极简的存活探针,甚至不配。把存活检查打到外部数据库上,每次数据库抖动都会变成全集群的重启风暴,还顺手毁掉本可以加速恢复的热缓存。优先用廉价的本地检查——主循环定时刷新的看门狗时间戳——不问进程之外的任何东西,只回答"这个进程是不是卡死了"。崩溃之后是否重启,取决于容器重启策略;存活探针则能在普通退出之前识别选定的不可恢复本地故障。

给探针留足余量

把 initialDelaySeconds 设到足够大,别把冷启动误判成崩溃;timeout 也要配合真实工作量。把两类探针分开,让慢的 Pod 由 readiness 护住,而不是被 liveness 误回收。启动慢的容器最好再加一个启动探针:它成功之前,存活与就绪探针都保持关闭,于是需要预热大缓存的版本升级能拿到几分钟而不是几秒,稳态的存活检查强度一点不减。

失败阈值按观测到的行为调,别在服务之间抄整数。检查周期乘以失败次数只是规划估计,不是真实反应时间保证。探针所处周期、超时、调度、终止宽限与重启退避都会改变时间线;就绪端点变化与已有连接也并非在同一瞬间全部完成。量出冷启动的分位耗时、依赖恢复时间、最坏的回收停顿,再定每个数字:比告警快,比噪声慢。

坏掉的样子

标志性故障是依赖故障期的重启循环:数据库变慢,本来就绪探针就够了,但存活探针共用同一个失败的接口,全集群一波波重启。每次重启清空热缓存,下一波恢复更慢,系统无限振荡。诊断看重启时间戳与依赖事故是否对齐;修法是拆接口——就绪查依赖,存活只查进程——让生病的下游只挡流量,不杀 Pod。

镜像故障是僵尸 Pod:服务能响应所以存活探针通过,但请求全卡在死锁的工作池后面。流量源源不断送进一个什么都不回答的 Pod。修法是让存活接口走真实路径——带短超时申请一个工作槽、确认没有客户端请求时仍会刷新的本地进度心跳保持新鲜——卡死能失败探针,单纯慢则不失败。

快速验证

检查探针接线与最近的杀戮记录:

kubectl get pod web-0 -o jsonpath='{.spec.containers[*].livenessProbe}{"\n"}{.spec.containers[*].readinessProbe}{"\n"}'
kubectl get events --field-selector reason=Unhealthy --sort-by=.lastTimestamp | tail -n 10
kubectl describe pod web-0 | grep -A3 -i 'liveness\|readiness'

这些未改动的诊断本轮没有重新执行。若语义适合两项决策,共用 URL 并不天然错误。Unhealthy 事件包含探针失败,不能证明发生了杀进程;需要关联具体 Pod、容器重启计数和终止原因。带期限的检查必须区分死锁、暂时负载及没有业务请求的正常空闲,不能用一次失败代替完整故障判断。

延伸阅读

来源