Skip to content

Kubernetes 健康检查机制

探针类型目的失败后果典型使用场景
就绪探针(Readiness Probe)检测 Pod 是否准备好接收流量从 Service 的 Endpoints 中移除该 Pod应用启动慢(如加载大文件)、依赖未就绪
存活探针 (Liveness Probe)检测 Pod 是否处于运行状态(防止死锁或僵死)重启容器(kubelet 触发)应用死锁、内存泄漏、内部线程阻塞

就绪探针 = 流量开关,存活探针 = 进程守护

这句话精准概括了 Kubernetes 中两种探针的核心职责,我们可以从行为机制故障影响两个维度深入理解:

就绪探针 = 流量开关(Traffic Switch)

核心逻辑:控制流量进出

  • ✅ 作用机制

    • 探针通过 → 打开开关 → Pod 被加入 Service 的 Endpoints → 接收流量
    • 探针失败 → 关闭开关 → Pod 从 Endpoints 移除 → 流量被切断
  • ✅ 关键特性

    • 非破坏性:不会重启容器,仅影响网络流量
    • 可逆操作:故障恢复后自动重新接入流量
    • 场景驱动
      bash
      # 典型触发场景(不影响进程)
       应用启动初始化耗时较长(如加载 1GB 数据到内存)
       临时依赖故障(数据库连接中断)
       高负载时主动限流(自我保护)

存活探针 = 进程守护(Process Guardian)

核心逻辑:保障应用存活

  • ✅ 作用机制

    • 探针通过 → 守护状态正常 → 无操作
    • 探针失败 → 判定进程异常 → kubelet 杀死容器并重启
  • ✅ 关键特性

    • 破坏性操作:触发容器重建(kubectl describe pod 可见 Restart Count 增加)
    • 终极手段:用于处理无法自我恢复的僵死状态
    • 场景驱动
      bash
      # 典型触发场景(需强制重启)
       进程死锁(线程阻塞导致无响应)
       内存泄漏(进程占用 99% 内存无法释放)
       文件描述符耗尽(无法处理新请求)

场景化对比:一次数据库故障中的双探针协作

假设某订单服务 Pod 依赖 MySQL 数据库:

关键结论

探针数据库故障时的行为结果
就绪探针切断流量入口业务层优雅降级
存活探针未重启容器(内部线程仍健康)避免不必要的重启风暴

终极设计原则

  1. 就绪探针(开关)

    yaml
    readinessProbe:
      # 必须包含外部依赖检查(DB、缓存、API)
      httpGet:
        path: /readyz  # 明确语义:专为流量开关设计
        port: 8080
      initialDelaySeconds: 5  # 启动缓冲期
      periodSeconds: 5        # 高频检测(快速响应状态变化)
  2. 存活探针(守护)

    yaml
    livenessProbe:
      # 仅检测进程内部状态(禁止检查DB等外部依赖!)
      httpGet:
        path: /livez   # 明确语义:进程健康检查
        port: 8080
      initialDelaySeconds: 30  # 预留充足启动时间
      periodSeconds: 10        # 避免过于频繁触发重启
      failureThreshold: 3      # 防止瞬时故障误重启

💡 一句话记忆

  • 就绪探针是门口的保安:“服务暂时不可用,请勿入内”(流量屏蔽)
  • 存活探针是急救医生:“病人心跳停止,立即抢救!”(容器重启)

最佳实践

以下是对 Kubernetes 就绪探针和存活探针最佳实践的深度解析,结合具体场景和底层原理说明:


1. 路径分离:专用端点设计

为什么必须分离?

yaml
# 反模式:混用同一端点
livenessProbe:
  httpGet:
    path: /healthz  # 危险!

# 正解:语义化分离
readinessProbe:
  httpGet:
    path: /readyz   # 就绪专属

livenessProbe:
  httpGet:
    path: /livez    # 存活专属

实现方案对比

方案优点缺点适用场景
物理分离
(/readyz /livez)
逻辑清晰,K8s日志易排查需开发两个端点新建项目/核心服务
逻辑分离
(/healthz?type=readiness)
代码改动小故障时难以定位问题存量系统改造
Header区分
(X-Probe-Type: readiness)
无URL污染依赖Header传递,调试复杂特殊安全要求场景

终极建议: ✅ 物理分离是黄金标准 - 开发成本可被运维收益抵消


2. 超时设置:毫秒级精准控制

底层网络原理

  • 网络延迟:跨节点通信可能增加 50-100ms 延迟
  • 应用延迟:GC暂停可能导致线程阻塞数百毫秒

推荐配置公式

