Docker环境下Bash脚本无法捕获SIGINT/SIGTERM信号问题咨询
Docker 容器内Bash脚本无法捕获SIGINT/SIGTERM的原因
- 信号未转发:默认执行
docker run时不会分配交互终端,也不会将宿主机的stdin和信号转发到容器内,你本地按下Ctrl+C产生的SIGINT信号根本没有传递到容器内部的脚本进程,自然不会触发trap逻辑。 - Bash信号处理机制限制:Bash在等待前台子进程运行时会阻塞所有信号处理。你的脚本最后执行的
sleep 100000是前台子进程,Bash会一直阻塞到sleep运行结束才会处理收到的SIGINT、SIGTERM信号;而sleep 100000的运行时长远超过docker stop默认的10秒超时时间,超时后Docker会直接发送SIGKILL强制销毁容器,所以你看不到信号触发的日志输出。 - PID1进程特殊规则:Linux系统中PID为1的进程有特殊的信号处理逻辑,默认会忽略所有没有显式注册处理函数的信号,你这里虽然已经注册了信号trap,但配合上Bash的前台进程等待阻塞逻辑,进一步导致信号无法被及时处理。
- 补充验证:你当前Dockerfile使用的
CMD ["/app/start.sh"]是exec格式,会直接将bash脚本作为容器PID1运行,逻辑是正确的;如果使用shell格式CMD /app/start.sh,容器PID1会是/bin/sh -c进程,信号会被该进程拦截,根本无法传递到你的bash脚本。
解决方案
- 调整Bash脚本逻辑,将长时间运行的进程放到后台执行,通过wait命令等待,此时Bash可以在等待期间正常响应信号:
#!/bin/bash trap "echo SIGINT; kill ${sleep_pid}; exit" SIGINT trap "echo SIGTERM; kill ${sleep_pid}; exit" SIGTERM echo Starting script # 将sleep放到后台运行 sleep 100000 & # 记录后台sleep进程的PID sleep_pid=$! # 等待sleep进程结束,等待期间Bash可正常处理信号 wait ${sleep_pid}
- 如果需要前台运行容器时
Ctrl+C能正常生效,启动容器时添加-it参数开启交互终端和信号转发:
docker run -it --name tmp tmp
- 也可以在启动容器时添加
--init参数,让Docker内置的轻量init进程作为容器PID1,它会自动转发所有信号给子进程,同时适配PID1的特殊信号处理规则,避免原生Bash作为PID1的信号处理异常:
docker run --init --name tmp tmp
内容的提问来源于stack exchange,提问作者thesilican
相关产品推荐
相关产品推荐

