1. 引言

在本教程中,我们将深入探讨 Kubernetes探针机制(Probes),并演示如何利用 Spring Boot Actuator 提供的 HealthIndicator 来更准确地反映应用的真实运行状态。

为了便于理解,我们假设读者已经具备一定的 Spring Boot ActuatorKubernetesDocker 使用经验。

2. Kubernetes 探针机制

Kubernetes 定义了两种不同的探针类型用于定期检查应用是否正常工作:存活探针(Liveness)就绪探针(Readiness)

2.1. Liveness 与 Readiness 探针的区别

通过 LivenessReadiness 探针,Kubelet 能够在检测到异常时及时做出响应,从而减少应用的停机时间。

虽然两者配置方式类似,但它们的语义不同,Kubelet 根据触发的探针类型执行不同的操作:

  • Readiness:验证 Pod 是否已准备好接收流量。只有当所有容器都处于就绪状态时,该 Pod 才会被标记为就绪。
  • Liveness:检查 Pod 是否需要重启。如果应用正在运行但处于无法继续处理请求的状态(例如死锁),则会触发重启。

我们可以在容器级别配置这两种探针:

apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: k8s.gcr.io/goproxy:0.1
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 10
      timeoutSeconds: 2
      failureThreshold: 1
      successThreshold: 1
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20
      timeoutSeconds: 2
      failureThreshold: 1
      successThreshold: 1

我们可以对探针进行更精细的控制,以下是几个关键字段说明:

  • initialDelaySeconds:容器启动后等待 n 秒再开始探测。
  • periodSeconds:探测间隔,默认为 10 秒,最小值为 1 秒。
  • timeoutSeconds:探测超时时间,默认为 1 秒,最小值也为 1 秒。
  • failureThreshold:失败重试次数。对于 Readiness,失败后 Pod 会被标记为未就绪;对于 Liveness,失败后 Pod 会被重启。默认为 3 次,最小为 1。
  • successThreshold:连续成功次数阈值,只有在之前失败的情况下才生效。默认为 1,最小也为 1。

在这个例子中,我们选择了 TCP 探针,但 Kubernetes 还支持其他类型的探针。

2.2. 探针类型详解

根据具体场景选择合适的探针类型非常重要。比如,如果容器是一个 Web 服务器,使用 HTTP 探针通常比 TCP 更可靠。

Kubernetes 支持以下三种探针类型:

  • exec:在容器内执行 bash 命令。例如检查某个文件是否存在。若返回非零状态码,则探针失败。
  • tcpSocket:尝试与指定端口建立 TCP 连接。连接失败则探针失败。
  • httpGet:向容器中的服务发送 HTTP GET 请求。响应码在 200~399 范围内表示成功。

⚠️ 对于 HTTP 探针,除了上述通用字段外,还支持以下额外字段:

  • host:目标主机名,默认为 Pod IP。
  • scheme:协议类型,HTTP 或 HTTPS,默认为 HTTP。
  • path:访问路径。
  • httpHeaders:自定义请求头。
  • port:端口号或端口名称。

3. Spring Actuator 与 Kubernetes 自愈能力结合

了解了 Kubernetes 如何检测应用状态后,我们来看看如何借助 Spring Boot Actuator 来监控应用及其依赖组件的健康状况。

以下示例基于 Minikube 环境运行。

3.1. Actuator 与 HealthIndicator

Spring 提供了大量开箱即用的 HealthIndicator 实现,能够反映应用依赖组件的健康状态。我们只需添加 Actuator 依赖即可:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

3.2. Liveness 探针实战

我们构建一个应用,它会正常启动,但在 30 秒后进入异常状态

我们通过创建一个 HealthIndicator 来模拟异常状态,它会检查一个布尔变量是否为 true。初始值设为 true,然后安排一个定时任务在 30 秒后将其改为 false

@Component
public class CustomHealthIndicator implements HealthIndicator {

    private boolean isHealthy = true;

    public CustomHealthIndicator() {
        ScheduledExecutorService scheduled =
          Executors.newSingleThreadScheduledExecutor();
        scheduled.schedule(() -> {
            isHealthy = false;
        }, 30, TimeUnit.SECONDS);
    }

    @Override
    public Health health() {
        return isHealthy ? Health.up().build() : Health.down().build();
    }
}

接着,我们将应用打包为 Docker 镜像:

FROM openjdk:8-jdk-alpine
RUN mkdir -p /usr/opt/service
COPY target/*.jar /usr/opt/service/service.jar
EXPOSE 8080
ENTRYPOINT exec java -jar /usr/opt/service/service.jar

然后编写 Kubernetes 部署模板:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: liveness-example
spec:
  ...
    spec:
      containers:
      - name: liveness-example
        image: dbdock/liveness-example:1.0.0
        ...
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 10
          timeoutSeconds: 2
          periodSeconds: 3
          failureThreshold: 1
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 20
          timeoutSeconds: 2
          periodSeconds: 8
          failureThreshold: 1

这里我们使用了指向 Actuator /health 接口的 HTTP GET 探针。任何应用状态变化都会被探针捕捉并反映到 Pod 的健康检查中。

部署后,大约 30 秒后,Pod 会被标记为未就绪并从流量中移除;稍后,Kubelet 会重启该 Pod。

我们可以通过如下命令查看 Pod 事件:

kubectl describe pod liveness-example

输出如下:

Warning  Unhealthy 3s (x2 over 7s)   kubelet, minikube  Readiness probe failed: HTTP probe failed ...
Warning  Unhealthy 1s                kubelet, minikube  Liveness probe failed: HTTP probe failed ...
Normal   Killing   0s                kubelet, minikube  Killing container with id ...

3.3. Readiness 探针实战

在上一个例子中,我们展示了如何通过 HealthIndicator 反映应用状态。现在我们来看另一个典型场景:

假设应用启动后需要一些时间来加载数据或完成初始化,此时并不适合立即接收流量。

这是一个典型的 Readiness 探针 使用场景。

我们对之前的 HealthIndicator 进行改造:

@Component
public class CustomHealthIndicator implements HealthIndicator {

    private boolean isHealthy = false;

    public CustomHealthIndicator() {
        ScheduledExecutorService scheduled =
          Executors.newSingleThreadScheduledExecutor();
        scheduled.schedule(() -> {
            isHealthy = true;
        }, 40, TimeUnit.SECONDS);
    }

    @Override
    public Health health() {
        return isHealthy ? Health.up().build() : Health.down().build();
    }
}

初始值为 false,40 秒后变为 true

接着,我们使用如下 Kubernetes 模板部署应用:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: readiness-example
spec:
  ...
    spec:
      containers:
      - name: readiness-example
        image: dbdock/readiness-example:1.0.0
        ...
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 40
          timeoutSeconds: 2
          periodSeconds: 3
          failureThreshold: 2
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 100
          timeoutSeconds: 2
          periodSeconds: 8
          failureThreshold: 1

配置要点如下:

  • 因为应用需要大约 40 秒准备就绪,我们将 Readiness 探针的 initialDelaySeconds 设为 40 秒
  • 同样地,我们将 Liveness 探针的 initialDelaySeconds 设为 100 秒,防止在应用未就绪时被误杀。

这样,即使初始化未完成,应用也有大约 60 秒的时间完成准备。如果仍未完成,Liveness 探针会触发重启。

4. 总结

本文介绍了 Kubernetes 的探针机制,并展示了如何结合 Spring Boot Actuator 提升应用的健康监控能力。

完整示例代码可在 GitHub 仓库 获取。


原始标题:Self-Healing Applications with Kubernetes and Spring Boot