先执行启动检查,成功后才开始就绪与存活检查。

启动探针成功后,其他探针才开始执行

Startup 探针为容器提供单独的初始化契约。在已经配置的启动探针成功之前,Kubernetes 不会执行这个容器的 liveness 或 readiness 探针。这比“宽限期内忽略一些存活失败”更具体:另外两种检查根本还没有开始。启动检查成功之后,后续健康和就绪判断才按各自规则接管,不能把三个名称理解成同一个接口的不同标签。 为每个阶段定义可观察条件 应用进程必须先运行,启动探针才可能成功。因此,容器已经 Running 时,container status 中的 started 仍可能为 false,所在 Pod 也可能尚未 Ready。运行状态描述进程正在执行,启动成功则描述配置的检查条件已经满足。提前开放的 HTTP 监听端可以报告进度,但不应该因此自动声称全部准备已经完成。 启动条件应代表必要的本地准备,例如加载索引或验证初始状态。只有打开端口确实就是所需前置条件时,单纯检查端口才足够。如果监听器先出现,昂贵的初始化稍后才结束,过早成功的检查就会提前解除存活保护,再次造成原本想避免的重启循环。接口名称不会替应用定义初始化完成的真实含义。 启动检查一旦成功,kubelet 就停止当前容器实例的 startup 探针,并按独立配置运行存活与就绪检查。启动成功不是永久健康证明:应用稍后仍可能变成未就绪,或者需要重启。新的容器实例还要重新完成自己的启动检查,不能继承上一实例已经成功的结论。把实例身份和状态变化一起记录,才能解释重复启动的原因。 坏掉的样子 启动探针如果一直不成功,最终会达到失败阈值。Kubelet 随后终止容器,并根据对应重启策略处理。因此,启动保护不是允许初始化无限等待。周期、超时和失败阈值应依据真实初始化耗时设置,同时考虑环境中可能发生的资源争用与可以接受的恢复时间,避免只按开发机器的一次快速启动选择参数。 failureThreshold 与 periodSeconds 的乘积可用于规划启动预算,但不能把它写成所有实际重启都严格遵守的秒表。初始延迟、探针执行与调度、终止宽限和重启退避也影响完整时间线。排查慢发布时,应记录真实尝试和状态变化,而不是仅根据两个配置数字推断容器应该已经重启,或断定平台行为错误。 除非通过 startup 明确建立依赖,否则不应认为 liveness 会等 readiness 成功才开始。就绪与存活原本回答两个持续运行阶段的问题。反过来,启动成功也不会强迫就绪成功:后续 readiness 可能因为另一个条件拒绝流量资格,而进程仍然正常存活。把这些结果分开,才能选择正确恢复动作,避免用反复重启处理暂时不可服务。 ...

八月 22, 2027 · 3 分钟 · XB Field Notes
把流量与重启策略分开的两种探针。

liveness 与 readiness 告诉 Kubernetes Pod 的状态

