我有一个脚本,可以选择在容器中运行。我观察到,如果我运行一个中间脚本,则可以使用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
答案 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将停止容器