You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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。

验证步骤

  1. 启动容器后,用docker exec <容器ID> ps aux确认PID1是entrypoint.sh,Java是它的子进程。
  2. 执行docker stop <容器ID>,查看容器日志(docker logs <容器ID>)有没有hello SIGTERM的输出,确认trap是否触发。
  3. 如果还是没触发,先注释掉source /shutdown.sh,只留echo测试,排除脚本本身的问题。

内容的提问来源于stack exchange,提问作者zappee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 14:05:17