Kubernetes 基础体系 · 第 20/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。

Kubernetes Probe:Startup、Readiness、Liveness、gRPC 和误配置风险

Kubernetes Probe 是 kubelet 对容器执行的健康检查。它不是一个抽象的“应用健康”标签,而是一组会直接影响容器重启、Pod 是否接收 Service 流量以及应用启动流程的控制机制。

Probe 主要解决三个不同问题:

  • Startup Probe:应用是否已经完成启动?
  • Readiness Probe:应用现在是否可以接收流量?
  • Liveness Probe:应用是否仍然处于需要继续运行的状态?

此外,Kubernetes 还支持使用 HTTP、TCP、命令和 gRPC 作为探测方式。探测方式相同,并不意味着语义相同:一个 HTTP 探针可以用于启动检查、就绪检查或存活检查,而它们失败后的处理完全不同。


1. Probe 在 Kubernetes 中处于什么位置

Pod 中的每个容器由 kubelet 管理。kubelet 根据容器状态和 Probe 结果,更新本机保存的容器状态,并通过控制面反映出 Pod 的部分状态。

简化后的路径如下:

flowchart LR
    K[kubelet] --> P[Probe worker]
    P --> C[容器端点或进程]
    C --> R[探测结果]
    R --> K
    K -->|Startup 失败达到阈值| X[停止并重启容器]
    K -->|Liveness 失败达到阈值| X
    K -->|Readiness 失败| CS[ContainerStatus.ready=false]
    CS --> PC[Pod Ready Condition=false]
    PC --> ES[EndpointSlice ready=false]
    ES --> S[Service 流量选择]

这里有几个重要边界:

  1. Probe 由 kubelet 执行,不是由 API Server 执行。
  2. HTTP、TCP 和 gRPC 探针通常从 Pod 所在节点发起,不能把它们等同于“从集群外部访问应用”。
  3. Readiness 结果会影响 Service 的后端选择,但不会让 Pod 进入 Failed Phase。
  4. Liveness 或 Startup 失败会触发容器重启,但通常不会直接删除整个 Pod。
  5. Pod 的整体状态还受到 restartPolicy、调度、驱逐、终止流程等机制影响,不能仅凭 Probe 判断 Pod 的 Phase

1.1 容器就绪、Pod 就绪和 Service 后端不是同一个概念

容器状态中的:

status:
  containerStatuses:
  - name: app
    ready: true

表示该容器最近一次 Readiness 判定为成功。

Pod 的 Ready Condition 通常要求 Pod 中所有需要就绪的容器都满足条件。若配置了额外的 Pod Readiness Gate,还需要这些条件也为 True

Service 使用 EndpointSlice 选择后端时,通常依据端点的 ready 状态。于是存在这样的故障路径:

应用进程仍在运行
    ↓
Readiness Probe 失败
    ↓
containerStatuses[].ready=false
    ↓
Pod Ready Condition=false
    ↓
EndpointSlice.ready=false
    ↓
Service 不再把新连接发给该 Pod

这与下面的路径不同:

应用进程失去响应
    ↓
Liveness Probe 连续失败
    ↓
kubelet 停止容器
    ↓
按 restartPolicy 尝试重启容器

Readiness 是“暂时不要给我流量”,Liveness 是“这个进程需要被恢复”。


2. 三种 Probe 的完整语义

2.1 Startup Probe:启动阶段的保护门

Startup Probe 用来判断容器中的应用是否已经完成启动。只要配置了 Startup Probe,在它第一次成功之前,kubelet 不会执行该容器的 Liveness Probe 和 Readiness Probe。

状态过程可以表示为:

容器启动
  ↓
Startup Probe 执行
  ├─ 失败:累计失败次数,达到 failureThreshold 后重启容器
  └─ 成功:Startup 阶段结束
              ↓
          开始执行 Liveness 和 Readiness

因此,Startup Probe 并不是“又一种 Liveness Probe”。它的关键作用是把慢启动时间从 Liveness 的检测窗口中隔离出来。

没有 Startup Probe 时,慢启动应用可能在初始化期间被 Liveness 杀死:

