健康检查与生命周期
概述#
Pod 从创建到终止是一个完整的生命周期:启动→运行→停止。在这个过程中,Kubernetes 提供了三类探针(Probe)来检查容器状态,以及 preStop Hook 和 terminationGracePeriodSeconds 来控制优雅退出。这些看似简单的配置,在生产环境中是避免"服务不健康却被分配流量""程序没启完就被杀掉""进程被强制 kill 丢数据"等问题的最基础保障。
Liveness Probe:容器该不该重启#
核心概念#
Liveness Probe 回答一个问题:容器里的进程还活着吗? 如果探针失败,kubelet 会杀死容器并根据 restartPolicy 决定是否重启。
它的职责是检测"死锁/卡死/不可恢复"的状态——比如 Java 应用 OOM 后线程僵死、进程还在但不再响应请求。
三种检测方式#
| 类型 | 原理 | 适用场景 |
|---|---|---|
| HTTP | 发送 HTTP GET 请求,2xx/3xx 才算成功 | Web 服务最常用 |
| TCP | 尝试 TCP 连接指定端口 | 数据库、gRPC 等非 HTTP 服务 |
| Exec | 在容器内执行命令,返回 0 才算成功 | 需要自定义健康逻辑的场景 |
HTTP 探针示例#
livenessProbe:
httpGet:
path: /healthz # 健康检查路径
port: 8080 # 端口
httpHeaders: # 可选:自定义 Header
- name: X-Check-Type
value: liveness
initialDelaySeconds: 30 # 容器启动后等 30 秒再开始检测
periodSeconds: 10 # 每 10 秒检测一次
timeoutSeconds: 5 # 每次检测的超时时间
failureThreshold: 3 # 连续失败 3 次才判定为失败
successThreshold: 1 # 成功 1 次即认为恢复TCP 探针示例#
livenessProbe:
tcpSocket:
port: 6379 # 检测 Redis 端口是否可连接
initialDelaySeconds: 15
periodSeconds: 10
failureThreshold: 3Exec 探针示例#
livenessProbe:
exec:
command:
- /bin/sh
- -c
- |
# 自定义检查逻辑:检查进程是否存在 + 检查某个文件状态
pgrep nginx && [ -f /tmp/healthy ]
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3常见坑点#
探针太激进是新手最常见的错误。如果 initialDelaySeconds 太短,应用还没启动完成就开始检测,会导致容器被反复误杀。
Liveness 不检查外部依赖——只检查容器自身状态,不要在 Liveness 里连数据库。如果数据库挂了,Liveness 把 Pod 杀了也解决不了问题,反而可能引发重启风暴。
Readiness Probe:是否接流量#
与 Liveness 的本质区别#
| 维度 | Liveness Probe | Readiness Probe |
|---|---|---|
| 回答的问题 | 容器还活着吗? | 容器能处理请求吗? |
| 失败后果 | 杀死容器并重启 | 从 Service Endpoints 中摘除,不再接收流量 |
| 典型场景 | 应用死锁、OOM 僵死 | 应用启动中、临时过载、依赖未准备好 |
| 恢复后 | 自行恢复或需要重启 | 重新加入 Endpoints,恢复正常流量 |
一句话总结:Liveness 决定"该不该杀",Readiness 决定"该不该给流量"。
配置示例#
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5 # 启动后 5 秒开始检测(比 Liveness 短,尽早接流量)
periodSeconds: 5 # 每 5 秒检测一次
timeoutSeconds: 3
failureThreshold: 3 # 连续失败 3 次摘除
successThreshold: 1 # 成功 1 次即可加入Readiness 的工作流程#
Pod 启动 → Readiness Probe 检测
├─ 失败 → Pod 被从 Service Endpoints 移除,不接用户流量
└─ 成功 → Pod 加入 Endpoints,开始接收流量
运行时 → Readiness 周期性检测
├─ 失败 → 再次从 Endpoints 移除
└─ 成功 → 重新加入 Endpoints典型场景#
- 应用启动后需要连接数据库、预热缓存——Readiness 在这些操作完成后再返回成功
- 临时过载时需要停止新流量——应用主动让 /ready 返回 503,Kubernetes 自动摘除
Startup Probe:慢启动应用的救星#
解决的问题#
想象一个 Java 应用需要 90 秒才能完全启动。如果按照 Liveness 的常规配置 initialDelaySeconds: 30, periodSeconds: 10, failureThreshold: 3:
0s - 容器启动
30s - 第一次 Liveness 检测 → 还没起完 → 失败
40s - 第二次 → 失败
50s - 第三次 → 失败 → 连续 3 次失败 → 容器被杀!明明再等 40 秒就启动完了,但容器被 Liveness 误杀了。
Startup Probe 的工作方式#
Startup Probe 和 Liveness/Readiness 是串行关系:
容器启动
→ Startup Probe 检测(Liveness/Readiness 休眠)
→ Startup 成功后
→ Liveness 和 Readiness 接管如果 Startup 失败了,容器会被重启(类似 Liveness)。但它给慢启动应用留出了充足的时间。
配置示例#
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30 # 最多允许失败 30 次
periodSeconds: 10 # 每 10 秒检测一次
# total=30×10=300秒,给应用 5 分钟启动时间
livenessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 10
failureThreshold: 3 # Startup 成功后,Liveness 恢复正常灵敏度
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5
failureThreshold: 3关键是 Startup failureThreshold × periodSeconds = 你给应用的最长启动时间窗口。一旦启动成功,Liveness 就不用设那么长的 initialDelaySeconds。
没有 Startup Probe 时怎么办#
如果没有 Startup Probe(Kubernetes 1.16 以前),只能把 Liveness 的 initialDelaySeconds 设得很大。但这意味着即使应用真正卡死后也要等很久才会被杀。Startup Probe 解决了这个矛盾。
preStop Hook:优雅退出#
核心概念#
preStop Hook 是容器停止前执行的最后一段命令。当 Pod 被删除时,流程如下:
1. Pod 进入 Terminating 状态(宽限期计时从这里开始)
2. preStop Hook 执行(如果配置了)
3. preStop 结束后,向容器主进程发送 SIGTERM
4. 直到 terminationGracePeriodSeconds(默认 30 秒,从第 1 步起算,preStop 也占用这段时间)耗尽
5. 如果还没退出,发送 SIGKILL 强制结束典型用法:发送 deregister 信号#
微服务架构中,一个服务实例下线前需要从注册中心摘除自己,并等待正在处理的请求完成:
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- |
# 1. 发送 deregister 信号到服务注册中心
curl -X POST http://consul:8500/v1/agent/service/deregister/my-service-${HOSTNAME}
# 2. 等待流量排空(给负载均衡器刷新时间)
sleep 15为什么需要 sleep?#
大多数负载均衡器(如 Ingress Controller、Service Endpoints)不是实时的——从 Pod 被从 Endpoints 移除到不再转发流量,有一个时间差。sleep 15 就是为这段时间留出缓冲,防止 Pod 在连接还没关闭时就被杀掉。
其他典型用法#
- 数据库连接池优雅关闭
- 消息队列 consumer 停止消费并处理完当前消息
- 文件/缓冲区 flush 到存储
terminationGracePeriodSeconds:宽限时间窗口#
与 preStop 的配合关系#
terminationGracePeriodSeconds 定义的是从 Pod 进入 Terminating 状态到被强制 SIGKILL 的总时长。preStop Hook 的执行时间包含在这个窗口内。
terminationGracePeriodSeconds = 60秒
时间线:
0s - Pod Terminating,preStop 开始执行
15s - preStop 完成(包含 sleep 15s)
15s - 主进程收到 SIGTERM
60s - 主进程还没退出 → SIGKILL 强制结束如果 preStop 执行了 70 秒(超过了 60 秒的宽限),在 60 秒时 preStop 会被 SIGKILL,然后主进程也会被 SIGKILL。
配置示例#
spec:
terminationGracePeriodSeconds: 60 # 给应用 60 秒优雅退出时间
containers:
- name: app
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- |
curl -X POST http://consul:8500/v1/agent/service/deregister/app-${HOSTNAME}
sleep 15值怎么定#
| 应用类型 | 建议值 | 原因 |
|---|---|---|
| 无状态 API | 30-60s | 处理在途 HTTP 请求 + LB 刷新 |
| 消息消费者 | 60-120s | 需要时间处理完正在消费的消息 |
| 长时间任务 | 根据任务最长执行时间 | 不希望任务被 SIGKILL 打断 |
| 数据库 | 120-300s | 需要 flush 缓冲区、关闭连接 |
三探针 + preStop 全配置示例#
apiVersion: v1
kind: Pod
metadata:
name: app-full-health
spec:
terminationGracePeriodSeconds: 60
containers:
- name: app
image: my-app:1.0
ports:
- containerPort: 8080
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
# 启动探针:给慢启动留时间
startupProbe:
httpGet:
path: /healthz
port: 8080
failureThreshold: 30
periodSeconds: 10
# 就绪探针:控制流量接入
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5
failureThreshold: 3
# 存活探针:检测死锁
livenessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 10
failureThreshold: 5
# 优雅退出
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- |
/app/deregister.sh && sleep 15实战要点#
- 先有 Readiness,再有 Liveness:Readiness 是所有生产服务的必备项,Liveness 只在确实需要检测死锁时才加。
- Liveness 不要连外部依赖:连数据库失败的应该是 Readiness 管,不是 Liveness 管。Liveness 只检查自身进程状态。
- 慢启动应用必须配 Startup Probe:Java、Node.js 等启动慢的应用,没有 Startup Probe 迟早被 Liveness 误杀。
- preStop 配合 terminationGracePeriodSeconds:只配 preStop 不调宽限时间,如果 preStop 执行太久会被 SIGKILL 中断。
- 探针参数要保守:
failureThreshold设大一点(3-5 次),periodSeconds不要太小(5-10 秒),避免网络抖动导致误判。
小结#
三类探针各有分工:Startup 给慢启动应用缓冲,Liveness 检测死锁决定是否重启,Readiness 控制流量开关。preStop Hook 和 terminationGracePeriodSeconds 共同构成优雅退出机制——preStop 做清理和 deregister,宽限时间确保一切操作来得及完成。这些配置加在一起,就是让你的 Pod "该活的时候活着,该死的时候优雅地死"。