Docker默认10秒超时易致强制终止,需延长stop-timeout(如--stop-timeout=60)、用exec启动或tini确保SIGTERM可达,并在应用层响应信号完成清理,K8s还需配合readinessProbe及时摘流。
关键不是“防止非法中断”,而是让长任务有足够时间完成清理和退出。docker 本身不会“非法”中断,但默认 10 秒超时对耗时操作(如数据库迁移、大文件上传、批量计算)明显不够,导致 sigkill 强制终止,任务被截断、状态丢失、连接未释放。
延长容器停止宽限期
Docker 默认只等 10 秒就发 SIGKILL。必须显式拉长这个窗口:
- 运行时加 --stop-timeout=60(单位秒),例如:
docker run --stop-timeout=60 -d my-app - Docker Compose 中写入:
stop_grace_period: 60s(支持60、60s、1m等格式) - Kubernetes 中对应配置 terminationGracePeriodSeconds: 60,且该值应 ≥ 应用实际最长关闭耗时
确保信号能真正传到你的应用
很多镜像用 CMD java -jar app.jar 这类 shell 形式启动,此时 sh 是 PID 1,它不转发 SIGTERM 给子进程——你的 Java 进程根本收不到信号。
- 改用 exec 格式:把
CMD java -jar app.jar换成CMD ["java", "-jar", "app.jar"] - 若必须用 shell 启动(比如要 source 环境变量),加轻量 init:在 Dockerfile 中加入
ENTRYPOINT ["/sbin/tini", "--"],并安装 tini(Alpine 用apk add --no-cache tini,Debian 用apt-get install -y tini)
应用层注册真正的退出逻辑
光等时间没用,你的代码得响应 SIGTERM 并主动管理生命周期:
-
Java(Spring Boot):启用优雅关机,设
server.shutdown=graceful和spring.lifecycle.timeout-per-shutdown-phase=45s;用@PreDestroy或实现SmartLifecycle做资源释放 -
Node.js:监听
process.on('SIGTERM', () => { server.close(() => process.exit(0)); }),避免直接process.exit() -
Go:用
signal.Notify(c, syscall.SIGTERM, syscall.SIGINT)捕获,调srv.Shutdown(ctx)等待 HTTP 连接 graceful 结束
配合就绪探针切断新流量
在 Kubernetes 等编排环境中,仅等关闭不够——新请求还在进来,会拖慢退出速度甚至引发并发问题。
- 在收到 SIGTERM 后,主动让 readinessProbe 失败(例如返回 503 或修改健康检查端点逻辑)
- K8s 会在几秒内将 Pod 从 Service Endpoints 中摘除,新流量不再流入,旧请求可专注完成
- 避免 readinessProbe 设置过长的
initialDelaySeconds或failureThreshold,否则摘流延迟











