Docker bash shell 脚本无法捕获 SIGINT 或 SIGTERM

问题描述 投票:0回答:5

我的目录中有以下两个文件:

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 未被捕获。

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

bash docker shell sigint sigterm
5个回答
6
投票

实际上,只要您使用以下命令之一运行容器,您的

Dockerfile
start.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 Interrupt signal}
  • 否则,如果信号没有转发到子进程,我们就有遇到“PID 1僵尸收割问题”的风险,例如
    {另请参阅此问题以了解详细信息:加速 docker-compose shutdown}

3
投票

原因是 bash 在前台进程终止之前不会处理信号,在您的情况下是

sleep 10000
。你的陷阱确实有效,但你必须先等待 10000 秒。

你的解决办法是

#!/bin/bash
pid=
trap 'echo SIGINT; [[ $pid ]] && kill $pid; exit' SIGINT
trap 'echo SIGTERM; [[ $pid ]] && kill $pid; exit' SIGTERM
echo Starting script
sleep 10000 & pid=$!
wait
pid=

此外,您可能希望用捕获任何出口来替换捕获特定信号

trap 'echo EXIT; [[ $pid ]] && kill $pid; exit' EXIT

这些以及更多内容在 http://mywiki.wooledge.org/SignalTrap#When_is_the_signal_handled.3F

都有很好的解释

2
投票

我找到的解决方案是只使用

--init
标志。

docker run --init [MORE OPTIONS] IMAGE [COMMAND] [ARG...]

根据他们的文档...


0
投票

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
。请注意,指定的信号没有
SIG
前缀

#!/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
命令被短命令的无限循环所取代,因为
sleep
可能会忽略一些信号


0
投票

尝试使用以下脚本作为您的

ENTRIPOINT

#!/bin/bash

# Start the main process and save its PID
# Use exec to replace the shell script process with the main process
exec my_main_process &
pid=$!

# Trap the SIGTERM signal and forward it to the main process
trap 'kill -SIGTERM $pid; wait $pid' SIGTERM

# Wait for the main process to complete
wait $pidh

来源:https://www.linkedin.com/pulse/propagating-sigterm-signal-main-process-kubernetes-pod-chidambaram/

© www.soinside.com 2019 - 2024. All rights reserved.