Kubernetes 中的 Liveness、Readiness 和 Startup Probe
1. 概览
当在 Kubernetes 上运行应用程序时,集群无法自动知道你的 pod 是否“活着”或“已准备好接收流量”——它必须通过 probe 来询问。本文解释了 liveness、readiness 和 startup probe 之间的区别,并附带 Spring Boot Actuator 和 Deployment YAML 的配置示例。读完本文,你将能够为真实的服务(如 OrderService)正确配置 probe,而不会让 pod 陷入无谓的重启循环。
2. 为什么需要 Probe
默认情况下,Kubernetes 只知道容器内的进程是否还在运行(容器状态 Running)。它不知道内部应用程序是否陷入死锁、丧失数据库连接,或者尚未完成启动。
如果没有 probe,后果是:
- Pod 被挂起(死锁),但仍然接收流量 → 请求大量超时。
- Pod 刚启动,尚未完成数据库连接,却已被 Service 添加到端点列表 → 返回 500 错误。
三种 probe 解决三个不同的问题:
| Probe | 回答问题 | 失败时 |
|---|---|---|
| Liveness | 容器还活着吗? | Kubernetes 重启容器 |
| Readiness | 容器已准备好接收流量吗? | Kubernetes 从 Service 端点移除 pod(不重启) |
| Startup | 容器已完成启动吗? | Kubernetes 等待,暂时禁用 liveness/readiness |
3. Liveness Probe
Liveness probe 回答:“这个进程是否处于无法自我恢复的状态?”。如果 probe 失败超过 failureThreshold 次,kubelet 杀死并重启容器。
3.1. 添加 Spring Boot Actuator
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management.endpoint.health.probes.enabled=true
management.endpoints.web.exposure.include=health,info
management.endpoint.health.probes.enabled=true 这一行启用了两个子端点:/actuator/health/liveness 和 /actuator/health/readiness,符合 Kubernetes 的预期标准。
3.2. 在 Deployment 中声明
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
template:
spec:
containers:
- name: order-service
image: nonladev/order-service:1.0
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 20
periodSeconds: 10
failureThreshold: 3
initialDelaySeconds: 20:容器启动后等待 20 秒才开始 probe。periodSeconds: 10:每 10 秒调用一次 probe。failureThreshold: 3:连续失败 3 次才认为已死亡 → 重启。
注意: Liveness probe 应该只检查“应用程序是否仍在运行”,不应该检查外部依赖项(数据库、Redis 等)。如果数据库宕机导致 liveness 失败,Kubernetes 会持续重启 pod 但无法解决任何问题——这是一个非常常见的错误。
4. Readiness Probe
Readiness probe 回答:“pod 已准备好处理请求吗?”。失败时,pod 从 Service 的端点中移除——流量不再被发送到它,但容器不会被重启。
4.1. 自定义 Readiness Indicator
假设 OrderService 需要连接到 PaymentGateway 后才能准备就绪:
@Component
public class PaymentGatewayReadinessIndicator implements HealthIndicator {
private final PaymentGatewayClient paymentGatewayClient;
public PaymentGatewayReadinessIndicator(PaymentGatewayClient paymentGatewayClient) {
this.paymentGatewayClient = paymentGatewayClient;
}
@Override
public Health health() {
if (paymentGatewayClient.isConnected()) {
return Health.up().withDetail("gateway", "connected").build();
}
return Health.down().withDetail("gateway", "unreachable").build();
}
}
要将此 indicator 包含在 readiness 组中(而不影响 liveness),请声明 group:
management.endpoint.health.group.readiness.include=readinessState,paymentGateway
health()当PaymentGatewayClient未连接时返回Health.down()— readiness probe 会失败,pod 在网关准备就绪之前不会接收流量。- Spring 自动将 bean 名称
paymentGatewayReadinessIndicator映射为健康响应中的键paymentGateway。
4.2. Indicator 的测试
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
class PaymentGatewayReadinessIndicatorTest {
@Test
void shouldReportDownWhenGatewayNotConnected() {
PaymentGatewayClient client = mock(PaymentGatewayClient.class);
when(client.isConnected()).thenReturn(false);
PaymentGatewayReadinessIndicator indicator = new PaymentGatewayReadinessIndicator(client);
Health health = indicator.health();
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
assertThat(health.getDetails()).containsEntry("gateway", "unreachable");
}
}
mock(PaymentGatewayClient.class)模拟外部依赖项,无需真实连接。assertThat(health.getStatus())确认当网关未连接时状态为DOWN— 这是 readiness 的预期行为。
4.3. 在 Deployment 中声明 Readiness
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
successThreshold: 1:只需 1 次 probe 成功即认为再次就绪(与 liveness 不同,通常无需设置此字段,因为默认值已是 1)。periodSeconds: 5比 liveness 更短,因为 readiness 需要快速响应依赖项状态变化。
5. Startup Probe
对于启动缓慢的应用程序(加载大型缓存、运行数据库迁移),如果只使用带有固定 initialDelaySeconds 的 liveness,你必须猜测一个足够大的数字——容易出错,且浪费随后每次重启的等待时间。
Startup probe 解决了这个问题:在 startup probe 通过之前,Kubernetes 暂时禁用 liveness 和 readiness probe,避免在应用程序合法启动时被无谓地杀死。
startupProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
failureThreshold: 30
periodSeconds: 10
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 10
failureThreshold: 3
startupProbe使用failureThreshold: 30和periodSeconds: 10→ 允许应用程序最多启动 300 秒(30 × 10 秒)才认为失败。startupProbe首次通过后,Kubernetes 切换到使用阈值较低的livenessProbe(3 次 × 10 秒 = 30 秒)进行监控——在稳定运行期间快速响应真实故障。
注意: 如果你的应用程序启动快速(几秒以内,如没有加载大型缓存的简单 REST API),无需声明
startupProbe— 添加它只会使 YAML 更长,而没有明显的好处。
6. 在单个 Deployment 中组合三个 Probe
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: nonladev/order-service:1.0
ports:
- containerPort: 8080
startupProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
failureThreshold: 30
periodSeconds: 10
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
periodSeconds: 5
failureThreshold: 3
创建新 pod 时的工作流程:
- 容器启动,Kubernetes 立即开始调用
startupProbe(没有initialDelaySeconds,因为 startup 自己处理等待)。 - 此时
livenessProbe和readinessProbe尚未运行。 - 当
startupProbe首次返回 HTTP 200 时,Kubernetes 完全停止 startup probe,开始并行运行 liveness 和 readiness。 - Readiness 失败 → pod 从 Service 中移除,流量不再流向它,但容器仍然存活。
- Liveness 失败 → 容器被杀死,Kubernetes 创建新容器(同一个 pod),流程从第 1 步重新开始。
使用 kubectl describe pod 检查 probe 的实际状态:
kubectl describe pod order-service-7d9f8c6b5-xk2pq
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Events:
Type Reason Age From Message
Warning Unhealthy 2m kubelet Readiness probe failed: HTTP probe failed with statuscode: 503
Readiness probe failed 这一行表示 readiness 正在失败 — 检查应用程序日志或直接调用 /actuator/health/readiness 以查看具体哪个部分 DOWN。
7. 常见错误
-
对 liveness 和 readiness 使用同一个端点:当外部依赖项(数据库、缓存)宕机时,两个 probe 同时失败 → pod 持续重启(crash loop),尽管主应用程序仍然健康。应该清楚地分离
/actuator/health/liveness(仅检查内部)和/actuator/health/readiness(检查依赖项)。 -
对启动缓慢的应用程序设置
initialDelaySeconds过小:liveness probe 在应用程序加载时立即失败,导致无限重启循环。解决方案:使用startupProbe而不是随意增加initialDelaySeconds。 -
periodSeconds×failureThreshold过大:pod 出现真实错误但需要很多分钟才能被发现,影响 SLA。 -
Readiness probe 调用不重要的下游服务:例如调用日志服务,如果该服务宕机,整个
OrderService会被错误地认为 not-ready,尽管主要功能仍在正常工作。
8. 结论
三种 probe 服务三个不同的目的:startupProbe 在启动缓慢阶段保护应用程序,livenessProbe 检测死锁以便 Kubernetes 自动重启,readinessProbe 基于依赖项的实际状态来调度流量。最重要的原则是:不要让 liveness probe 依赖于外部系统——这样会将下游故障转变为你自己服务的 crash loop。正确配置这三个 probe 可以帮助集群平稳地自我修复,大大减少部署或基础设施故障期间的停机时间。