原力注入

Spring Boot on K8s 优雅停机流程解析

相关文章

容器技术回顾 - 什么是优雅关闭以及如何实现

一文学会 Kubernetes Pod 的生命周期管理

三张图了解 Pod 的生命周期(初始化、运行与终止)

Image

Image

在 Kubernetes 环境中,Pod 的优雅停止是保障服务平滑下线、避免请求丢失和数据不一致的关键环节。结合 Spring Boot 提供的 server.shutdown=graceful 特性,可以进一步优化应用的停止流程,确保资源清理和事务处理的完整性。

本文将通过一个完整的时间轴,详细解析 Kubernetes 和 Spring Boot 在 Pod 停止时的交互过程,并附上具体配置示例,帮助开发者实现优雅的停止机制。

1. 背景与问题

在微服务架构中,服务实例可能因滚动更新、扩缩容或手动操作而被终止。如果没有优雅停止机制,可能会导致以下问题:

  • • 未完成的请求被中断。

  • • 正在进行的事务或任务(如数据库写入、消息队列消费)丢失或失败。

  • • 资源(如线程池、文件句柄)未正确释放。

为了解决这些问题,Kubernetes 提供了 terminationGracePeriodSeconds 和 lifecycle 钩子,而 Spring Boot 则通过 server.shutdown=graceful 支持优雅关停。

2. 关键配置解析

以下是一个示例 Deployment 文件,展示了 Kubernetes 与 Spring Boot 的优雅停止配置。

Kubernetes Deployment 示例

apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
  labels:
    app: user-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
    spec:
      hostname: user-service
      terminationGracePeriodSeconds: 25
      containers:
        - name: user-service
          image: user-service:2025
          imagePullPolicy: IfNotPresent
          env:
            - name: EUREKA_URL
              value: http://eureka:8080/eureka
          ports:
            - containerPort: 9090
          startupProbe:
            httpGet:
              path: /actuator/health
              port: 8081
            initialDelaySeconds: 15
            periodSeconds: 5
            failureThreshold: 30
          readinessProbe:
            httpGet:
              path: /actuator/health
              port: 8081
            initialDelaySeconds: 0
            periodSeconds: 3
            failureThreshold: 2
          livenessProbe:
            httpGet:
              path: /actuator/health
              port: 8081
            initialDelaySeconds: 0
            periodSeconds: 3
            failureThreshold: 2
          lifecycle:
            preStop:
              exec:
                command:
                  [
                    "sh",
                    "-c",
                    "curl -X POST http://localhost:8081/actuator/shutdown",
                  ]
          resources:
            requests:
              cpu: 1
              mem)ory: 512Mi
            limits:
              cpu: 2
              memory: 1024Mi

1. terminationGracePeriodSeconds

terminationGracePeriodSeconds 是 Kubernetes 中用于控制 Pod 优雅关闭时间的关键参数。它定义了从 Kubernetes 发送 SIGTERM 信号到强制终止 Pod(发送 SIGKILL)之间的时间窗口。

  • • 作用:

    • • 为应用提供足够的时间完成未完成的请求、释放资源(如数据库连接、线程池等)以及执行清理任务。

    • • 如果应用在 terminationGracePeriodSeconds 内未完成关闭,Kubernetes 会强制终止 Pod。

  • • 建议:

    • • 根据应用的关闭逻辑合理设置该值。如果应用关闭耗时较长(如处理大量未完成请求或清理复杂资源),可以适当增加该值。

    • • 默认值为 30 秒,如果未显式设置,Kubernetes 会使用默认值。

2. lifecycle.preStop 钩子
preStop 钩子是 Kubernetes 提供的一种机制,允许在 Pod 终止前执行自定义命令或 HTTP 请求。它可以与 Spring Boot 的优雅关闭机制结合使用,确保应用在收到 SIGTERM 信号前完成必要的清理工作。

  • • 作用:

    • • 在 Pod 终止前触发 Spring Boot 的 /actuator/shutdown 端点,启动优雅关闭流程(如果应用可以自己捕捉 SIGTERM 信号,则可以不使用该方法)。

  • • 解析:

    • • curl -X POST http://localhost:8081/actuator/shutdown:向 Spring Boot 应用的 /actuator/shutdown 端点发送 POST 请求,触发优雅关闭。

  • • 建议:

    • • 确保 Spring Boot 应用中启用了 actuator,并开始了管理端点,以支持优雅关闭;

    • • 建议管理端口不要对外暴露或者增加认证,以避免产生安全隐患。

Spring Boot 配置

在 Spring Boot 的 application.yml 文件中,启用 graceful shutdown:

server:
  shutdown: graceful
spring:
  lifecycle:
    timeout-per-shutdown-phase: 20s
...
# 指定管理端口
management.server.port=8081

# 暴露 shutdown 端点
management.endpoints.web.exposure.include=shutdown,health,info

# 指定管理端点的路径
management.endpoints.web.base-path=/actuator

# 启用 shutdown 端点
management.endpoint.shutdown.enabled=true

1. server.shutdown

  • • 取值范围:immediate 或 graceful

  • • 含义:指定应用的关闭模式。

    • • immediate:应用在接收到停止信号后立即退出,不执行资源清理。

    • • graceful:应用在接收到停止信号后优雅关停,确保完成未处理的任务和资源释放。

  • • 推荐设置:graceful,以确保服务在停止时能够安全释放资源和完成事务。

