通过supervisor关闭Docker容器

14
我无法关闭由supervisor通过“supervisorctl stop all”启动的Docker容器。即使通过“supervisorctl status”显示容器已关闭,“docker ps”和“ps”也表明它们实际上仍在运行。
查阅supervisor文档中关于“supervisorctl stop ”操作的说明,发现会向进程发送“SIGTERM”,然后在一定的宽限期后再发送“SIGKILL”以终止进程。我尝试手动执行此操作,并发现:
- 发送到“docker run”进程的“SIGTERM”没有任何作用。 - “SIGKILL”确实可以杀死进程,但实际上并未更新docker。“docker ps”显示此容器仍在运行。 - Supervisor的“SIGKILL”无法关闭容器。
问题是:如何通过supervisor正确关闭Docker容器?
以下是模拟supervisor的实验结果:
起始状态:foo-1和bar-1正在运行(我将GCE容器保留了下来,以防它们有所不同)。 “ps aux”和“docker ps”是同步的。
me@devenv:~$ sudo docker ps
CONTAINER ID        IMAGE                   COMMAND                CREATED             STATUS              PORTS                    NAMES
5ba70bf8937f        me/app:foo              "/bin/sh -c 'supervi   5 minutes ago       Up 5 minutes                                 foo-1
e1a684bcfceb        me/app:bar              "/bin/sh -c 'supervi   5 minutes ago       Up 5 minutes                                 bar-1
fce5db0517df        google/cadvisor:0.8.0   "/usr/bin/cadvisor"    35 minutes ago      Up 35 minutes                                bbbb 
db677eed47ef        kubernetes/pause:go     "/pause"               35 minutes ago      Up 35 minutes       0.0.0.0:4194->8080/tcp   aaaa

me@devenv:~$ ps aux | grep "docker run"
root     23358  0.0  0.1 124092 11856 pts/0    Sl   02:05   0:00 docker run --rm --name foo-1 ... -i me/app:foo
root     23365  0.0  0.1 124092 11928 pts/0    Sl   02:05   0:00 docker run --rm --name bar-1 ... -i me/app:bar

通过向进程发送SIGTERM信号来模拟supervisorctl stop foo-1命令。结果:进程仍处于活动状态。

me@devenv:~$ sudo kill -SIGTERM 23358

... <waiting> ...

me@devenv:~$ ps aux | grep "docker run"
root     23358  0.0  0.1 124092 11856 pts/0    Sl   02:05   0:00 docker run --rm --name foo-1 ... -i me/app:foo
root     23365  0.0  0.1 124092 11928 pts/0    Sl   02:05   0:00 docker run --rm --name bar-1 ... -i me/app:bar

me@devenv:~$ sudo docker ps
CONTAINER ID        IMAGE                   COMMAND                CREATED             STATUS              PORTS                    NAMES
5ba70bf8937f        me/app:foo              "/bin/sh -c 'supervi   6 minutes ago       Up 6 minutes                                 foo-1
e1a684bcfceb        me/app:bar              "/bin/sh -c 'supervi   6 minutes ago       Up 6 minutes                                 bar-1
fce5db0517df        google/cadvisor:0.8.0   "/usr/bin/cadvisor"    36 minutes ago      Up 36 minutes                                bbbb 
db677eed47ef        kubernetes/pause:go     "/pause"               36 minutes ago      Up 36 minutes       0.0.0.0:4194->8080/tcp   aaaa

接下来主管会发出 SIGKILL 信号。结果:进程被杀死(ps aux),但在 docker 进程中仍然显示为正在运行(docker ps)。

me@devenv:~$ sudo kill -SIGKILL 23358
me@devenv:~$ ps aux | grep "docker run"
root     23365  0.0  0.1 124092 11928 pts/0    Sl   02:05   0:00 docker run --rm --name bar-1 ... -i me/app:bar

me@devenv:~$ sudo docker ps
CONTAINER ID        IMAGE                   COMMAND                CREATED             STATUS              PORTS                    NAMES
5ba70bf8937f        me/app:foo              "/bin/sh -c 'supervi   19 minutes ago      Up 19 minutes                                foo-1
e1a684bcfceb        me/app:bar              "/bin/sh -c 'supervi   19 minutes ago      Up 19 minutes                                bar-1
fce5db0517df        google/cadvisor:0.8.0   "/usr/bin/cadvisor"    49 minutes ago      Up 49 minutes                                bbbb 
db677eed47ef        kubernetes/pause:go     "/pause"               49 minutes ago      Up 49 minutes       0.0.0.0:4194->8080/tcp   aaaa

在上述实验过程中,为避免自启动行为干扰,监管进程已关闭。然而,即使监管程序记录显示相反的结果,Supervisor 发送 SIGKILL 信号给该进程也无法终止该进程;但是,通过使用 docker stop <container_id> 命令可以停止容器。

更新

在 Docker 容器内部还运行着一个管理某些进程的 supervisord 进程。或许问题在于信号未被传递,因此进程无法关机...

更新 2

我缩小了问题范围。我能够通过直接从 Dockerfile 中开始容器进程来控制此容器(而不是通过启动 supervisord),这样做有所区别。我能够通过监管程序 (位于 Docker 容器之外,用于控制容器)来控制此容器。

更新 3

此处建议,将 stopasgroup=true 设为 true 并没有改变什么。

更新 4

我已经解决了其中一个问题:监管程序无法关闭进程。问题在于,在监管配置文件中使用 command=sudo docker run... 启动 Docker 容器时,会创建一个 sudo docker run... 进程和一个 docker run... 进程。当运行 supervisorctl stop... 时,只会终止 sudo docker run... 进程,而实际的 Docker 进程仍在运行。当我省略 sudo 命令时,每个监管程序只会启动一个进程,并且可以通过 supervisorctl stop 命令终止该进程。