容器启动
  ↓ 10 秒
第一次 Liveness 检查失败
  ↓
连续失败达到阈值
  ↓
容器被重启
  ↓
应用永远无法完成启动

配置 Startup Probe 后,Liveness Probe 在启动成功前不会干扰应用:

startupProbe:
  httpGet:
    path: /startup
    port: 8080
  periodSeconds: 5
  failureThreshold: 24

livenessProbe:
  httpGet:
    path: /live
    port: 8080
  periodSeconds: 10
  failureThreshold: 3

在名义配置下,Startup Probe 给应用约 24 × 5 = 120 秒的探测窗口。实际失败确认时间还受首次探测时机、探测耗时和 kubelet 调度影响,因此不能把这个乘积当作严格的墙上时钟保证。

Startup Probe 成功后,失败计数不会继续阻塞其他 Probe;之后如果 Liveness 失败,仍然会触发重启。

2.2 Readiness Probe:是否接收流量

Readiness Probe 表示容器是否具备处理业务请求的条件。Readiness 失败时,kubelet 不会重启容器,而是将容器标记为未就绪。

典型场景包括:

  • 应用仍在加载模型或缓存;
  • 依赖的数据库、配置中心或密钥服务暂时不可用;
  • 应用正在排空连接,准备退出;
  • 过载保护被触发;
  • 实例仍然存活,但不应该接收新的业务请求。

例如:

readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  initialDelaySeconds: 2
  periodSeconds: 5
  timeoutSeconds: 2
  failureThreshold: 2
  successThreshold: 2

这里要求连续两次成功后才重新就绪,连续两次失败后才变为未就绪。successThreshold 对 Readiness 可以大于 1;对 Startup 和 Liveness 必须为 1。

Readiness 失败的结果不是“Pod 被删除”,也不是“容器被重启”。如果所有副本都因为错误的 Readiness Probe 失败,Service 可能暂时没有可用后端,但容器仍全部运行着。

这正是误配置很危险的原因:系统可能没有崩溃,却完全无法接收流量。

2.3 Liveness Probe:是否需要重启容器

Liveness Probe 用来发现进程仍然存在、但已经无法继续提供服务的情况,例如:

  • 事件循环死锁;
  • 内部线程池永久卡死;
  • 应用陷入无法自行恢复的状态;
  • 进程没有退出,但关键处理路径已经失效。

Liveness 失败达到 failureThreshold 后,kubelet 会停止容器,并根据 Pod 的 restartPolicy 处理后续重启。对于常见的 Deployment,Pod 通常使用 restartPolicy: Always,因此容器会被重新启动。

livenessProbe:
  httpGet:
    path: /live
    port: 8080
  periodSeconds: 10
  timeoutSeconds: 2
  failureThreshold: 3

Liveness 不应检查所有业务依赖。例如:

数据库暂时不可用

通常更适合让 Readiness 失败,因为应用进程本身可能仍然能够恢复,重启反而会:

  1. 让正在处理的请求中断;
  2. 增加数据库重连压力;
  3. 触发多个副本同时重启;
  4. 在依赖恢复前形成重启循环。

但如果应用明确设计为“没有数据库就无法恢复”,并且重启确实是恢复动作,才有理由把这类条件纳入 Liveness。这是应用恢复语义,而不是 Kubernetes 自动推导出的结论。


3. Probe 的执行模型和参数

Probe 的主要参数如下:

参数 含义
initialDelaySeconds 容器启动后,首次探测前等待的秒数
periodSeconds 两次探测之间的目标间隔
timeoutSeconds 单次探测超时时间
successThreshold 从失败恢复为成功所需的连续成功次数
failureThreshold 从成功变为失败所需的连续失败次数
terminationGracePeriodSeconds 该 Probe 触发容器终止时使用的终止宽限期,版本需确认

其中,failureThreshold 不是“失败一次就重启”的开关,而是连续失败判定所需的次数。名义上的失败确认时间可以近似理解为:

TdetectTfirst+(F1)P+DT_{\text{detect}} \approx T_{\text{first}} + (F-1)P + D