2. spring.lifecycle.timeout-per-shutdown-phase

  • • 默认值:30 秒

  • • 含义:定义每个停机阶段的超时时间,适用于 Spring 生命周期中的停机回调(如 @PreDestroy 和 SmartLifecycle)。

  • • 单位:支持时间单位,例如 20s(秒)、1m(分钟)。

  • • 推荐设置:建议设置值略高于应用中耗时最长的资源清理任务,以避免任务在超时前中断。

3. 时间轴解析

结合 Kubernetes 的优雅停止机制和 Spring Boot 的行为,一个 Pod 停止的时间轴如下:

时间(秒)阶段Kubernetes 操作Spring Boot 应用行为
0Pod 终止流程开始Kubernetes 接收到删除 Pod 的请求,开始终止流程。
立即停止所有探针(livenessProbe 和 readinessProbe)。
-
0preStop 钩子执行执行 preStop 钩子(如 curl -X POST /actuator/shutdown)。接收到 /actuator/shutdown 请求,开始优雅关闭流程。
0-TpreStop 钩子延迟等待 preStop 钩子完成(如 sleep 5)。停止接收新请求,准备关闭内部资源。
T发送 SIGTERM 信号preStop 钩子执行完成后,Kubernetes 向容器发送 SIGTERM 信号。接收到 SIGTERM 信号,继续处理未完成请求并释放资源。
T-25terminationGracePeriodSeconds 倒计时Kubernetes 等待容器退出。处理未完成请求,释放资源(如数据库连接池、线程池等)。
≤25应用完成优雅停止,退出容器容器正常退出,Kubernetes 清理 Pod。完成资源释放,应用正常退出。
>25应用未退出,Kubernetes 强制终止如果容器在 terminationGracePeriodSeconds 内未退出,Kubernetes 发送 SIGKILL 信号。应用被强制终止,未完成任务丢失。

4. 注意事项与优化建议

配置优化

1. 合理设置 terminationGracePeriodSeconds

  • • 建议:

    • • 根据应用的关停逻辑耗时调整 terminationGracePeriodSeconds 的值。

    • • 如果应用需要处理大量未完成请求或清理复杂资源(如数据库连接、线程池等),建议适当增加该值。

  • • 目的:

    • • 避免未完成的任务因超时被 Kubernetes 强制终止,确保停机流程的完整性。

    • • 默认值为 30 秒,如果未显式设置,Kubernetes 会使用默认值。

2. 探针的初始延迟和失败次数

  • • 建议:

    • • 合理设置 readinessProbe 的 initialDelaySeconds 和 failureThreshold。

    • • 例如,initialDelaySeconds 应大于应用的启动时间(如果启用了 startupProbe,则可以将initialDelaySeconds 设为 0,由 startUp 来判断 Pod 是否启动完毕) ,failureThreshold 应根据应用的稳定性需求调整。

  • • 目的:

    • • 确保流量路由正常切换,避免在应用未完全准备好时接收请求或在停止时流量切换不及时。

    • • 通过 readinessProbe 的合理配置,Kubernetes 可以及时将 Pod 从服务端点中移除,避免新流量进入正在关闭的 Pod。

3. preStop 钩子的优化

  • • 建议:

    • • 在 preStop 钩子中调用 Spring Boot 的 /actuator/shutdown 端点(如果程序可以捕捉 SIGTERM 进行清理,则可以不需要配置 preStop 钩子;使用 preStop 钩子可以触发自定义清理流程)。

  • • 目的:

    • • 确保应用在收到 SIGTERM 信号前完成必要的清理工作,避免请求丢失或资源泄漏。

4. Spring Boot 优雅关闭超时配置

  • • 建议:

    • • 在 Spring Boot 中配置 server.shutdown=graceful 和 timeout-per-shutdown-phase。

    • • 确保 timeout-per-shutdown-phase 的值小于 Kubernetes 的 terminationGracePeriodSeconds。

  • • 目的:

    • • 避免 Spring Boot 的优雅关闭超时后,被 Kubernetes 强制终止。

测试与验证

1. 验证优雅停止

使用以下命令手动删除 Pod,验证优雅停止流程:

kubectl delete pod user-service-xxxx --grace-period=30
  • • 说明:

    • • --grace-period=30 表示 Kubernetes 会等待 30 秒后强制终止 Pod。

    • • 观察 Pod 的停止过程,确保应用在 terminationGracePeriodSeconds 内完成关闭。

2. 模拟滚动更新

通过更新 Deployment 的镜像版本,触发滚动更新,验证优雅停止流程:

kubectl set image deployment/user-service user-service=user-service:2026
  • • 说明:

    • • 观察旧 Pod 的停止过程,确保流量平滑切换到新 Pod。

日志观察

1. 服务端日志

通过观察服务端日志输出,检查停止流程是否符合预期:

  • • 确认 Spring Boot 应用是否接收到 /actuator/shutdown 请求。

  • • 检查未完成请求是否被正确处理,资源是否被正确释放。

2. Kubernetes 事件日志

使用以下命令查看 Pod 的事件日志,分析停止过程:

kubectl describe pod user-service-xxxx
  • • 说明:

    • • 检查 Pod 的状态变化,确认 preStop 钩子是否执行成功。

    • • 确认 Pod 是否在 terminationGracePeriodSeconds 内正常退出。