Docker bash shell 脚本无法捕获 SIGINT 或 SIGTERM 文档详情相关说明

问题描述

我在一个目录中有以下两个文件

Dockerfile

FROM debian

workdir /app
copY start.sh /app/

CMD ["/app/start.sh"]

start.sh(使用 chmod +x start.sh 的权限为 755)

#!/bin/bash
trap "echo SIGINT; exit" SIGINT
trap "echo SIGTERM; exit" SIGTERM
echo Starting script
sleep 100000

然后我运行以下命令:

$ docker build . -t tmp
$ docker run --name tmp tmp

然后我希望按 Ctrl+C 会向程序发送一个 SIGINT,该程序会将 SIGINT 打印到屏幕然后退出,但这不会发生。

我也尝试运行 $ docker stop tmp,我希望它会向程序发送一个 SIGTERM,但检查 $ docker logs tmp显示未捕获 SIGTERM。

为什么 SIGINT 和 SIGTERM 没有被 bash 脚本捕获?

解决方法

CTRL+C 向在该控制台上运行的 docker 发送信号。
要向脚本发送信号,您可以使用

docker exec -it <containerId> /bin/sh -c "pkill -INT -f 'start\.sh'"

或者在您的脚本中包含 echo "my PID: $$" 并发送

docker exec -it <containerId> /bin/sh -c "kill -INT <script pid>"

docker 中的某些 shell 实现可能会忽略该信号。 此脚本将正确响应 pkill -15。请注意,信号被指定为 without the SIG prefix

#!/bin/sh
trap "touch SIGINT.tmp; ls -l; exit" INT TERM
trap "echo 'really exiting'; exit" EXIT
echo Starting script
while true; do sleep 1; done

sleep may ignore some signals 起,长 sleep 命令被短命令的无限循环所取代。

,

实际上,如果您使用以下命令之一运行容器,您的 Dockerfilestart.sh 入口点脚本与我使用 Ctrl+C 时一样: >

  • docker run --name tmp -it tmp
  • docker run --rm -it tmp

文档详情

docker run --help中所述:

  • --interactive = -i CLI 标志要求即使未附加也保持 STDIN 打开
    (通常对交互式 shell 有用,或者当还传递 --detach = -d CLI 标志时)
  • --tty = -t CLI 标志要求分配一个伪 TTY
    (特别是将信号转发到 shell 入口点,对您的用例特别有用)

相关说明

为了完整起见,请注意有几个相关问题可能使 docker stop 花费太多时间并“退回”到 docker kill,这可能在 shell入口点启动其他一些进程:

  • 首先,当 shell 入口点的最后一行运行另一个主程序时,不要忘记在这一行前面加上内置的 exec
    exec prog arg1 arg2 ...
  • 但是当 shell 入口点打算长时间运行时,捕获信号(至少 INT / TERM,但 不是 KILL)是很重要;
    {另见这个问题:Docker Run Script to catch interruption signal}
  • 否则,如果信号没有转发到子进程,我们就有可能遇到“PID 1 僵尸收割问题”,例如
    {另请参阅此 SO 问题了解详情:Speed up docker-compose shutdown}