为何在Docker容器的Bash脚本中额外添加一组trap与wait?
trap - SIGTERM与wait? 先来看你的完整脚本:
_term() { echo "SHELL: Caught SIGTERM signal, propagating to the child" kill -TERM "$child" 2>/dev/null } trap _term SIGTERM java -jar RunningProcessWithJMX.jar & # 在后台启动Java进程 child=$! wait "$child" # Line #10 trap - SIGTERM # Line #11 wait "$child" # Line #12
咱们来拆解一下这额外两行的作用,得先搞清楚Bash处理信号和wait命令的特性:
第一个wait "$child"的局限性
当Docker要停止容器时,会给PID为1的这个Bash脚本发送SIGTERM信号。这时候Bash会暂停当前正在执行的wait "$child"命令,转而执行我们定义的_term函数——给Java子进程转发SIGTERM信号,让它开始退出流程。
但问题来了:当_term函数执行完毕后,Bash会回到之前被中断的wait命令,这时候这个wait会立即返回一个非零的退出状态码(因为它被信号打断了),而不会继续等待Java进程真正完成退出操作。如果脚本在这里就结束了,Java进程可能还在做关闭资源、保存数据之类的收尾工作,这时候Docker会认为PID 1已经退出,但容器里还残留着Java进程,导致容器无法优雅停止,甚至留下孤儿进程。
额外两行的作用
trap - SIGTERM:这行是移除之前设置的SIGTERM信号捕获。这么做是为了避免后续如果Docker因为超时再次发送SIGTERM(或者其他意外的SIGTERM),脚本不会再触发_term函数——毕竟我们已经给子进程发过终止信号了,重复发送没必要,反而可能干扰子进程的退出流程。第二个
wait "$child":这才是真正等待Java子进程彻底退出的关键。因为第一个wait被信号打断提前返回了,我们需要再执行一次wait,确保等到子进程的退出状态被完全回收,不管它是正常退出还是被信号终止。只有当这个wait完成,我们才能确定Java进程已经彻底清理完毕,脚本再退出的话,Docker容器就能优雅停止了。
总结
这两行是为了弥补Bash中wait命令被信号中断的特性,确保子进程能完成完整的退出流程,避免Docker容器停止时出现残留进程的问题,让整个容器的停止过程更优雅可靠。
内容的提问来源于stack exchange,提问作者comiventor

