Docker容器平滑停机需用exec模式ENTRYPOINT(如["./entrypoint.sh"])使脚本作为PID 1直接接收SIGTERM,再通过trap捕获信号执行清理并wait主进程;应用自身也须支持优雅关闭。

Docker 容器默认通过 SIGTERM 信号触发停机,但仅靠 CMD 或简单命令无法捕获该信号——必须由进程本身监听并处理。使用 ENTRYPOINT 配合可信号感知的启动脚本(如 shell 脚本或 Go/Python 等语言编写的主程序),才能实现平滑停机:例如关闭监听端口、等待活跃请求完成、释放资源等。
ENTRYPOINT 必须是可执行文件或脚本,且不被 shell 模式包裹
若写成 ENTRYPOINT ["./entrypoint.sh"](exec 模式),容器 PID 1 就是脚本本身,能直接接收 SIGTERM;而 ENTRYPOINT ./entrypoint.sh(shell 模式)会启动 /bin/sh -c 作为 PID 1,它**不会转发信号**给子进程,导致你的应用收不到停机信号。
- ✅ 正确写法:
ENTRYPOINT ["./entrypoint.sh"] - ❌ 错误写法:
ENTRYPOINT ./entrypoint.sh或ENTRYPOINT ["/bin/sh", "-c", "./entrypoint.sh"] - 确保
entrypoint.sh有可执行权限:RUN chmod +x entrypoint.sh
entrypoint.sh 需显式捕获 SIGTERM 并协调退出
脚本需在后台启动主进程(如 myapp &),再用 trap 捕获信号,执行清理逻辑后等待主进程结束。不能直接 exec myapp(虽简洁但无法拦截信号)。
- 启动应用时记录 PID:
./myapp & APP_PID=$! - 用
trap 'kill "$APP_PID" && wait "$APP_PID"' TERM响应停机 - 若应用支持优雅关闭(如
myapp stop),可在 trap 中调用,再wait - 最后用
wait "$APP_PID"阻塞脚本,让 PID 1 保持运行直到应用真正退出
应用自身也要支持优雅关闭语义
外部信号只是“通知”,最终是否平滑取决于应用能否响应。例如:
- Go 程序可用
signal.Notify监听os.Interrupt或syscall.SIGTERM - Node.js 可监听
process.on('SIGTERM', () => { server.close(...) }) - Java Spring Boot 默认监听
SIGTERM并触发ApplicationContext.close() - 若应用无内置支持,需在 entrypoint.sh 中模拟:如发送 HTTP shutdown endpoint、写停止文件、或用
timeout --signal=SIGKILL设定最大等待时间
验证平滑停机是否生效
构建镜像后,手动测试信号传递链是否完整:
- 运行容器:
docker run -d --name test-app your-image - 查看 PID 1 进程:
docker exec test-app ps -o pid,ppid,comm,确认是entrypoint.sh而非sh - 发送信号:
docker kill -s SIGTERM test-app - 观察日志或应用行为:是否打印 “shutting down…”、是否等待活跃连接、是否在超时后才被强制终止
- 检查退出码:
docker inspect test-app --format='{{.State.ExitCode}}',正常应为 0(非 137,137 表示被 SIGKILL 强杀)

