变量含义:

  • TfirstT_{\text{first}}:首次探测开始前的等待时间,通常受 initialDelaySeconds 影响;
  • FFfailureThreshold
  • PPperiodSeconds
  • DD:最后一次探测耗时,最多接近 timeoutSeconds

这个公式表达的是推导直觉,而不是严格实时保证。kubelet 还会受到探测执行时间、节点负载、容器运行时和调度时序影响。

例如:

periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3

如果首次检查立即开始,且每次失败都在 2 秒超时,那么大致会经历:

t=0:第 1 次失败
t≈10:第 2 次失败
t≈20:第 3 次失败
t≈22:完成第 3 次超时,确认失败

如果 initialDelaySeconds: 30,则整体时间大致再增加 30 秒。实际时序应通过 kubelet 事件和容器状态验证,而不能依赖手算值进行精确告警。

3.1 默认值会造成隐含行为

常见字段默认值包括:

  • periodSeconds 默认 10 秒;
  • timeoutSeconds 默认 1 秒;
  • failureThreshold 默认 3;
  • successThreshold 默认 1;
  • initialDelaySeconds 默认 0。

timeoutSeconds: 1 对本地 HTTP 端点可能足够,但对于以下端点可能过短:

  • JVM 第一次触发较长 Stop-The-World;
  • 需要加锁的复杂健康检查;
  • 节点 CPU 被严重限制;
  • gRPC 服务端偶发排队;
  • 探针访问路径包含不必要的外部依赖。

增加超时时间只能解决“合理的短暂延迟”,不能掩盖长期阻塞。否则探针本身会占用资源,且失败确认会变慢。

3.2 探针成功和失败如何定义

不同探测方式的成功条件不同:

  • HTTP:通常返回 HTTP 200399 的状态码视为成功;
  • TCP:能够建立连接视为成功;
  • Exec:在容器内执行命令,退出码为 0 视为成功;
  • gRPC:调用标准 gRPC Health Checking Protocol,服务状态为 SERVING 视为成功。

HTTP 响应体通常不会被用来判断业务内容是否正确。若接口返回 HTTP 200,但 JSON 中表示数据库已经不可用,Probe 仍可能被判断为成功。因此健康端点应明确设计返回码和检查范围。


4. HTTP Probe:最常用,但也最容易误配

一个完整的 HTTP Probe 示例:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: probe-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: probe-demo
  template:
    metadata:
      labels:
        app: probe-demo
    spec:
      containers:
      - name: app
        image: nginx:1.27
        ports:
        - name: http
          containerPort: 80
        readinessProbe:
          httpGet:
            path: /
            port: http
          periodSeconds: 5
          timeoutSeconds: 2
          failureThreshold: 3
        livenessProbe:
          httpGet:
            path: /
            port: http
          periodSeconds: 10
          timeoutSeconds: 2
          failureThreshold: 3

应用配置:

kubectl apply -f probe-demo.yaml
kubectl get pods -l app=probe-demo -w

预期可以看到 Pod 最终类似:

NAME                          READY   STATUS    RESTARTS   AGE
probe-demo-xxxxxxxxxx-xxxxx   1/1     Running   0          30s

这里:

  • port: http 使用容器端口的名称;
  • kubelet 从节点访问 Pod IP 的 80 端口;
  • / 返回 2xx,因此两个 Probe 都成功;
  • 如果 Nginx 仍在运行但 / 返回 500,Readiness 会失败,Liveness 也最终可能触发重启。

4.1 HTTP Probe 的地址和请求属性

HTTP 探针可以配置:

httpGet:
  scheme: HTTP
  host: 127.0.0.1
  port: 8080
  path: /ready
  httpHeaders:
  - name: X-Probe
    value: kubernetes

需要区分:

  • port 是容器网络命名空间中可访问的端口;
  • host 是目标主机名,通常不应随意填写 Service 名称;
  • path 必须与应用实际路由一致;
  • scheme: HTTPS 要求端点支持 TLS;
  • 自定义 Header 可用于区分探针请求,但不应把长期有效的敏感凭据直接写入 Pod YAML。

