我有一个脚本,我想在一个容器中可选地运行。我观察到,如果我运行一个中间脚本,它可以用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
来终止。
为什么会这样?
Docker版本18.06.1-ce在WSL上的Windows 10上运行
这是码头工人常见的误解,但这是有充分理由的。
当进程在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,它会使用一个默认程序tini来准备处理这些情况:
docker run --rm -it --init $(docker build -f basic-Dockerfile -q .) /test1.sh true $@
你也可以使用exec
用一个新的替换当前的shell,可以用ctrl-c来停止它。例如start.sh脚本启动nginx服务器并运行uwsgi
#!/usr/bin/env bash
service nginx start
uwsgi --ini uwsgi.ini
应该改为
#!/usr/bin/env bash
service nginx start
exec uwsgi --ini uwsgi.ini
在theese改变后ctrl c将停止容器