dockerfile无法实现优雅停机,其作用是为优雅停机打基础:需用exec格式使应用成为pid 1、通过stopsignal明确信号约定、配合stop-timeout预留关闭时间、必要时引入tini确保信号可靠传递。

直接在 Dockerfile 里写“优雅停机逻辑”是不可能的——Dockerfile 不执行应用代码,也不处理信号响应。它只负责构建镜像、设定运行时环境。真正决定能否优雅停机的,是镜像怎么启动、主进程是否为 PID 1、以及应用本身是否监听 SIGTERM。Dockerfile 的作用,是为这些前提打下基础。
确保信号能传到应用:用 exec 启动,别让 shell 拦截
这是最常见也最容易被忽略的一环。如果 Dockerfile 写成:
CMD java -jar app.jar实际启动的是 sh -c "java -jar app.jar",shell 成了 PID 1,而它默认不转发 SIGTERM 到子进程。结果就是 docker stop 发了信号,但你的 Java 应用根本收不到。
正确做法是使用 exec 格式(JSON 数组),让应用直接成为 PID 1:
CMD ["java", "-jar", "app.jar"]或者更稳妥地显式 exec:
CMD ["sh", "-c", "exec java -jar app.jar"]显式声明期望信号:STOPSIGNAL 不是万能,但要对齐
STOPSIGNAL 只告诉 Docker:“stop 时发哪个信号”,默认是 SIGTERM(15),绝大多数场景就该用它。除非你有特殊需求(比如 CLI 工具习惯用 Ctrl+C 触发 SIGINT),否则不必改。
在 Dockerfile 中加一行即可明确意图:
STOPSIGNAL SIGTERM注意:这不会让应用自动响应,只是统一信号约定。应用代码仍需监听 SIGTERM 并执行 shutdown 流程。
为慢操作留出时间:配合 stop-timeout 使用
Dockerfile 本身不能设超时,但你可以通过注释或配套文档提醒运维人员设置合理 stop-timeout。例如在 Dockerfile 顶部加说明:
# 注意:本应用关闭需约 25 秒(含连接 draining + DB 连接释放),建议运行时指定 --stop-timeout=30这样团队部署时就知道该配多长等待时间,避免默认 10 秒导致强杀。
加固启动链路:引入 tini 防止孤儿进程和信号丢失
如果你的应用必须用 shell 启动(比如要 source 环境变量),又不想冒险让 shell 当 PID 1,可以用轻量 init 工具 tini:
- FROM alpine:latest
- RUN apk add --no-cache tini
- ENTRYPOINT ["/sbin/tini", "--"]
- CMD ["sh", "-c", "source /env.sh && exec java -jar app.jar"]
tini 会作为真正的 PID 1,负责信号转发、僵尸进程回收,大幅提高信号传递可靠性。











