为什么我不能总是用Ctrl-C杀死docker进程?

时间:2018-09-26 13:05:32

标签: docker

我有一个脚本,可以选择在容器中运行。我观察到,如果我运行一个中间脚本,则可以使用Ctrl-C将其杀死,但是如果不执行,则不能。
这是一个示例:
test1.sh

#!/bin/bash

if [ "${1}" = true ]; then
  while true; do echo "args: $@"; sleep 1; done
else
  docker run --rm -it $(docker build -f basic-Dockerfile -q .) /test2.sh $@
fi

test2.sh

#!/bin/bash

/test1.sh true $@

basic-Dockerfile

FROM alpine:3.7

RUN apk add --no-cache bash
COPY test1.sh test2.sh /
ENTRYPOINT ["bash"]

运行./test1.sh true foo bar将愉快地打印出true foo bar,运行./test1.sh foo bar将在容器中执行相同的操作。发送Ctrl-C将终止进程并按预期方式删除容器。
但是,如果我尝试通过将/test2.sh $@更改为/test1.sh true $@来消除对多余文件的需要:
test1.sh

#!/bin/bash

if [ "${1}" = true ]; then
  while true; do echo "args: $@"; sleep 1; done
else
  docker run --rm -it $(docker build -f basic-Dockerfile -q .) /test1.sh true $@
fi

然后,该过程将不再由Ctrl-C终止,而必须由docker kill终止。

为什么会这样?

在Windows 10中以WSL运行的Docker版本18.06.1-ce

2 个答案:

答案 0 :(得分:2)

这是Docker中常见的误解,但这是有充分理由的。

当进程在Linux中以PID 1运行时,其行为会有所不同。具体来说,除非脚本经过编码,否则它将忽略信号为SIGTERM(在按Ctrl-C时发送)。 PID> 1时不会发生这种情况。

这就是为什么您的第二个方案起作用的原因(PID 1是script2.sh,它在script1.sh中委派了信号,由于不是PID1而停止了信号),而不是第一个方案(script1.sh是PID 1,因此它不会因SIGTERM而停止。

要解决此问题,您可以将信号捕获在script1.sh中并退出:

exit_func() {
        echo "SIGTERM detected"            
        exit 1
}
trap exit_func SIGTERM SIGINT

或者告诉docker run使用与PID 1不同的进程来初始化容器。具体地说,如果将--init添加到没有更多参数的docker run中,它将使用默认程序{{3} },准备处理这些情况:

docker run --rm -it --init $(docker build -f basic-Dockerfile -q .) /test1.sh true $@

答案 1 :(得分:1)

您也可以使用exec将当前的shell替换为可以用ctrl-c停止的新shell。 例如,启动nginx服务器并运行uwsgi的start.sh脚本

#!/usr/bin/env bash

service nginx start
uwsgi --ini uwsgi.ini

应更改为

#!/usr/bin/env bash

service nginx start
exec uwsgi --ini uwsgi.ini

这些更改之后,ctrl c将停止容器