优雅退出的关键是确保pid 1进程直接响应sigterm:必须用exec格式写cmd/entrypoint,避免shell占用pid 1;推荐运行时加--init或docker-compose中设init: true;应用代码需监听sigterm,并配置足够stop-grace-period。
要在 docker 容器中实现优雅退出,关键不是在 dockerfile 里“配置信号量”,而是确保容器启动后,pid 1 进程能直接收到并响应 sigterm。dockerfile 的作用是为这个目标打下基础——控制进程启动方式、避免信号被拦截、预留信号处理空间。
用 exec 格式写 CMD 或 ENTRYPOINT
这是最核心的一条。Shell 格式(如 CMD node server.js)会让 shell 占据 PID 1,而真正应用变成子进程,收不到 Docker 发来的 SIGTERM。
- ✅ 正确写法(exec 格式,PID 1 是你的应用):
CMD ["node", "server.js"]ENTRYPOINT ["python", "app.py"] - ❌ 错误写法(shell 格式,PID 1 是 sh):
CMD node server.jsENTRYPOINT python app.py - ⚠️ 若需 shell 功能(比如变量展开),可用
exec显式交出控制权:CMD exec node server.js或CMD sh -c 'exec node $APP_FILE'
可选:用 --init 启动容器(推荐生产环境)
Docker 提供 --init 参数,会自动注入轻量 init 进程(如 tini)作为 PID 1,它能正确转发信号、回收僵尸进程。虽然这不是 Dockerfile 内的配置,但应在运行时启用:
- docker run --init myapp
- Docker Compose 中加:
init: true - 它不替代应用自身的信号处理,但能兜底提升健壮性
可选:自定义 STOPSIGNAL(极少需要)
默认 Docker 发送 SIGTERM(编号 15),绝大多数应用都监听这个信号。只有当你明确需要换其他信号(如 SIGUSR2)时,才用 STOPSIGNAL 指令:
STOPSIGNAL SIGUSR2- 对应地,你的应用代码也得监听 SIGUSR2,而非 SIGTERM
- 一般项目无需改动,默认 SIGTERM 就是最标准、最兼容的选择
配套动作不在 Dockerfile,但在部署中必须做
Dockerfile 只管“怎么启”,优雅退出还依赖两个外部配合:
-
应用代码要监听 SIGTERM:Node.js 用
process.on('SIGTERM', ...),Go 用signal.Notify,Python 用signal.signal(signal.SIGTERM, ...) -
设置足够 stop-timeout:Docker 默认只等 10 秒,建议在 docker-compose.yml 中设
stop_grace_period: 30s,或运行时加--stop-timeout=30











