文章目录
- 概述
- 一、探针种类、方法与使用场景
-
- [1. 探针种类(Probe Types)](#1. 探针种类(Probe Types))
- [2. 探针检测方法(Handler Types)](#2. 探针检测方法(Handler Types))
- [3. 探针关键参数](#3. 探针关键参数)
- 二、探针使用案例
-
- [1. livenessProbe(存活探针)](#1. livenessProbe(存活探针))
- [2. readinessProbe(就绪探针)](#2. readinessProbe(就绪探针))
- [3. startupProbe(启动探针)](#3. startupProbe(启动探针))
- 三、探针接口设计最佳实践
-
- [1. 探针目标与接口职责对照表](#1. 探针目标与接口职责对照表)
- [2. 每种探针对应接口的设计最佳实践](#2. 每种探针对应接口的设计最佳实践)
-
- [Liveness Probe 接口(/live 或 /health/live)](#Liveness Probe 接口(/live 或 /health/live))
- [Readiness Probe 接口(/ready 或 /health/ready)](#Readiness Probe 接口(/ready 或 /health/ready))
- [Startup Probe 接口(/startup 或 /started)](#Startup Probe 接口(/startup 或 /started))
- [反面案例 vs 正确做法](#反面案例 vs 正确做法)
概述
Kubernetes(简称 k8s)中的探针(Probes)是用于检测容器健康状态的重要机制,它帮助 Kubernetes 决定何时将 Pod 加入服务流量、何时重启容器,以及何时从服务中剔除不可用的实例。
一、探针种类、方法与使用场景
1. 探针种类(Probe Types)
Kubernetes 提供了三种类型的探针:
(1) livenessProbe(存活探针)
- 作用:判断容器是否仍在运行。
- 行为:如果探针失败,kubelet 会杀死容器,并根据重启策略(restartPolicy)决定是否重启。
- 典型场景:应用陷入死锁、内存泄漏、无限循环等无法自行恢复的状态。
(2) readinessProbe(就绪探针)
- 作用:判断容器是否准备好接收流量。
- 行为:如果探针失败,Pod 的 IP 地址会从所有 Service 的 Endpoints中移除,即不再接收新请求。
- 典型场景:应用启动较慢(如加载大模型、连接数据库)、依赖外部服务未就绪等、应用短暂不可用时避免影响流量。
(3) startupProbe(启动探针,v1.16+ 引入)
- 作用:判断容器是否已成功启动。
- 行为:在 startupProbe 成功之前,liveness 和 readiness 探针不会执行。
- 典型场景:启动时间很长的应用(如 Java 应用、大型 ML 模型加载),避免因启动慢被误杀。
⚠️ 注意:三种探针可以同时配置,但 startupProbe 优先级最高,在其成功前其他探针不生效。
2. 探针检测方法(Handler Types)
每种探针都支持以下三种检测方式:
| 方法 | 说明 |
|---|---|
| exec | 在容器内执行一个命令,退出码为 0 表示成功。 |
| httpGet | 向容器发送 HTTP GET 请求,响应状态码在 200--399 之间表示成功。 |
| tcpSocket | 尝试与容器指定端口建立 TCP 连接,能连通即成功。 |
3. 探针关键参数
yaml
initialDelaySeconds: 5 # 容器启动后等待多少秒才开始探测
periodSeconds: 10 # 探测间隔(秒)
timeoutSeconds: 5 # 探测超时时间
successThreshold: 1 # 连续成功多少次才算通过(liveness 必须为1)
failureThreshold: 3 # 连续失败多少次才算失败
- 对于 livenessProbe,successThreshold 必须为 1。
- 对于 readinessProbe,可设为>1,用于容忍短暂失败。
二、探针使用案例
1. livenessProbe(存活探针)
yaml
livenessProbe:
httpGet:
path: /live
port: 8080
initialDelaySeconds: 15 # 容器启动后等待多少秒才开始探测
periodSeconds: 20 # 探测间隔(秒)
timeoutSeconds: 5 # 单次探测超时时间
failureThreshold: 3 # 连续失败多少次才判定为"不存活"
successThreshold: 1 # 成功阈值(liveness 必须为 1)
参数详解:
| 参数 | 默认值 | 说明 |
|---|---|---|
| initialDelaySeconds | 0 | 容器启动后延迟多久开始第一次探测。对慢启动应用至关重要(但更推荐用 startupProbe 来兜底)。 |
| periodSeconds | 10 | 每隔多少秒执行一次探测。建议 10~30 秒,太频繁影响性能,太稀疏恢复慢。 |
| timeoutSeconds | 1 | 单次探测允许的最大耗时,超时即视为失败。建议 2~5 秒。 |
| failureThreshold | 3 | 连续失败多少次才触发容器重启。总容忍时间为:(timeoutSeconds + periodSeconds) × (failureThreshold - 1) + timeoutSeconds,简化估算:≈ periodSeconds × failureThreshold |
| successThreshold | 1 | livenessProbe 必须为 1,即一次成功就算恢复(不能设为 >1)。 |
⚠️ 注意:
- liveness 接口必须轻量、只读、无外部依赖
- 避免在 liveness 中检查数据库、Redis、下游服务
- 合理设置failureThreshold:太小,网络抖动导致误杀;太大,故障恢复慢。
- 配合日志和监控
🔹 只关心"我还能不能活",不关心"我能不能干活"。
🔹 失败 = 重启,所以必须谨慎设计失败条件。
🔹 它是最后的安全网,不是日常健康检查。
2. readinessProbe(就绪探针)
yaml
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5 # 容器启动后延迟多少秒开始探测
periodSeconds: 10 # 探测间隔(秒)
timeoutSeconds: 3 # 单次探测超时时间
failureThreshold: 3 # 连续失败多少次才判定为"未就绪"
successThreshold: 1 # 连续成功多少次才算"就绪"(可 >1)
参数详解:
| 参数 | 默认值 | 说明 |
|---|---|---|
| initialDelaySeconds | 0 | 启动后等待多久开始探测。对慢启动应用很重要(但更推荐配合 startupProbe)。 |
| periodSeconds | 10 | 每隔多少秒探测一次。建议 5~15 秒。 |
| timeoutSeconds | 1 | 单次探测最大耗时,超时即失败。建议 2~5 秒。 |
| failureThreshold | 3 | 连续失败多少次才将 Pod 标记为 NotReady。 |
| successThreshold | 1 | 可设为 >1(如 2),用于防止因短暂抖动导致频繁切换 Ready 状态。 |
场景 :数据库暂时不可用 → Pod 不接收流量
| 时间 | 事件 |
|---|---|
| T=0s | Pod 启动 |
| T=5s | /ready 探测 → DB 连接失败 → 503 ❌(第1次失败) |
| T=15s | 第二次失败 ❌ |
| T=25s | 第三次失败 ❌ → 达到 failureThreshold=3 |
| T=25s+ | Pod 状态为 Running but NotReady |
| T=30s | Service Endpoints 不包含该 Pod → 零流量打入 ✅ |
| T=60s | DB 恢复 |
| T=65s | /ready 首次成功 ✅(第1次) |
| T=75s | 第二次成功 ✅ → Pod 变为 Ready |
| T=76s | 流量自动恢复 ✅ |
⚠️ 注意:
- 检查所有关键外部依赖:DB、Redis、Config Server、认证服务等。
- 使用短连接或连接池 ping:避免长期占用连接。
- 设置合理的 successThreshold(如 2):防止因网络抖动频繁进出 Ready 状态。
- 配合 preStop hook 实现优雅下线:
yaml
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"] # 先 sleep,同时让 readiness 失败
- 监控 NotReady Pod 数量:通过 Prometheus 报警。
🔹 "能跑 ≠ 能干" ------ Running 不等于 Ready。
🔹 流量开关由 readiness 控制,不是由容器是否启动决定。
🔹 它是服务网格、滚动更新、弹性伸缩的基石。
3. startupProbe(启动探针)
yaml
startupProbe:
httpGet:
path: /startup
port: 8080
initialDelaySeconds: 0 # 容器启动后多久开始第一次探测(秒)
periodSeconds: 5 # 探测间隔(秒)
timeoutSeconds: 2 # 单次探测超时时间(秒)
failureThreshold: 10 # 允许连续失败的最大次数
参数详解:
| 参数 | 默认值 | 说明 |
|---|---|---|
| initialDelaySeconds | 0 | 容器启动后等待多少秒才开始第一次探测。对于启动极慢的应用可设为 5~10 秒。 |
| periodSeconds | 10 | 每隔多少秒探测一次。建议设为 3~10 秒。 |
| timeoutSeconds | 1 | 单次探测允许的最大耗时,超时即视为失败。 |
| failureThreshold | 3 | 最关键参数! 表示最多允许连续失败多少次。总容忍时间为:(initialDelaySeconds) + (periodSeconds × failureThreshold) |
只要在这 60 秒内有一次探测成功,startupProbe 就算通过,之后 livenessProbe 和 readinessProbe才会开始工作。
如果 startupProbe 在其最大容忍时间(即 initialDelaySeconds + periodSeconds × failureThreshold)后仍然探测失败,Kubernetes 会认为容器"启动失败",并按照 Pod 的重启策略(restartPolicy)进行处理------通常是杀死容器并重新创建(重启)
⚠️ 注意:startup 接口必须轻量:只检查"是否初始化完成",不要连数据库。
合理设置容忍时间(留足余量),宁可多给,不要少给
所有启动时间 > 30 秒的应用都应配置 startupProbe。
不要在 startupProbe 中做 readiness 的事(如检查 DB),那是 readinessProbe 的职责。
监控 startupProbe 失败事件:可通过 Prometheus 抓取kube_pod_container_status_waiting_reason{reason="CrashLoopBackOff"} 或事件日志。
🔹 startupProbe 成功前,liveness 和 readiness 不生效。
🔹 startup 接口只关心"我启好了没",不关心"我能干活吗"。
🔹 宁可多给几秒,也不要让 Pod 死在黎明前。
三、探针接口设计最佳实践
下面从 三种探针的特性出发,系统性地阐述 健康检查接口的最佳实践设计方法。
1. 探针目标与接口职责对照表
| 探针类型 | 核心目标 | 健康检查接口应验证的内容 | 不应包含的内容 |
|---|---|---|---|
| startupProbe | 判断容器是否已完成启动 | 应用主进程已启动、初始化逻辑(如加载模型、配置)完成 | 外部依赖(DB、缓存)连通性 |
| readinessProbe | 判断是否可以接收流量 | 所有外部依赖就绪(DB、Redis、API 网关等) | 应用内部状态(如内存泄漏、死锁) |
| livenessProbe | 判断是否需要重启容器 | 应用是否处于可恢复的运行状态(无死锁、未崩溃) | 外部依赖失败(不应因 DB 挂了重启) |
2. 每种探针对应接口的设计最佳实践
Liveness Probe 接口(/live 或 /health/live)
✅ 应该做:
仅检查应用自身是否处于可运行状态:主线程未阻塞、内存未 OOM、无死锁
最好是一个纯内存操作,不涉及 I/O。
即使外部依赖(如 DB)宕机,只要应用本身还能运行(比如有重试队列),就应返回成功。
Liveness 只对不可恢复错误返回失败(如死锁)。
❌ 不应该做:连接数据库、调用外部 API。
因外部服务不可用而返回失败(这会导致不必要的重启)。
示例(最简形式):
python
# 检查内部状态标志(如后台线程是否存活):
if not background_worker.is_alive():
return jsonify({"error": "worker dead"}), 500
⚠️ 极简即可!甚至可以直接返回 200,因为如果进程挂了,根本不会响应。
Readiness Probe 接口(/ready 或 /health/ready)
✅ 应该做:
验证所有关键外部依赖是否可用:数据库连接池是否能获取连接、Redis 是否可 ping、下游微服务是否可达(谨慎使用)
返回 200 仅当所有依赖就绪,可安全接收流量。
Readiness 对暂时不可用返回失败(如 DB 连接池满)。
/ready 返回 200 但 body 是 {"ok": false},K8s 仍认为就绪(只看状态码)
❌ 不应该做:检查非关键依赖(如日志服务、监控上报)。
执行写操作或修改状态。
因单点依赖失败导致整个应用不可用(可考虑降级逻辑)。
示例(最简形式):
python
@app.route('/ready')
def readiness_check():
try:
# 检查数据库
db.session.execute(text("SELECT 1"))
# 检查 Redis
redis_client.ping()
return jsonify({"status": "ready"}), 200
except Exception as e:
app.logger.warning(f"Readiness check failed: {e}")
return jsonify({"error": "dependencies not ready"}), 503
⚠️ 注意:避免在 readiness 中做"全链路健康检查",否则容易引发级联故障。
Startup Probe 接口(/startup 或 /started)
✅ 应该做:
检查应用主进程是否已完全初始化(例如:模型加载完毕、配置解析完成)。
返回成功仅当内部初始化逻辑完成。
轻量、快速、无外部依赖。
❌ 不应该做:连接数据库、调用外部服务。
执行耗时操作(如全表扫描)。
示例(最简形式):
python
@app.route('/startup')
def startup_check():
if app.config.get('INIT_DONE'):
return jsonify({"status": "started"}), 200
else:
return jsonify({"error": "still initializing"}), 503
💡 提示:可在 init 或 before_first_request 中设置 INIT_DONE = True。
反面案例 vs 正确做法
| 场景 | 错误做法 | 正确做法 |
|---|---|---|
| 数据库挂了 | liveness 返回 500 → 容器被反复重启 | liveness 仍返回 200;readiness 返回 503 → 流量切断,但容器保留 |
| 应用启动慢(30s) | 无 startupProbe,liveness 在 10s 超时杀死容器 | 配置 startupProbe,允许最多 60s 启动 |
| 缓存不可用 | readiness 检查缓存失败 → 整个服务不可用 | 若缓存非关键,readiness 忽略;或实现降级逻辑 |