如果应用根据 Host Header 选择虚拟主机,必须确认 kubelet 发送的 Host 是否符合应用要求。不要为了绕过路由而把生产认证 Token 放在 Probe 配置中,因为它会出现在 Pod 对象及其审计、备份或调试输出中。

4.2 常见 HTTP 误配置

端口写成 Service 端口

假设 Service 是:

ports:
- port: 80
  targetPort: 8080

容器实际监听 8080。Probe 应访问:

httpGet:
  port: 8080

而不是根据 Service 的 port: 80 填 80。Probe 直接访问 Pod,不经过 Service 的端口转换。

服务只监听 127.0.0.1

如果进程只监听回环地址:

127.0.0.1:8080

而不是:

0.0.0.0:8080

节点通过 Pod IP 访问时可能无法建立连接。应用内部用 curl localhost:8080 成功,不代表 kubelet 的 HTTP Probe 成功。

依赖 Ingress 或外部 DNS

Probe 访问 Pod IP,不会自动经过 Ingress、外部负载均衡器或公网 DNS。把 /healthz 配置成必须经过网关鉴权、Host 路由或外部证书链的地址,容易导致“应用本身正常,但 Probe 路径不通”。


5. TCP Probe:只证明“端口能接受连接”

TCP Probe 的配置很简单:

readinessProbe:
  tcpSocket:
    port: 5432
  periodSeconds: 5
  timeoutSeconds: 1

它只做 TCP 连接建立检查。成功意味着:

从探测端能够连接到目标端口

它不证明:

  • 数据库能执行查询;
  • HTTP 服务路由正确;
  • gRPC 服务状态为 SERVING
  • 应用能够处理业务请求;
  • 端口背后的服务没有死锁。

因此 TCP Probe 适合检查“端口是否已经监听”的早期条件,或者某些没有健康检查协议的简单服务。对数据库这类服务,TCP 成功而查询失败是完全可能的。

一个典型反例:

应用进程启动
  ↓
先 bind(8080)
  ↓
后台线程还未加载配置
  ↓
TCP Probe 成功
  ↓
Service 开始发送流量
  ↓
HTTP 请求全部返回 503

如果业务需要“已经可以服务”,HTTP、gRPC 或 Exec 检查通常更能表达真实条件。


6. Exec Probe:表达力强,但成本和安全边界更复杂

Exec Probe 在容器内执行命令:

readinessProbe:
  exec:
    command:
    - /bin/sh
    - -c
    - test -f /tmp/application-ready
  periodSeconds: 5
  timeoutSeconds: 1

应用在完成初始化后创建文件:

touch /tmp/application-ready

命令退出码为 0 时,探针成功;非 0 时失败。

Exec 的优点是可以检查容器内部状态,例如:

  • 本地 UNIX Socket;
  • 本地文件;
  • 进程内部生成的状态;
  • 没有网络端点的应用。

但它有三个重要风险:

  1. 每次检查都要创建进程,频率高时会增加 CPU 和 PID 压力;
  2. 命令依赖镜像中的 Shell、工具和路径;
  3. 如果命令包含秘密、复杂替换或不安全输入,可能引入安全问题。

下面的配置在精简镜像中经常失败:

exec:
  command: ["sh", "-c", "curl -f http://127.0.0.1:8080/ready"]

原因不是应用一定不健康,而是镜像可能没有 shcurl。应在目标镜像中验证:

kubectl exec deploy/probe-demo -- sh -c 'command -v curl'

生产配置应尽量使用稳定、短小、无副作用的命令,避免在 Probe 中执行数据库迁移、缓存清理或其他会改变系统状态的操作。


7. gRPC Probe:检查标准健康服务,不是任意 RPC

Kubernetes 原生 gRPC Probe 使用 gRPC Health Checking Protocol。它不是向任意业务 RPC 发请求,也不是把 HTTP/2 端口当作“能连通就算成功”。

典型配置:

