一个实验

一个实验

sleep当然可以替代大多数复杂的过程。

这个 Dockerfile(如您所见,使用 exec 形式,因此只有一个进程正在运行并且没有子进程bash):

FROM busybox
CMD ["/bin/sleep", "100000"]

创建一个不可中断的容器:

docker build -t kill-sleep .
docker run --rm --name kill-sleep kill-sleep

当我尝试阻止它时:

time docker stop kill-sleep

kill-sleep
real    0m10.449s
user    0m0.021s
sys     0m0.027s

在容器被终止之前,命令将在 10 秒内超时。

问题不在于sleep不处理信号,因为如果我在主机上运行它:

sleep 100000
# in another shell
ps faxww | grep sleep
kill -TERM 31333  # the PID

该过程立即停止。

该问题可能与其在容器中作为 PID 1 运行有关,但我还没有看到有关该问题的参考文档。

答案1

当你跑步时docker stop ...有些事情会发生

  1. docker将 发送SIGTERM到容器的主进程。该进程能够屏蔽/忽略SIGTERM,如果它这样做(或在不终止的情况下处理它)“没有什么“将会发生。
  2. 超时(默认 10 秒)后,向主进程docker发送一个SIGKILL。此信号不能被进程屏蔽,因此它会立即死亡,没有机会执行关闭程序。

理想情况下,在其中运行的进程将及时docker响应,并在终止之前处理所有日常事务。SIGTERM

如果您知道该过程没有任何内部处理要执行(例如sleep:),或者不会正确响应SIGTERM,则可以使用标志指定更短(或更长)的超时-t

-t, --time=10
    Seconds to wait for stop before killing it

例如,就您而言,您可能喜欢运行docker stop -t 0 ${CONTAINER}


该信号行为不同的原因是由于sleepPID = 1 的运行。

通常(例如:以 PID != 1 运行),进程未明确处理的任何信号都会导致进程终止 - 尝试发送sleepa SIGUSR1

但是,当以 PID = 1 运行时,未处理的信号将被忽略,否则最终会导致内核恐慌:

Kernel panic - not syncing: Attempted to kill init!

您可以使用 docker 工具向 docker 容器发送信号,例如:

docker kill -s TERM kill-sleep

我们可以看到,这并没有达到预期的效果,而下面的代码确实达到了预期的效果:

docker kill -s KILL kill-sleep

一个实验

Dockerfile

FROM busybox
COPY run.sh /run.sh
RUN chmod +x /run.sh
CMD "/run.sh"

运行

#!/bin/sh

echo "sleeping"
sleep 100000

现在,运行

docker build -t kill-sleep .
docker run --rm --name kill-sleep kill-sleep

在另一个终端上:

docker stop kill-sleep

我们观察到相同的10秒延迟/超时。

一个办法

现在让我们来处理SIGTERM。后台处理和waitingsleep取决于 POSIX shell 处理信号的方式(参见了解更多信息)。

运行

#!/bin/sh

die_func() {
        echo "oh no"
        sleep 2
        exit 1
}
trap die_func TERM

echo "sleeping"
sleep 100000 &
wait

再次运行命令,我们就会看到我们想要的结果!

$ time docker stop kill-sleep
kill-sleep

real    0m2.515s
user    0m0.008s
sys     0m0.044s

答案2

更多选项:

  • 将开关添加--init到容器运行命令。这样,sleep 就不是 PID 1,并且 init 在 TERM 上执行正确的操作。
  • 将其添加--stop-signal=KILL到容器运行命令中。不过,一般不建议将 KILL 用作某种常规操作。

相关内容