几年前。当我刚开始玩docker时。我记得,如果您对pid(1)的处理不好,就会提到一些博客文章。您将创建一个僵尸docker容器。那时候。我选择按照建议开始使用名为dumb-init的初始化工具。而且我从来没有真正看到过创建僵尸容器。
但是我仍然很好奇为什么这是一个问题。如果我没记错的话,默认情况下,docker stop xxx
会将SIGTERM
发送到容器pid(1)进程。如果该过程不能在10秒内正常停止(默认)。 Docker将通过发送SIGKILL
到pid(1)进程来强制杀死它。而且我也知道pid(1)进程在Linux系统中很特殊。它可以忽略SIGKILL
信号(link)。但我认为,即使docker容器中进程的PID为1,也仅仅是因为它使用命名空间来限定进程的范围。在主机上,您应该看到该进程是另一个PID。可以被内核杀死。
所以我的问题是:
答案 0 :(得分:0)
不是僵尸容器,而是僵尸进程。写下这个zombie.py
:
#!/usr/bin/env python3
import subprocess
import time
p = subprocess.Popen(['/bin/sleep', '1'])
time.sleep(2)
subprocess.run(['/bin/ps', '-ewl'])
写这个Dockerfile
:
FROM python:3
COPY zombie.py /
CMD ["/zombie.py"]
构建并运行它:
chmod +x zombie.py
docker build -t zombie .
docker run --rm zombie
这里发生的是/bin/sleep
命令运行执行。父进程需要使用wait
进行清理,但是在运行ps
时并不需要,您会看到一个“ Z”僵尸进程。
但是,等等,还有更多!说您的过程确实会仔细清除。在此特定示例中,例如,subprocess.run()
包括必需的wait
调用,您可以将Popen
调用更改为run
。如果该子进程启动另一个子进程,并且 it 退出(或崩溃)而不等待它,则pid为1的init进程将成为僵尸的新父进程。 (这种方式已经工作了40年。)但是,在Docker容器中,主容器进程使用pid 1运行,如果不期望“额外”子进程,则可以在整个生命周期中使用陈旧的僵尸进程。容器。
这导致偶尔出现一个建议,即Docker容器应始终运行某种“真实”的初始化过程,可能只有tini最小,这样僵尸进程后某些东西会捡起来,而您的实际容器工作却没有不必担心。