startupProbe:
  grpc:
    port: 9090
    service: my.api.v1.Application
  periodSeconds: 5
  timeoutSeconds: 2
  failureThreshold: 30

readinessProbe:
  grpc:
    port: 9090
    service: my.api.v1.Application
  periodSeconds: 5
  timeoutSeconds: 2
  failureThreshold: 2

livenessProbe:
  grpc:
    port: 9090
    service: my.api.v1.Application
  periodSeconds: 10
  timeoutSeconds: 2
  failureThreshold: 3

应用必须实现标准健康检查服务:

grpc.health.v1.Health/Check

调用时的 service 字符串由应用定义。若 service 省略,通常检查整体服务器状态;若填写,则检查指定逻辑服务。应用需要返回标准状态,例如:

  • SERVING:Probe 成功;
  • NOT_SERVING:Probe 失败;
  • SERVICE_UNKNOWN:通常表示指定服务没有健康状态;
  • RPC 错误或无法连接:Probe 失败。

7.1 gRPC Probe 的限制

原生 gRPC Probe 的配置能力比“用 grpcurl 自己执行命令”更有限:

  • port 使用数字端口;
  • 不能像 HTTP Probe 那样指定 HTTP Header;
  • 不能配置用户名、密码或 Bearer Token;
  • 不能直接配置 TLS 证书、SNI 或客户端证书;
  • 不适合依赖复杂鉴权流程的健康端点;
  • 应用必须支持标准 gRPC Health Checking Protocol。

因此,下面这种理解是错误的:

gRPC 端口能建立 TCP 连接,所以 gRPC Probe 一定成功

实际过程至少是:

kubelet 建立 gRPC 连接
  ↓
调用标准 Health/Check RPC
  ↓
解析 gRPC 健康状态
  ↓
只有 SERVING 才判定成功

如果服务启用了 mTLS,而 Kubernetes 内置 gRPC Probe 无法提供客户端证书,就可能无法直接使用原生 gRPC Probe。此时可以考虑:

  • 暴露一个适合节点探测的独立健康端点;
  • 使用带有必要 TLS 配置的 Exec Probe;
  • 调整应用的健康服务设计,但必须评估暴露面和认证边界。

Exec 方案示例:

readinessProbe:
  exec:
    command:
    - /usr/local/bin/grpc-health-probe
    - -addr=127.0.0.1:9090
    - -service=my.api.v1.Application
  periodSeconds: 5
  timeoutSeconds: 3

这不是 Kubernetes 原生 gRPC Probe,而是容器内的外部工具。它的版本、参数、TLS 行为和退出码都必须由镜像维护者验证,不能把两者混为一谈。

7.2 gRPC 健康状态应由应用明确维护

gRPC 健康服务返回 SERVING 并不自动意味着所有业务依赖都正常。应用需要决定各服务名对应的状态。例如:

整体服务:SERVING
写入服务:NOT_SERVING
读取服务:SERVING

如果写入服务依赖的数据库不可用,那么 Readiness Probe 可以检查写入服务名;而只提供只读能力的实例仍可能对读取流量保持就绪。

这比简单检查“gRPC 端口是否开放”更精确,但也要求应用正确更新 Health 状态。健康服务本身实现错误,会把业务故障转化为错误的调度和流量结果。


8. Startup、Readiness、Liveness 的组合规则

推荐先明确三种状态,再决定是否复用端点:

状态 需要回答的问题 失败后的主要动作
Startup 初始化是否完成? 达到失败阈值后重启容器
Readiness 现在是否应该接收流量? 从可用后端中移除,不重启
Liveness 进程是否需要被恢复? 达到失败阈值后重启容器

端点可以相同,但语义必须不同。最危险的复用方式是:

同一个端点检查数据库、缓存、配置中心和外部 API
同时用于 Readiness 和 Liveness

当外部依赖短暂故障时,可能发生:

外部依赖故障
  ↓
Readiness 失败:实例被摘流
  ↓
Liveness 也失败:实例被重启
  ↓
所有副本同时重连外部依赖
  ↓
外部依赖压力更大
  ↓
重启和故障互相放大

