3. 健康检查基础:健康检查的目的、类型与探针机制

健康检查是弹性伸缩与发布流程中最重要的质量门禁之一。没有健康检查,系统无法判断实例是否真正就绪、是否存活、是否能够正常处理业务流量。Kubernetes 提供了三种探针(Probe)来覆盖不同阶段的应用状态检测:Liveness、Readiness 和 Startup。每种探针都有明确的职责边界,组合使用才能构建完整的健康门禁体系。

3.1 健康检查的核心目的

健康检查不是简单的“ping 通即健康”,而是从多个维度验证应用实例的实际运行质量。在弹性伸缩和发布场景中,健康检查需要解决以下关键问题:

  • 检测实例是否存活:进程是否运行、是否陷入死锁或内存泄漏导致的假死状态。
  • 检测实例是否就绪:应用是否已完成初始化、依赖服务是否可用、是否能够接收并处理请求。
  • 检测实例是否启动完成:对于启动缓慢的应用(如 Java 应用、加载大量模型的服务),需要区分“正在启动”和“启动失败”。
  • 避免流量损失:在滚动更新或弹性扩容时,确保只有健康的实例才被加入负载均衡池。
  • 加速故障自愈:自动重启不健康的实例,减少人工介入时间。
实践要点:健康检查的粒度应该与业务场景匹配。对于无状态服务,简单的端口检测可能足够;对于有状态服务或关键业务链路,建议使用自定义的 HTTP 端点或 gRPC 探针,验证核心业务流程是否正常。

3.2 三种探针类型与职责对比

Kubernetes 定义了三种探针,分别对应应用生命周期的不同阶段。下表清晰展示了它们的职责、触发动作和典型使用场景:

探针类型 检测目的 失败后动作 典型场景
Liveness Probe 检测容器是否存活(进程是否健康) 重启容器(kubelet 执行 restart) 检测死锁、内存泄漏、进程挂起
Readiness Probe 检测容器是否就绪(能否接收流量) 从 Service Endpoints 中移除 滚动更新、扩容时等待应用就绪
Startup Probe 检测容器是否启动完成(慢启动保护) 重启容器(启动阶段失败) Java 应用、模型加载、冷启动耗时服务
常见误区:不要将 Liveness Probe 用于检测外部依赖(如数据库、缓存)。如果外部依赖短暂不可用,Liveness 失败会导致容器重启,造成级联故障。外部依赖的健康应该由 Readiness Probe 处理,或者由应用内部的熔断/重试机制来保障。

3.3 探针机制:四种检测方式

Kubernetes 支持四种探针执行方式,每种方式适用于不同的应用协议和环境。选择哪种方式取决于应用暴露的端口、协议以及是否需要自定义检测逻辑。

3.3.1 HTTP GET 探针

最常用的探针方式。kubelet 向容器的指定端口和路径发起 HTTP GET 请求,返回 200-399 状态码视为成功,其他状态码或超时视为失败。

livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
    httpHeaders:
    - name: X-Custom-Header
      value: health-check
  initialDelaySeconds: 5
  periodSeconds: 10
  timeoutSeconds: 3
  failureThreshold: 3

3.3.2 TCP Socket 探针

kubelet 尝试与容器的指定端口建立 TCP 连接。连接成功视为健康,失败则视为不健康。适用于只暴露 TCP 端口、没有 HTTP 服务的应用(如数据库、消息队列)。

readinessProbe:
  tcpSocket:
    port: 3306
  initialDelaySeconds: 10
  periodSeconds: 5

3.3.3 gRPC 探针

Kubernetes 1.24+ 原生支持 gRPC 探针。kubelet 通过 gRPC 健康检查协议(grpc.health.v1.Health/Check)检测应用状态。适用于微服务架构中大量使用 gRPC 通信的场景。

livenessProbe:
  grpc:
    port: 50051
    service: "myapp.HealthService"
  initialDelaySeconds: 5
  periodSeconds: 10

3.3.4 Exec 命令探针

kubelet 在容器内执行一条命令,命令退出码为 0 视为健康,非零视为不健康。适用于无法通过网络端口检测的场景,或者需要执行自定义脚本验证内部状态。

readinessProbe:
  exec:
    command:
    - cat
    - /tmp/healthy
  initialDelaySeconds: 5
  periodSeconds: 10
