从容器内部停止docker容器

38

我有一个在 Docker 容器内运行的 cronjob,它会检查所有服务是否按预期运行。如果 cronjob 确定存在问题,我想停止 Docker 容器(从内部...)

不幸的是,exit 只会停止我的 cronjob 脚本。


1
你的容器基于哪个镜像? - Thomasleveil
1
它来自于Ubuntu 12.04。 - Nils Ziehn
1
这似乎是一个XY问题。你可能想要使用像Supervisord这样的进程管理器作为入口来管理多个进程,并使用supervisord-watchdog事件监听器在特定条件下终止supervisord。 - StockB
如果你的Apache正在运行前台-只需杀死该进程(使用ps -afx列出进程,使用kill PIDNUMBER(通常是kill 1)杀死进程),这似乎也适用于Mariadb / mysqld - jave.web
7个回答

18

基本上,你需要 PID 1 退出以停止容器。

执行 kill -s SIGKILL 1 无法生效,因为 PID 1 受到保护。

如 @Thomasleveil 建议的,你可以在 PID 1 脚本中添加代码,例如 trap "exit" SIGINT SIGTERM,这将意味着当发送 kill -s SIGINT 1 时,进程会退出。我稍微倾向于这种方法,而不是你提出的方法(直接杀死子进程),因为它给父进程一个机会来清理,并且父进程应该能够找到子进程的 PID,而不需要使用 awk。

处理这个问题最简单的方法是使用某种监督进程,而 Docker 现在提供了一个名为 "tini" 的监督进程。只需在 docker run 中添加参数 --init,Docker 将在容器中设置 tini 作为 PID 1。完整的描述在这里:https://docs.docker.com/engine/reference/run/#specify-an-init-process


2
trap "exit" SIGINT SIGTERM添加到我的shell脚本中,使我能够在PID 1上使用kill命令。我想,除非我们知道正在使用哪个docker镜像,否则无法回答OP的特定情况。 - Thomasleveil
14
对我来说真正奏效的方法就是杀死所有进程。我有一个作为PID 1运行的shell脚本,其中有一个长时间运行的子进程。这个子进程没有保护机制,只要杀掉它就可以退出计算机。ps x | awk {'{print $1}'} | awk 'NR > 1' | xargs kill。 - Nils Ziehn
7
@NilsZiehn,您应该回答自己的问题并提供详细信息,以帮助其他人了解实际问题和克服问题的不同方法。此外,请查看pkill命令。 - Thomasleveil
2
@AdrianMouat,无论是kill -s SIGINT 1还是kill -s SIGKILL 1都不起作用。虽然我后来发现将PID更改为负数以杀死整个PID组似乎可以工作,例如kill -s SIGINT -1kill -s SIGKILL -1。这似乎与您的经验相反...我想知道您的入口脚本是否有所不同。 - StockB
2
如果有人到这里来,现在一个直截了当且正确的方法是使用tini作为init - 它已经内置于docker中。请查看:https://docs.docker.com/engine/reference/run/#specify-an-init-process - Mr.Budris
显示剩余9条评论

13

我遇到了同样的问题,通过使用docker的healthcheck功能解决了它。

您可以在类似于heahthcheck.sh的文件中添加检查以验证您的服务。然后将以下内容附加到您的Dockerfile

# Healthcheck
COPY healthcheck.sh /
RUN chmod +x /healthcheck.sh

HEALTHCHECK --interval=10s --retries=5 CMD /healthcheck.sh

这样可以确保Docker通过在指定的时间间隔内运行脚本来检查容器的健康状况,如果连续5次失败,则将状态标记为不健康。

现在,为了在状态变为不健康时重新启动容器,您可以像这样使用autoheal

docker run -d \
    --name autoheal \
    --restart=always \
    -e AUTOHEAL_CONTAINER_LABEL=all \
    -v /var/run/docker.sock:/var/run/docker.sock \
    willfarrell/autoheal

每当容器状态不健康时,这将重新启动您的容器。


5
受@JakeRobb启发: 您可以使用以下命令执行Docker:
/bin/bash -c "while true; do sleep infinity || exit 0; done"