更稳妥的逻辑通常是:

Startup:确认进程初始化完成,允许较慢
Readiness:确认当前可以接收业务流量,可包含必要依赖
Liveness:只检查进程是否仍有合理的自我恢复能力

但这不是强制规范。实际划分取决于应用能否从依赖故障中自行恢复。


9. 终止、重启和 Probe 的关系

当 Liveness 或 Startup 失败达到阈值时,kubelet 会终止容器。容器终止可能经过:

  1. 发送终止信号;
  2. 等待终止宽限期;
  3. 超时后发送强制终止信号;
  4. 根据 restartPolicy 启动新容器。

Pod 级别的 spec.terminationGracePeriodSeconds 控制默认终止宽限期。较新的 Kubernetes API 还支持在 Probe 级别设置 terminationGracePeriodSeconds,用于覆盖该容器因 Probe 失败而终止时的宽限期;使用前应确认集群版本和 API Server 是否支持该字段。

不要把 Probe 失败与优雅下线混为一谈。应用收到终止信号后,通常应:

  • 停止接受新请求;
  • 让现有请求完成或超时;
  • 更新 Readiness,使实例尽快摘流;
  • 关闭连接和资源。

如果只依赖 Liveness 失败触发重启,应用可能先被强制终止,导致请求丢失。发布或主动删除 Pod 时,Readiness、EndpointSlice 更新和终止宽限期共同决定排空效果。


10. 误配置风险:从 YAML 到故障的完整路径

10.1 把启动时间当成固定延迟

错误配置:

livenessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 5
  failureThreshold: 3

假设应用启动有时需要 30 秒。初始延迟只是“首次检查前等待”,不是“禁用检查直到应用一定完成”。如果应用在第 10 秒仍未监听端口,连续失败后可能在应用真正启动前就被重启。

改进方式是使用 Startup Probe:

startupProbe:
  httpGet:
    path: /startup
    port: 8080
  periodSeconds: 5
  failureThreshold: 12

livenessProbe:
  httpGet:
    path: /live
    port: 8080
  periodSeconds: 10
  failureThreshold: 3

Startup 允许启动阶段最多经历约 60 秒的名义失败窗口,成功后才开始执行 Liveness。

10.2 把业务依赖放进 Liveness

错误现象:

数据库出现短暂网络故障
  ↓
/health 返回 500
  ↓
Liveness 连续失败
  ↓
所有副本重启
  ↓
数据库恢复时收到大量重连

诊断时应比较:

kubectl describe pod <pod-name>
kubectl get pod <pod-name> -o wide
kubectl logs <pod-name> --previous
kubectl get endpointslice -l kubernetes.io/service-name=<service-name> -o yaml

重点观察:

  • Events 中是否有 Liveness probe failed
  • RESTARTS 是否持续增加;
  • --previous 中是否存在被终止前的日志;
  • EndpointSlice 中端点是否 ready: false
  • 所有副本是否在相近时间重启。

如果应用只是暂时不能处理业务,优先让 Readiness 失败,而不是立即重启。

10.3 探针路径需要认证

应用把所有 HTTP 路由都置于认证中间件之后:

GET /ready → 401 Unauthorized

即使应用完全正常,HTTP Probe 也会失败。解决方式不是把认证失败视为健康,而是提供明确的内部健康路由,并通过网络策略、监听地址或专用端口限制访问。

同时,健康路由不能泄露:

  • 数据库密码;
  • 内部拓扑;
  • 详细堆栈;
  • 用户数据;
  • 外部依赖的敏感错误信息。

10.4 Probe 自身消耗大量资源

如果 Probe 执行一次会:

  • 扫描大量文件;
  • 访问多个远程依赖;
  • 执行复杂 SQL;
  • 启动大型命令;
  • 分配大量对象;

那么检查本身可能成为负载来源。尤其当每个 Pod 有多个 Probe,且副本数量较大时,节点上的探针开销会叠加。

健康检查应当短小、有限、幂等,并且对超时有明确处理。Readiness 可以比 Liveness 检查更多业务条件,但仍不应变成完整业务交易。


