bigywater.tech
Kubernetes · 30

探针:liveness、readiness、startup 各自改变了什么

三种探针都叫“健康检查”,失败后的后果却完全不同:摘流量、杀容器、还是什么都不做?这篇每种都让它真的失败一次,量出多久起效、Service 的端点和容器的重启次数各发生了什么变化,并验证 startupProbe 怎么保护慢启动的程序。

4.0 s / 7.9 s
readiness 失败后 Pod 变 NotReady 的耗时 / liveness 失败后容器被重启的耗时(period 2 秒)
exitCode 137
liveness 杀掉容器后 lastState.terminated 的退出码
0 次 vs 3 次
同样要 20 秒启动的程序,有 startupProbe 与没有时 26 秒内的重启次数

readiness:只摘流量

实测 · 本机执行输出
  Pod rd 就绪:Ready=True;EndpointSlice 里端点的 ready 条件=true
  删掉 /tmp/ok 后 4.0 秒 Pod 变成 NotReady(periodSeconds=2,failureThreshold=2);容器 restartCount=0(没有重启);EndpointSlice 里端点 ready=false
  事件:[('Unhealthy', 3, "Readiness probe failed: cat: can't open '/tmp/ok': No such file or directory")]
  恢复 /tmp/ok 后 1.6 秒 Pod 重新 Ready,EndpointSlice 里端点 ready=true

探针命令 cat /tmp/ok,periodSeconds: 2、failureThreshold: 2。删掉文件 4.0 秒后 Pod 变成 NotReady,EndpointSlice 里这个端点的 ready 从 true 变成 false(Service 不再把流量发给它),容器 restartCount 仍是 0。恢复文件 1.6 秒后重新 Ready。readiness 失败是可恢复的状态,不会动容器。

liveness:杀容器

实测 · 本机执行输出
  删掉 /tmp/ok 后 7.9 秒容器被重启(periodSeconds=2,failureThreshold=3 → 约 3 次失败 ≈ 6 秒);restartCount=1;容器 ID 23e6f439387b… → 8c09339d789e…(是新容器);lastState.terminated.reason=Error exitCode=137
  事件:[('Unhealthy', 3, "Liveness probe failed: cat: can't open '/tmp/ok': No such file or directory"), ('Killing', 1, 'Container c failed liveness probe, will be restarted')]
  重启后的新容器里 /tmp/ok 又被命令重新创建,probe 通过:Ready=True

同样的探针放在 livenessProbe、failureThreshold: 3:连续失败 3 次(约 6 秒加调度误差,实测 7.9 秒)之后,kubelet 打出 Killing: Container c failed liveness probe, will be restarted,容器以 exitCode=137(128+9,被 SIGKILL)退出并重启,restartCount=1,容器 ID 换了。重启发生在同一个 Pod 里。

startupProbe:给启动留时间

实测 · 本机执行输出
  + 0.0s containerStatuses[0]:started=False ready=False restartCount=0
  +22.7s containerStatuses[0]:started=True ready=True restartCount=0
  容器启动后 20 秒才创建 /tmp/started(startupProbe 通过);liveness 的 failureThreshold 只有 1,如果它在启动阶段就执行,这个容器早被杀了:最终 restartCount=0
  对照 —— 没有 startupProbe、只有同样的 liveness 的容器:
    26 秒后 restartCount=3;lastState=Error(liveness 一启动就开始检查,/tmp/ok 还不存在,被反复杀)

程序要 20 秒才创建 /tmp/ok。只配 failureThreshold: 1 的 liveness,26 秒内被杀了 3 次;加上 startupProbe(每 2 秒一次,最多失败 15 次 = 30 秒预算)之后,started 一直是 false,到 +22.7 秒才变成 true、ready=true,期间 liveness 和 readiness 都没有执行,restartCount=0。慢启动的程序应该用 startupProbe 而不是把 liveness 的 initialDelaySeconds 设得很大。

怎样算失败

实测 · 本机执行输出
  httpGet(/healthz 返回 200)+ tcpSocket(8080 端口能连上):Ready=True restartCount=0
  删掉 /www/healthz(接口变 404,端口还通):3.0 秒后 Ready=False;restartCount=0(tcpSocket 仍通过);最后一个 Unhealthy 事件:[('Unhealthy', 4, 'Readiness probe failed: HTTP probe failed with statuscode: 404')]
  exec 命令要 3 秒才结束而 timeoutSeconds=1:Ready=False;事件:[('Unhealthy', 7, 'Readiness probe failed: command timed out: "sh -c sleep 3; cat /tmp/ok" timed out after 1s')]

httpGet:状态码 200~399 算成功,接口变成 404 就是失败(HTTP probe failed with statuscode: 404),而同一容器的 tcpSocket 只看端口能不能连上,仍然通过,所以 readiness 是 False、liveness 没动静。exec 命令的耗时超过 timeoutSeconds 也算失败(command timed out … timed out after 1s)。

分步演示

三种探针
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / kind(Docker 里的单节点或多节点真实集群,节点是 Docker Desktop 虚拟机里的容器)/ kubectl;用空的 kubeconfig 之外的独立 kubeconfig,不碰用户自己的集群;计时用 date +%s%N / kubectl 事件时间戳,数字每次运行会有小幅抖动。三节点 kind 集群 kx-main(Kubernetes v1.37.0);探针命令多用 exec(cat /tmp/ok),httpGet 与 tcpSocket 用 busybox httpd(端口 8080);时间用 0.2 秒间隔轮询 Pod 状态得到,包含一个轮询周期的误差;EndpointSlice 的 ready 条件读自 kubernetes.io/service-name 标签对应的 EndpointSlice;每种情形各一次
    • 没有验证:initialDelaySeconds 的效果;successThreshold 大于 1;grpc 探针;terminationGracePeriodSeconds 在 liveness 失败时的覆盖(probe-level);探针对节点资源的开销;kubelet 重启后探针状态的恢复;Pod 带 readinessGates 时的就绪判断;探针 failureThreshold 与 Service 流量中断的精确时序
    京ICP备2026067502号-1京公网安备11010502063487号