这将在一个小于1的PID上执行sleep。一旦您杀死它,循环将退出。为此,请在docker中使用以下命令:

pkill -f sleep

2
你能澄清一下你所说的“使用命令执行Docker”的确切含义吗? - JakeRobb
1
如果他的意思是 docker exec...,那么它不起作用,只会冻结,也许他的意思是在容器启动时将其作为开头命令运行,那么它就可以工作了。 - jave.web

4
最近,我不得不想办法解决这个问题。经过长时间的挣扎,我总结了以下方法:
在你的dockerfile中,指定一个ENTRYPOINT:
ENTRYPOINT ["/entrypoint.sh"]

然后,提供这样一个脚本。它不需要做任何事情,只需调用您的应用程序。您可以随意向脚本添加其他设置,但请注意,如果脚本在调用您的应用程序之后执行任何操作,可能会掩盖您的应用程序返回代码。如果这对您有影响,请确保脚本捕获返回代码并将其作为自己的返回代码传播出去。

注意:不要使用exec来调用您的应用程序!

结果是PID 1属于entrypoint.sh,而您的应用程序将具有一些其他PID。我的应用程序通常落在PID 9或10上。

然后,当您需要终止您的应用程序时,只需确定其PID并调用kill -SIGKILL $PIDpkill -SIGKILL yourapp。您的进程不是PID 1,将接收到信号并立即退出。您的entrypoint.sh将立即退出,因为这是您设计它在您的进程退出后所做的事情。


这对于终止入口点非常有效,但不允许终止可能已经在没有入口点的情况下运行的容器。 - StockB
当然,你可以自由地为任何容器创建自己的Dockerfile。如果你有一个原始的Dockerfile,可以以它为基础,编写并复制适当的entrypoint.sh,然后包含相应的ENTRYPOINT。这对于已经运行的容器没有帮助,所以从外部使用docker kill杀死它们,并用更新后的镜像替换它们。 - JakeRobb
@JakeRobb,谢谢你的想法。我发布了一个受这个答案启发的答案,它允许杀死容器但不修改Dockerfile。 - Yuval Atzmon

2

以下是我自杀 /usr/local/bin/docker-entrypoint.sh 的示例

#!/bin/sh
#

# Following guard prevents to stuck containers in DIND GitLab Runner
# If container still works in 300 seconds, this will kill yourself
THE_PID=$$
if [ $THE_PID -eq 1 ]; then
    # Docker does not allow kill process 1, so restart as child process
    /usr/local/bin/docker-entrypoint.sh
    exit $?
fi
(sleep 300; echo "Killing process: $THE_PID"; kill -9 $THE_PID) &


YOUR_SCRIPT_TEXT....

完美运行! - cwirz

1
我杀掉了“阻塞进程”,也就是你的Docker正在运行的主进程;就像这样(在这种情况下,Docker正在运行一个sleep infinity命令以进行演示)。 ps -afx | grep sleep | awk '{print $1}' | xargs kill -9

谢谢分享这个想法!我稍微修改了一下。我有一个在Docker容器中的Node.js应用程序,并使用Entrypoint。因此,我只需使用以下命令杀死“npm”进程:ps -afx |grep npm | awk '{print $1}' | xargs kill -9 - gearcoded

-4

我试图杀死进程1,但没有成功。

尝试使用@zero323的评论中的shutdown -h now。它可以正常工作(抱歉,我无法直接为它投票,因为它不在答案列表中)。


5
以下是最新的Ubuntu映像中发生的情况:
root@b968bf313300:/# shutdown -h now 无法连接总线:没有这样的文件或目录 无法与init守护程序通信。
并且什么也没发生。
- Kevin Buchs
5
因为这个方法不起作用,所以被踩了。shutdown 命令与 initd/systemd/等等通信,而在非容器场景下,这些进程是 PID 1。但在容器中,它们不存在。同样的情况也适用于 reboot 命令。 - JakeRobb
对我而言,在使用 node:10 镜像时,我会得到 bash: shutdown: command not found 的错误提示。 - andrew lorien

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接