Spring Boot Docker优雅关机总结

1. 问题背景

在 Docker 环境中运行 Spring Boot 应用时,为了确保应用能够优雅关机(Graceful Shutdown),需要正确配置多个组件。错误的配置可能导致应用无法正常接收关闭信号,从而无法执行优雅关机流程。

今天才发现服务器上配置有问题没支持, 本地是没问题的。。。最后发现是配置有问题, 这里总结一下。

日志中出现INFO o.s.b.w.e.tomcat.GracefulShutdown - Commencing graceful shutdown. Waiting for active requests to complete表示成功(我使用的是 tomcat 容器, 其他的日志内容可能不太一样)。

2. Docker 关机流程

Docker 容器的关闭过程如下:

  1. Docker 向容器的 1 号进程(PID 1)发送 SIGTERM 信号
  2. 等待 stop_grace_period 时间(默认 10 秒)
  3. 如果进程还未退出,发送 SIGKILL 信号强制终止

3. 常见配置错误

3.1 Dockerfile ENTRYPOINT 错误写法

1
2
3
4
5
# ❌ 错误写法(shell 形式)
ENTRYPOINT java -jar app.jar

# ✅ 正确写法(exec 形式)
ENTRYPOINT ["java", "-jar", "app.jar"]

我遇到的问题就是写了第一种导致 PID 不是 1,信号没有传递。

shell 形式的问题:

  • 命令会通过 /bin/sh -c 执行
  • Java 进程不是 PID 1
  • 信号会被 shell 进程接收,而不是直接传递给 Java 应用
  • 可能导致优雅关机失效

4. 正确配置方法

4.1 Spring Boot 配置

1
2
3
4
# application.properties/yaml
server.shutdown=graceful
# 可以不配 默认30s
spring.lifecycle.timeout-per-shutdown-phase=20s

4.2 Docker Compose 配置

1
2
3
4
services:
your-service:
init: true # 使用 tini 作为初始化系统 如果PID是1 这个不用写
stop_grace_period: 30s # 设置足够的关闭等待时间

4.3 Dockerfile 配置

基础版本:

1
ENTRYPOINT ["java", "-jar", "app.jar"]

使用 tini 的增强版本:

1
2
3
RUN apt-get update && apt-get install -y tini
ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["java", "-jar", "app.jar"]

4.4 JVM 配置优化

1
ENTRYPOINT ["java", "-XX:+UseContainerSupport", "-XX:+ExitOnOutOfMemoryError", "-jar", "app.jar"]

我用的是 java21 里 UseContainerSupport写不写都可以,这个参数很老了,除非你用老版本的 java8,那可能还是需要开启的。

5. 调试方法

5.1 查看容器日志

1
docker-compose logs your-service

Spring Boot 会把 graceful shutdown 的内容打出来

5.2 Spring Boot 日志配置

1
2
3
4
5
6
7
8
9
@Component
public class ShutdownHook {
private static final Logger log = LoggerFactory.getLogger(ShutdownHook.class);

@PreDestroy
public void onShutdown() {
log.info("Application is shutting down...");
}
}

5.3 Docker Compose 命令

1
2
3
4
5
# 使用详细日志模式关闭
docker-compose down --verbose

# 指定超时时间关闭
docker-compose down -t 30

6. 最佳实践建议

  1. 总是使用 exec 形式的 ENTRYPOINT/CMD
  2. 配置足够的 stop_grace_period 时间
  3. 确保 stop_grace_period 大于 Spring Boot 的 shutdown timeout
  4. 考虑使用 tini 等初始化系统
  5. 添加适当的日志来监控关机过程
  6. 在开发环境充分测试关机流程

7. 关键时间设置建议

  • Spring Boot shutdown timeout: 20s
  • Docker stop_grace_period: 30s
  • Docker Compose down timeout: 30s

第二个和第三个是同一个东西,第三个用-t 会覆盖配置文件中的 stop_grace_period,临时设置用

这样的配置可以确保:

  1. 应用有足够时间处理现有请求
  2. Docker 不会过早发送 SIGKILL 信号
  3. 避免资源泄露和数据丢失

通过以上配置和最佳实践,可以确保 Spring Boot 应用在 Docker 环境中能够正确执行优雅关机流程。