“为什么我的容器 docker stop 要等那么久?”(默认的等待时间随环境配置而不同,这台 Docker Desktop 是 3 秒,见 c30)几乎总是因为 SIGTERM 没有到达你的程序,或者你的程序作为 PID 1 没有处理函数。这篇把几种写法并排跑一遍,测 docker stop 的用时和退出码,再验证 PID 1 对 SIGTERM 的特殊对待。
[exec 形式 ENTRYPOINT ["/app.sh"]]
docker stop -t 3 用时 0.10 秒;退出码 0;日志:app 启动,PID=1 / 收到 SIGTERM,清理后退出
[shell 形式、单条命令 ENTRYPOINT /app.sh]
docker stop -t 3 用时 0.08 秒;退出码 0;日志:app 启动,PID=1 / 收到 SIGTERM,清理后退出
[shell 形式、复合命令 ENTRYPOINT /app.sh && echo done]
docker stop -t 3 用时 3.13 秒;退出码 137;日志:app 启动,PID=7
[shell 形式、复合命令,但用 exec 接管:ENTRYPOINT exec /app.sh]
docker stop -t 3 用时 0.13 秒;退出码 0;日志:app 启动,PID=1 / 收到 SIGTERM,清理后退出
[exec 形式 ENTRYPOINT ["sleep","300"](没有信号处理)]
docker stop -t 3 用时 3.16 秒;退出码 137;日志:
[同上,docker run --init]
docker stop -t 3 用时 0.13 秒;退出码 143;日志:exec 形式和 exec /app.sh 接管后,程序自己就是 PID 1,docker stop 发的 SIGTERM 它能收到;复合 shell 命令(/app.sh && echo done)让 /bin/sh 当 PID 1,程序成了 PID 7 的子进程,sh 不转发信号,日志里程序根本没收到 SIGTERM,等满超时后被强杀(137)。
容器内对 PID 1(sleep 300)发 kill -TERM 1 后 1 秒,ps 看到:PID COMMAND | 1 sleep 300 | 7 sh -c kill -TERM 1; sleep 1; ps -o pid,args | head -4 在同一个容器里另起一个 sleep 300(PID 不是 1),对它发 kill -TERM:退出码 143(143 = 128 + 15,即被 SIGTERM 终止)
容器里对 PID 1 发 kill -TERM 1,没有信号处理函数的 sleep 300 1 秒后还在;同一个容器里另一个 sleep 300(PID 不是 1)被同样的信号杀死(退出码 143)。内核对 PID 1 的默认信号动作做了保护。我没有读内核源码,这是现象。
docker kill --signal TERM(sleep 300 作为 PID 1,没有信号处理):1 秒后容器状态 = running 接着 docker stop -t 2:用时 2.13 秒,退出码 137
从宿主机用 docker kill --signal TERM 发信号,PID 1 的 sleep 1 秒后仍是 running;docker stop -t 2 等满 2 秒后发 SIGKILL,退出码 137。加 --init(容器里多一个 init 进程负责转发信号并回收子进程)后,同样的 sleep 在 0.13 秒内以 143 退出。