选择建议:
  • 优先使用 HTTP GET 探针,因为它能返回具体的业务状态码,便于调试。
  • 如果应用只暴露 TCP 端口,使用 TCP Socket 探针。
  • 如果应用使用 gRPC 通信,使用 gRPC 探针,避免额外的 HTTP 封装。
  • 仅在无法通过网络检测时使用 Exec 探针,注意 Exec 探针会消耗容器内资源。

3.4 探针参数与调优原则

探针的行为由一组参数控制,错误的参数配置可能导致频繁重启或延迟发现故障。下表列出关键参数及其推荐配置思路:

参数 说明 推荐配置
initialDelaySeconds 容器启动后等待多久开始第一次探测 根据应用启动时间设置,通常 5-30s
periodSeconds 每次探测的间隔时间 10-30s,避免过于频繁
timeoutSeconds 单次探测的超时时间 1-5s,超时即视为失败
failureThreshold 连续失败多少次后判定为不健康 3-5 次,避免瞬态波动导致误判
successThreshold 连续成功多少次后判定为健康(仅 Readiness) 1(默认),通常不需要修改

3.5 三种探针与四种机制的组合关系图

下图展示了三种探针在应用生命周期中的位置,以及四种检测方式如何与探针类型结合:

三种探针与四种检测机制组合关系 三种探针与四种检测机制组合关系 应用生命周期阶段 容器启动 → 初始化 → 运行中 → 终止 Startup Probe 慢启动保护 启动阶段专用 失败则重启容器 Liveness Probe 存活检测 运行期持续检测 失败则重启容器 Readiness Probe 就绪检测 控制流量接入 失败则移除流量 四种检测机制(探针执行方式) HTTP GET 请求 /healthz 状态码 200-399 TCP Socket 端口连接检测 连接成功即健康 gRPC Health/Check 协议 1.24+ 原生支持 Exec 命令 容器内执行脚本 退出码 0 为健康 组合使用原则 Startup Probe 保护慢启动 → Liveness Probe 持续保活 → Readiness Probe 控制流量接入

3.6 三种探针的协同工作流程

在实际发布和弹性伸缩过程中,三种探针需要协同工作才能实现平滑的流量切换和故障自愈。下图展示了从容器启动到稳定运行的完整探针协作流程:

三种探针协同工作流程 三种探针协同工作流程 阶段一:启动阶段(Startup Probe 主导) 容器启动 → Startup Probe 开始检测 → 应用初始化(加载配置、连接依赖) 期间 Liveness 和 Readiness 探针暂停,避免误判 阶段二:就绪阶段(Readiness Probe 主导) Startup 成功 → Readiness Probe 开始检测 → 应用是否准备好接收流量 Readiness 成功 → 实例加入 Service Endpoints,开始接收请求 阶段三:运行阶段(Liveness + Readiness 持续检测) Liveness 持续检测存活状态,失败则重启容器 Readiness 持续检测就绪状态,失败则从 Endpoints 移除 故障处理与自愈 Liveness 失败 → 重启容器 → 重新进入阶段一

3.7 最佳实践与常见陷阱

推荐实践:
  • 为每个容器配置 Readiness Probe:这是流量门禁的基础,确保只有就绪的实例接收流量。
  • 为关键服务配置 Liveness Probe:避免死锁或内存泄漏导致的服务假死。
  • 为慢启动应用配置 Startup Probe:防止启动阶段的正常耗时被 Liveness 误判为故障。
  • 探针端点返回业务状态:HTTP 探针的 /healthz 端点应返回应用的核心依赖状态,而不仅仅是进程存活。
  • 设置合理的超时和阈值:避免因网络抖动或短暂负载导致频繁重启。
常见陷阱:
  • 探针端点包含耗时操作:不要在健康检查端点中执行数据库查询或外部 API 调用,这会导致探针超时和级联故障。
  • 所有探针使用相同的端点:Liveness 和 Readiness 应该使用不同的端点或不同的检测逻辑,因为它们的职责不同。
  • 忽略 Startup Probe:对于启动时间超过 initialDelaySeconds + failureThreshold * periodSeconds 的应用,不配置 Startup Probe 会导致容器被反复重启。
  • Exec 探针执行复杂脚本:Exec 探针会消耗容器内的 CPU 和内存资源,复杂脚本可能影响应用性能。