慢启动不等于进程已死:readiness 负责遮挡流量,liveness 只负责让进程活着。把两者搞混是最常见的健康检查错误。团队把同一个接口同时接进两种探针,于是每个变慢的依赖都变成重启循环——应用需要时间,存活探针偏不给,Pod 恰恰死在本来再忍一会儿就能活的关口。两种探针并存,因为"没就绪"和"已死"要的是相反的动作:一个要时间,一个要重启。 防混的心智模型是医院分诊。就绪探针是分诊护士,决定谁现在能看医生;存活探针是抢救车,只在病人心跳停了才推出来。没人会因为坐在候诊区就给人除颤。记住这个画面,探针配置几乎不用想就写对了。 Readiness 把流量挡在门外 readiness 探针失败,会把 Pod 从 Service 的端点里剔除,但 Pod 仍在运行,好让进程完成热身或自行恢复。只要探针恢复通过,流量马上回流。所以就绪探针是可以放心失败的那一个:缓存可以慢慢热,迁移可以跑完,下游依赖可以回来,全程不丢 Pod 的内存状态,也不付重启的代价。 就绪接口要围绕真正的可服务性设计,而不是进程存在性。检查那些决定这个 Pod 此刻能否正确回答的东西:结构迁移是否应用、必需的缓存是否装满、关键下游是否可达。只检查"服务能响应"的就绪探针,会把注定失败每个请求的 Pod 放进流量——比不检查还糟,因为负载均衡器是信任它的。但也别把所有依赖都塞进去:非关键的推荐服务挂了,就绪失败等于为一件装饰品扔掉主干流量。 Liveness 守护进程本身 liveness 探针失败会触发重启,这是结束僵死状态的最后一招。它要处理的该是"进程卡死"这类问题,而不是容器只是暂时还没就绪。合格的目标很窄:死锁——此后任何请求都不会再成功;协程或线程泄漏到只有新进程能清;内存状态损坏到任何重试都救不回来。每种情况的共同点是不可逆:不重启就不可能恢复,而不只是恢复得慢。 这个门槛意味着很多服务应该只配极简的存活探针,甚至不配。把存活检查打到外部数据库上,每次数据库抖动都会变成全集群的重启风暴,还顺手毁掉本可以加速恢复的热缓存。优先用廉价的本地检查——主循环定时刷新的看门狗时间戳——不问进程之外的任何东西,只回答"这个进程是不是卡死了"。崩溃的容器本就会被重启;存活探针只为行尸走肉而存在。 给探针留足余量 把 initialDelaySeconds 设到足够大,别把冷启动误判成崩溃;timeout 也要配合真实工作量。把两类探针分开,让慢的 Pod 由 readiness 护住,而不是被 liveness 误回收。启动慢的容器最好再加一个启动探针:它运行期间存活探针保持关闭,于是需要预热大缓存的版本升级能拿到几分钟而不是几秒,稳态的存活检查强度一点不减。 失败阈值按观测到的行为调,别在服务之间抄整数。检查周期乘以失败次数就是真实反应时间——十秒周期配三次失败,等于坏了三十秒才动手。对就绪探针,这是三十秒失败请求继续被路由进来;对存活探针,这是卡死三十秒才重启。量出冷启动的分位耗时、依赖恢复时间、最坏的回收停顿,再定每个数字:比告警快,比噪声慢。 坏掉的样子 标志性故障是依赖故障期的重启循环:数据库变慢,本来就绪探针就够了,但存活探针共用同一个失败的接口,全集群一波波重启。每次重启清空热缓存,下一波恢复更慢,系统无限振荡。诊断看重启时间戳与依赖事故是否对齐;修法是拆接口——就绪查依赖,存活只查进程——让生病的下游只挡流量,不杀 Pod。 ...

七月 14, 2027 · 1 分钟 · XB Field Notes
一个 Pod 在就绪状态变化和随后容器重启期间始终保留相同身份。

就绪探针改变路由资格,存活探针触发容器重启

Readiness 与 liveness 回答不同的运维问题。就绪探针判断当前容器是否应该参与一个能够接收普通 Service 流量的 Pod,存活探针则判断是否应该执行预先配置的容器重启恢复。两者都可以使用 HTTP,但协议相同不代表语义相同。应先确定希望平台采取什么动作,再设计触发该动作的条件,否则一个健康接口可能同时承担互相冲突的责任。 同时观察响应和平台动作 就绪探针失败会让容器变成未就绪,并影响 Pod 的 Ready 条件。对于普通带选择器的 Service,端点协调随后改变哪些后端可以参加正常路由。Pod 可以保持 Running,同时处于未就绪状态,应用进程也可能继续运行。重启次数、Pod 标识和 Ready 条件回答的是不同问题,排查时不能只看其中一个状态就推导全部行为。 存活探针连续失败达到配置阈值后,kubelet 会终止容器并按相应重启规则处理。普通 Always 策略应用 Pod 会在原 Pod 中获得替换容器,而不是必然创建一个新 Pod 对象,也不是发布另一版应用。应在比较 Pod 身份的同时查看容器重启次数与终止状态,避免把一次进程恢复误报为完整工作负载替换。 检查应便宜,而且成功条件要具有实际含义。TCP 连接成功只证明监听端能够接受连接,不证明应用能够完成有用工作。HTTP 可以表达更丰富的问题,但如果它递归调用昂贵的依赖检查,也可能成为额外负载来源。探针自己的执行成本应符合工作负载容量和超时预算,不能让健康检查本身把健康服务压慢。 坏掉的样子 把共享数据库故障当成存活失败,可能导致所有正常应用进程不断重启,却完全无法修复数据库。把同一个依赖直接加入就绪检查也需要判断:撤走全部副本可能同时撤掉本来有价值的降级响应,在局部故障中还可能把流量集中到剩余副本。应依据服务实际契约定义就绪,包括已经明确支持的部分可用行为,而不是照抄统一依赖清单。 就绪状态不是网络访问控制。即使某个 Pod 不再属于 Service 的就绪目标,它的地址仍可能被直接访问。既有连接、其他路由层,以及 publishNotReadyAddresses 等 Service 设置,也需要单独分析。因此,不能把 readiness 失败描述成一个瞬时保证,认为从此没有任何请求能够到达进程。它改变的是对应服务发现和路由流程中的资格。 ...

一月 13, 2027 · 3 分钟 · XB Field Notes