Spring Boot on K8s 优雅停机流程解析
相关文章
在 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: 1024Mi1. 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 应用行为 |
| 0 | Pod 终止流程开始 | Kubernetes 接收到删除 Pod 的请求,开始终止流程。 立即停止所有探针( livenessProbe 和 readinessProbe)。 | - |
| 0 | preStop 钩子执行 | 执行 preStop 钩子(如 curl -X POST /actuator/shutdown)。 | 接收到 /actuator/shutdown 请求,开始优雅关闭流程。 |
| 0-T | preStop 钩子延迟 | 等待 preStop 钩子完成(如 sleep 5)。 | 停止接收新请求,准备关闭内部资源。 |
| T | 发送 SIGTERM 信号 | preStop 钩子执行完成后,Kubernetes 向容器发送 SIGTERM 信号。 | 接收到 SIGTERM 信号,继续处理未完成请求并释放资源。 |
| T-25 | terminationGracePeriodSeconds 倒计时 | 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内正常退出。