11. 如何验证 Probe,而不是只看 Pod 是否 Running

11.1 查看配置和事件

kubectl describe pod <pod-name>

重点查看:

Readiness probe failed: HTTP probe failed with statuscode: 503
Liveness probe failed: dial tcp ...: connect: connection refused
Startup probe failed: command timed out

这些信息可以区分:

  • 端口未监听;
  • 路径不存在;
  • 返回码不在成功范围;
  • 命令不存在;
  • 命令超时;
  • gRPC 服务状态不是 SERVING

查看原始对象,确认实际生效字段:

kubectl get pod <pod-name> -o yaml
kubectl get deployment <deployment-name> -o yaml

不要只看本地文件,因为 Deployment 滚动更新期间,旧 Pod 和新 Pod 可能使用不同配置。

11.2 从容器内测试应用端点

HTTP:

kubectl exec <pod-name> -- sh -c \
  'wget -S -O - http://127.0.0.1:8080/ready'

如果镜像没有 wget,可以使用临时调试容器或应用自带工具。测试 127.0.0.1 只能验证容器内部访问;还应确认 Pod IP 路径:

kubectl get pod <pod-name> -o wide

在具备网络工具的调试容器中访问 Pod IP,才能更接近 kubelet 的网络路径。

注意:kubectl exec 进入容器后测试成功,不一定说明 Probe 成功,因为两者可能有不同的:

  • 网络源地址;
  • DNS 配置;
  • 用户权限;
  • 环境变量;
  • 文件系统;
  • TLS 和 Host Header。

11.3 查看重启和历史日志

kubectl get pod <pod-name> \
  -o jsonpath='{range .status.containerStatuses[*]}{.name}{" restart="}{.restartCount}{" ready="}{.ready}{" state="}{.state}{"\n"}{end}'

如果容器已经重启:

kubectl logs <pod-name> -c <container-name> --previous

--previous 对诊断 Probe 引起的重启尤其重要,因为当前容器日志可能只显示“重新启动后正常运行”的内容。

11.4 验证 Service 是否仍有可用端点

kubectl get endpointslice \
  -l kubernetes.io/service-name=<service-name> -o yaml

检查端点的:

conditions:
  ready: false

这可以证明 Readiness 失败已经影响到 Service 后端选择。但不同 Service 实现、代理模式和流量来源可能有额外行为,不能用单个客户端连接结果替代 EndpointSlice 状态验证。


12. 一个带错误场景的可运行演示

先部署一个故意会失败 Readiness 的 Pod:

apiVersion: v1
kind: Pod
metadata:
  name: probe-failure-demo
  labels:
    app: probe-failure-demo
spec:
  containers:
  - name: web
    image: nginx:1.27
    readinessProbe:
      httpGet:
        path: /does-not-exist
        port: 80
      periodSeconds: 3
      timeoutSeconds: 1
      failureThreshold: 2

执行:

kubectl apply -f probe-failure-demo.yaml
kubectl get pod probe-failure-demo -w
kubectl describe pod probe-failure-demo

预期现象类似:

NAME                 READY   STATUS    RESTARTS   AGE
probe-failure-demo   0/1     Running   0          15s

这个结果说明:

  • Nginx 进程仍在运行,所以 STATUS 仍可能是 Running
  • /does-not-exist 返回 404;
  • Readiness 失败,因此 READY0/1
  • 因为没有 Liveness Probe,所以不会因为该失败自动重启;
  • 若把它放入 Service,通常不会作为就绪后端接收新流量。

再检查事件:

kubectl describe pod probe-failure-demo

应能看到 HTTP Probe 失败相关事件。修正路径为 /

kubectl patch pod probe-failure-demo --type='json' \
  -p='[{"op":"replace","path":"/spec/containers/0/readinessProbe/httpGet/path","value":"/"}]'

对于生产工作负载,不建议直接修改由 Deployment 管理的 Pod;应修改 Deployment 清单后重新发布。这个演示的价值在于区分:

进程运行 ≠ 容器就绪 ≠ Service 可路由

