优雅地停止 Docker 个容器

Gracefully Stopping Docker Containers

我在理解如何在容器停止时进行清理时遇到一些问题。

为了方便起见,我准备了一个示例来重现该问题。

以下是我的文件内容:

Dockerfile

FROM opensuse:latest

# Install tcsh (non-interactive mode)
RUN zypper -n in tcsh

# Create user
RUN useradd -ms /bin/tcsh dummyuser

# Set the user
USER dummyuser

# Change Working Dir
WORKDIR /home/dummyuser

# Copy entrypoint script
COPY docker-entrypoint.sh $HOME

# Starter Script
ENTRYPOINT ["./docker-entrypoint.sh"]

docker-entrypoint.sh

#!/bin/tcsh

echo "Starting"

onintr cleanup

# Running something in foreground, otherwise the container will stop
while (1)
   sleep 1000
end
exit 0

cleanup:
   onintr -
   echo "cleanup on going"
   exit 0

使 docker-entrypoint.sh 可执行:

chmod 744 docker-entrypoint.sh

构建图像:

docker build -t my-dummy-img .

请注意我正在使用 tcsh shell.

如果您看一下 docker-entrypoint.sh,您会发现我正在等待 cath 中断 (onintr cleanup) 并调用清理方法。

现在,这些是我的命令 运行:

mstack/dummy-project> docker run --name my-service -ti -d my-dummy-img ps -eaf
da1dc21281a58e384f2ff34aa49a82019214e204e6d7a77ff54e8c96e005f913
mstack/dummy-project> docker logs my-service
Starting
mstack/dummy-project> docker stop my-service
my-service
mstack/dummy-project> docker logs my-service
Starting
mstack/dummy-project>

这是问题所在,我希望在第二个 docker logs my-service 之后输出将是:

Starting
cleanup on going

而不只是

Starting

因为docker应该在停止时发出信号...

另一方面,如果我 运行:

docker run --name my-service-attached -ti my-dummy-img ps -eaf

然后点击 CTRL+C,我可以看到预期的输出。

我在这里错过了什么?我希望问题足够清楚。

顺便说一句,我使用以下文章作为指南:

Gracefully Stopping Docker Containers

Trapping signals in Docker containers

这可能是因为您在分离模式下启动它:see documentation

onintr is ignored if the shell is running detached

你必须找到替代方案,比如使用 bash 和陷阱 as seen here

终于解决了问题

Tcsh shell doesn't receive most of the signals like SIGTERM这是docker停止容器时发出的信号。

所以我将脚本更改为使用 bash shell 每当我想 运行 tcsh 命令时,我就这样做:

/bin/tcsh ./my-command

所以,我的docker-entrypoint.sh是这样的:

#!/bin/bash

# SIGTERM-handler this funciton will be executed when the container receives the SIGTERM signal (when stopping)
term_handler(){
   echo "***Stopping"
   /bin/tcsh ./my-cleanup-command
   exit 0
}

# Setup signal handlers
trap 'term_handler' SIGTERM

echo "***Starting"
/bin/tcsh ./my-command

# Running something in foreground, otherwise the container will stop
while true
do
   #sleep 1000 - Doesn't work with sleep. Not sure why.
   tail -f /dev/null & wait ${!}
done

我有一个简单的陷阱 bash 脚本,运行 main 函数调用所有其他程序,shutdown 函数 运行优雅关闭它的脚本:

trap "shutdown" SIGTERM

main

停止容器时无法Docker触发关闭函数。在命令前添加 exec,使用 tini 作为 init,更改 STOPSIGNAL 都无济于事。原因是程序main运行没有return控件

在末尾添加 & wait 使其工作正常:

trap "shutdown" SIGTERM

main & wait