Docker容器停止前自动执行shutdown.sh脚本失败排查
为什么你的Bash Trap没触发?
核心问题:Bash等待子进程时会忽略SIGTERM
你设置的trap没生效,本质是因为你的entrypoint.sh作为PID1启动Java进程后,进入了阻塞等待子进程完成的状态。此时Bash的默认行为是忽略SIGTERM信号——它会把信号直接转发给子进程(Java),自身不会触发你定义的trap逻辑。
再加上PID1进程的特殊规则:普通进程默认会响应SIGTERM,但PID1的Bash如果没有显式配置信号处理,在等待子进程期间不会主动处理trap。
其他可能的诱因
- 没使用后台运行Java:如果直接用
java -jar app.jar启动,Bash会一直阻塞在这个命令上,完全没机会响应SIGTERM触发trap。 - Trap设置位置错误:如果
trap是在启动Java的命令之后才设置的,那Bash已经进入阻塞状态,根本没执行到trap的配置代码。 - Shutdown脚本的隐性错误:虽然你手动执行正常,但如果
shutdown.sh里有set -e之类的严格模式,可能在trap执行时因某个命令失败直接退出,导致你看不到echo的输出(可以先把source /shutdown.sh注释掉,只留echo测试)。
修复方案
方案1:后台启动Java+Wait+Trap(最可靠)
修改entrypoint.sh,把Java后台启动,记录PID,然后用wait等待,同时让trap负责清理:
#!/bin/bash # 定义信号处理逻辑:先执行shutdown脚本,再杀Java进程,最后退出 trap 'echo "hello SIGTERM"; source /shutdown.sh; kill "$JAVA_PID"; exit' SIGTERM # 后台启动Java,保存进程ID java -jar your-app.jar & JAVA_PID=$! # 等待Java进程结束,SIGTERM会中断wait并触发trap wait "$JAVA_PID"
这样,当docker stop发送SIGTERM给PID1的Bash时,wait会被中断,直接触发trap逻辑。
方案2:用Exec让Java成为PID1(适合简单场景)
如果你的entrypoint.sh除了启动Java和执行shutdown脚本外没其他逻辑,可以让Java直接成为PID1,结合Java自身的ShutdownHook处理退出:
#!/bin/bash # 先设置trap(仅在exec前生效,适合快速验证) trap 'echo "hello SIGTERM"; source /shutdown.sh' SIGTERM exec java -jar your-app.jar
注:该方案的trap仅在exec命令执行前收到信号才会触发,长期使用更推荐方案1。
方案3:开启Bash作业控制
在entrypoint.sh开头加set -m,开启作业控制,让Bash在等待子进程时响应信号:
#!/bin/bash set -m trap 'echo "hello SIGTERM"; source /shutdown.sh; exit' SIGTERM java -jar your-app.jar
这个方案简单,但作业控制可能带来意想不到的信号转发问题,优先推荐方案1。
验证步骤
- 启动容器后,用
docker exec <容器ID> ps aux确认PID1是entrypoint.sh,Java是它的子进程。 - 执行
docker stop <容器ID>,查看容器日志(docker logs <容器ID>)有没有hello SIGTERM的输出,确认trap是否触发。 - 如果还是没触发,先注释掉
source /shutdown.sh,只留echo测试,排除脚本本身的问题。
内容的提问来源于stack exchange,提问作者zappee
相关产品推荐
相关产品推荐