问题仍然存在,即 docker ps 显示容器仍在运行,但 ps aux 显示相反的结果。奇怪的是,容器仍然对请求做出响应。快速查看进程列表可以确认,由 Docker 容器生成的所有进程仍处于活动状态,但进程列表中缺少 docker run... 进程。

更新 5

docker run 进程发送 SIGTERMSIGHUPSIGQUIT 信号似乎对该进程没有任何影响。只有使用 SIGKILL 才能正常终止 Docker 进程。监管程序会得到更新,但 docker ps 仍然显示 Docker 进程正在运行。

2个回答

9
我想我找到了问题所在。我没有意识到,但是在docker容器启动时有多种启动程序的方式。
显然,CMD myexec param1 param2会启动一个shell,然后再启动myexec(实际上这两个进程在容器中可见,PID 1为/bin/sh -c myexec...)。更好的方法是直接启动程序(在我的情况下是supervisord)。
另一方面,CMD ["/usr/bin/python", "/usr/local/bin/supervisord", "-c", "/root/supervisord.conf", "--nodaemon"]有效。我现在能够通过supervisor启动和停止docker容器。
这是docker文档中相关部分的链接

CMD指令有三种形式:

CMD ["executable","param1","param2"](执行形式,这是首选形式)

CMD ["param1","param2"](作为ENTRYPOINT的默认参数)

CMD command param1 param2(shell形式)

更新 示例supervisor文件(在Docker容器内):
[program:app]
command=python run_web_server.py
stdout_logfile=/var/log/app/app.log
directory=/opt/app
autostart=true
autorestart=false
stopsignal=INT
redirect_stderr=true
startretries=0
stopasgroup=true
killasgroup=true


[unix_http_server]
file=/var/run/supervisor.sock
chmod=0700

[supervisord]
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid
childlogdir=/var/log/supervisor

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///var/run/supervisor.sock

生成Docker(外部)supervisor文件的mako模板:
[program:container]
command=docker run --rm --name ${name} \
% if container_links is not UNDEFINED:
% for host in container_hosts:
--add-host ${host['name']}:${host['ip']} \
% endfor
% endif
% if container_links is not UNDEFINED:
% for link in container_links:
--link ${link}:${link} \
% endfor
% endif
% if port_mappings is not UNDEFINED:
% for ext in port_mappings:
-p ${ext}:${port_mappings[ext]} \
% endfor
% endif
-e "INSTANCE_NAME=${name}" \
-e "TZ=${timezone}" \
% if environ is not UNDEFINED:
% for k in environ:
-e "${k}=${environ[k]}" \
% endfor
% endif
-v ${deployment_dir}/tmp:${deployment_dir}/app/tmp \
... more -v
-i foo/app-${version}:${type}
stdout_logfile=${deployment_dir}/log/${name}.log
redirect_stderr=true
autostart=false
autorestart=false
% if priority is not UNDEFINED:
priority=${priority}
% endif
startretries=0
# stopasgroup=true
# killasgroup=true

能否看到您正在使用的supervisor配置文件示例?我也在尝试同样的事情,甚至使用了CMD ["executable"],但是supervisor仍然让我的容器保持运行状态。现在我担心我一开始就没有正确地运行容器。 - Daniel Buckmaster
非常感谢!哦,但是那个文件是容器内的supervisord文件还是外部的? - Daniel Buckmaster
这是Docker容器内的监管文件。我发布了我用来启动容器的文件,但我没有时间清理模板并提供实际的监管文件。 - orange
非常感谢您的帮助。我在想是否使用 --rm 是关键。我会再试一次! - Daniel Buckmaster
没问题。我认为 --rm 对于命名容器有帮助,否则在终止容器后,一个带有该名称的镜像仍会存在,这会阻止容器的后续运行(除非您手动删除该镜像)。 - orange

0
你好,我通过使用包装器和捕获信号来解决了这个问题。

Wrapper.sh:

function cleanup()
{
    docker kill ${CONTAINER_NAME}
}

trap cleanup INT

docker run -i --rm --name ${CONTAINER_NAME} ${IMAGE}&
wait

supervisord配置:
[program:coolapp]
command=/opt/coolapp/wrapper.sh
directory=/opt/coolapp
autostart=true
autorestart=true
stderr_logfile=/var/log/coolapp/error.log
stderr_logfile_maxbytes=0
stdout_logfile=/var/log/coolapp/stdouot.log
stdout_logfile_maxbytes=0
stopasgroup=true
stopsignal=INT

关于这个问题的一些想法:

你可能想要使用-it选项来保持容器在前台运行,并且supervisor将控制运行时。但是如果你尝试这样做,你会得到一个类似这样的错误:

the input device is not a TTY

可以通过使用-i选项而不是-it来修复它。

当您使用-i选项时,您可以启动容器,但无法向其发送信号。因此,在这种情况下,当监管者停止容器时,它会发送配置的信号(通常为SIGINT),并等待进程发送SIGCHLD,但是Docker客户端(docker run ...)不会发送响应。之后,监管者将发送SIGKILL信号,这将杀死客户端,但容器本身仍在运行。

我尝试使用script命令创建pty(虚拟TTY):

scropt -qc 'docker run -i --rm --name ${CONTAINER_NAME} ${IMAGE}' /dev/null

但是仍然没有结果,这次主管终止了脚本进程,容器仍然在运行。

你的回答可以通过提供更多的支持性信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的回答是否正确。你可以在帮助中心找到关于如何撰写好回答的更多信息。 - undefined

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