python
# 就绪探针超时 = 平均网络延迟 + 应用延迟 + 安全余量
readiness_timeout = (node_latency * 2) + app_max_delay + 100ms 

# 存活探针超时 = 就绪超时 * 2 + 重试缓冲
liveness_timeout = readiness_timeout * 2 + 200ms

示例场景

  • 网络环境:跨可用区延迟 30ms
  • 应用特性:Java服务GC暂停最大 200ms
  • 计算结果
    yaml
    readinessProbe:
      timeoutSeconds: 1    # 30*2 + 200 + 100 ≈ 360ms → 取整1秒
      periodSeconds: 5     # 高频检测
    
    livenessProbe:
      timeoutSeconds: 3    # 360*2 + 200 ≈ 920ms → 取整3秒
      periodSeconds: 10    # 避免频繁触发重启

3. 依赖隔离:存活探针的"防火墙"规则

为什么必须隔离?

假设存活探针检查数据库:

结果: ⚠️ CrashLoopBackOff 状态,服务完全瘫痪

存活探针的安全检查清单

允许检查禁止检查替代方案
内存使用率数据库连接状态就绪探针检查DB
线程池活跃线程数Redis/PG等外部依赖Sidecar健康检查
内部队列深度第三方API响应熔断机制 + 就绪探针
文件描述符数量网络存储挂载状态initContainer验证存储

关键代码示例(Go语言):

go
// /livez 端点实现 - 仅检查内部状态
func LivezHandler(w http.ResponseWriter, r *http.Request) {
    // 1. 检查内存
    if memUsage > 90% {
        w.WriteHeader(503)
        return
    }
  
    // 2. 检查Goroutine泄漏
    if runtime.NumGoroutine() > 1000 {
        w.WriteHeader(503)
        return
    }
  
    // 3. 通过检查
    w.WriteHeader(200)
}

4. 雪崩故障防御机制

典型雪崩场景复现

bash
时间线:
T0: 数据库故障
T1: 就绪探针失败 Pod移出Endpoints 
T2: 存活探针检测DB 失败
T3: kubelet重启容器 
T4: 新容器启动中 存活探针立即检测 失败(DB未恢复)
T5: 进入CrashLoopBackOff状态 所有Pod不可用 💥

这个序列图清晰地展示了雪崩故障的完整过程:

  1. 初始故障 (T0):数据库发生故障
  2. 正确响应 (T1):就绪探针检测到DB故障,Pod被移出服务端点
  3. 设计缺陷 (T2):存活探针错误地检查外部DB
  4. 重启风暴 (T3-T4):容器不断重启并立即检测
  5. 崩溃循环 (T5):进入CrashLoopBackOff状态
  6. 服务瘫痪:所有Pod最终不可用

关键问题点突出显示:

  • ✅ 就绪探针正确行为:隔离故障Pod
  • ❌ 存活探针设计错误:检查外部依赖
  • 💥 最终结果:级联故障导致服务完全瘫痪

这种场景完美解释了为什么存活探针必须只检查内部状态 - 任何外部依赖检查都会在依赖服务故障时触发灾难性的重启循环。

三维防御策略

  1. 存活探针避障

    yaml
    livenessProbe:
      initialDelaySeconds: 30  # 跳过启动期
      failureThreshold: 5      # 连续5次失败才重启
  2. 就绪探针熔断

    go
    // /readyz 端点增加熔断器
    if dbConnFailCount > 10 {
        // 主动返回503,避免持续重试冲击DB
        w.WriteHeader(503)
    }
  3. Sidecar 守护

    yaml
    # 使用辅助容器隔离检测
    - name: probe-agent
      image: probe-agent:1.0
      command: ["/agent", "--liveness=/proc/self/health"]

终极配置模板

yaml
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: main-app
        # 就绪探针 - 流量开关
        readinessProbe:
          httpGet:
            path: /readyz
            port: 8080
          initialDelaySeconds: 5   # 等待基础启动
          periodSeconds: 2         # 高频检测
          timeoutSeconds: 1        # 快速响应
          successThreshold: 1
          failureThreshold: 3      # 连续3次失败才剔除

        # 存活探针 - 进程守护
        livenessProbe:
          httpGet:
            path: /livez
            port: 8080
          initialDelaySeconds: 30  # 预留充足启动时间
          periodSeconds: 10        # 避免频繁检测
          timeoutSeconds: 3        # 容忍GC暂停
          failureThreshold: 5      # 防止瞬时故障误重启

💡 运维口诀就绪勤(高频),存活稳(低频); 路径分,依赖离; 超时短就绪,阈值高存活; 混用酿雪崩,分离保平安。