13. 设计健康端点时必须先回答的边界问题

13.1 /live 应该检查什么

Liveness 端点应尽量检查进程本身是否仍能完成最基本的工作。例如:

  • 主事件循环是否可响应;
  • 核心线程是否仍在工作;
  • 应用内部是否进入明确的不可恢复状态。

它不必验证每个下游服务,也不应执行大型业务请求。

13.2 /ready 应该检查什么

Readiness 端点应检查“接收流量后是否能够提供承诺的服务”。检查内容取决于流量类型:

  • 读请求可能只需要缓存和只读数据库;
  • 写请求可能需要主数据库;
  • 消费者可能需要队列连接;
  • 分片服务可能需要当前分片分配完成;
  • 发布期间可能需要确认新版本已经加载配置。

如果同一 Pod 承担不同流量类型,可以使用不同的 gRPC service 名称或不同的 HTTP 路径表达不同就绪条件。

13.3 /startup 应该检查什么

Startup 端点应回答初始化是否完成,而不是要求所有运行时依赖永久正常。它可以检查:

  • 配置是否加载;
  • 必需的本地资源是否准备好;
  • 模型是否加载完成;
  • 监听服务是否启动;
  • 应用是否已完成不可跳过的初始化。

Startup 成功后,运行时依赖的变化应主要由 Readiness 和应用自身恢复逻辑处理。


14. 版本和实现边界

Kubernetes Probe 的核心字段属于稳定 API,但具体能力仍需要结合集群版本确认。

  • 原生 gRPC Probe 在较新的 Kubernetes 版本中已稳定;生产环境应确认控制面和节点版本,而不是只看客户端 kubectl 版本。
  • gRPC Probe 需要应用实现标准 gRPC Health Checking Protocol,不能由 Kubernetes 自动把任意 RPC 转换为健康检查。
  • Probe 级别的 terminationGracePeriodSeconds 属于较新的能力,旧集群可能拒绝该字段或不按预期处理;应通过目标集群的 API 校验和实际 Pod 行为验证。
  • 云厂商的 Service、负载均衡器、健康检查和节点网络实现可能增加额外探测,但不能假设云负载均衡器的健康检查会替代 kubelet Probe。
  • kubectl 是客户端工具,kubectl get pod 显示的是 API Server 中的状态快照;探测的实际执行者仍是节点上的 kubelet。

检查集群版本:

kubectl version
kubectl api-resources
kubectl explain pod.spec.containers.readinessProbe
kubectl explain pod.spec.containers.livenessProbe
kubectl explain pod.spec.containers.startupProbe

kubectl explain 显示的是当前客户端或其 OpenAPI 信息可见的字段,最终能否被集群接受仍应以目标 API Server 验证为准。


15. 归纳:用失败后的动作反推 Probe 类型

可以用一个简单的决策过程避免把 Probe 配错:

应用正在启动,但启动时间不可预测?
  → Startup Probe

应用还活着,但现在不能接收请求?
  → Readiness Probe

应用已进入无法自行恢复的状态,重启是恢复手段?
  → Liveness Probe

只需要确认端口正在监听?
  → TCP Probe

需要检查容器内文件或进程状态?
  → Exec Probe

服务使用标准 gRPC Health Checking Protocol?
  → gRPC Probe

最终应验证的不是“YAML 能否通过校验”,而是故障路径是否符合预期:

启动变慢时:不会被过早重启
依赖短暂故障时:会摘流,而不是盲目重启
进程死锁时:最终会被恢复
发布终止时:会先停止接收新流量
端口可连接但业务不可用时:Probe 不会错误地报告成功

Probe 是 Kubernetes 工作负载稳定性控制面的一部分。它连接了应用内部状态、kubelet 的容器管理、Pod Ready Condition、EndpointSlice 和 Service 流量选择。只有明确区分 Startup、Readiness 和 Liveness 的因果语义,再选择与应用协议匹配的 HTTP、TCP、Exec 或 gRPC 检查方式,健康检查才不会从故障隔离工具变成故障放大器。


系列导航与关联阅读

官方资料

本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。