如何在docker中使用bash作为PID1重现僵尸进程?

Pat*_*ick 5 unix bash zombie-process docker

我有一个在PID1上运行bash的Docker容器,后者又运行一个长期运行(复杂)的服务,有时会在PID1上生成bashie进程,这些进程是bash的父级.这些僵尸似乎从来没有收获过.

我正在尝试在最小容器中重现此问题,以便我可以测试缓解,例如使用正确的init作为PID1而不是bash.

但是,我无法重现僵尸进程.PID1的bash似乎收获了孩子,即使是从另一个过程继承的孩子.

这是我尝试过的:

docker run -d ubuntu:14.04 bash -c \
  'bash -c "start-stop-daemon --background --start --pidfile /tmp/sleep.pid --exec /bin/sleep -- 30; sleep 300"'
Run Code Online (Sandbox Code Playgroud)

我的期望是,start-stop-daemon双重分叉创建一个进程,在PID1的bash,然后exec进入sleep 30,当睡眠退出时,我预计这个过程仍然是一个僵尸.该sleep 300模拟长时间运行的服务.

但是,bash重新开始这个过程,我可以通过运行stracebash进程(从运行docker的主机)来观察:

$ sudo strace -p 2051
strace: Process 2051 attached
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 9
wait4(-1,
Run Code Online (Sandbox Code Playgroud)

我正在运行docker 1.11.1-rc1,尽管我对docker 1.9有相同的经验.

$ docker --version
Docker version 1.11.1-rc1, build c90c70c
$ uname -r
4.4.8-boot2docker
Run Code Online (Sandbox Code Playgroud)

鉴于strace显示bash收割(孤儿)的孩子,是否在Docker容器中使用合适的PID1?还有什么可能导致我在更复杂的容器中看到的僵尸?我怎样才能重现?

编辑:

我设法在strace显示问题的一个活动容器上附加一个bash PID1.

Process 20381 attached
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 11185
wait4(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGTERM}], 0, NULL) = 11191
wait4(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGTERM}], 0, NULL) = 11203
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 11155
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 11151
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 11152
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 11154
wait4(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGTERM}], 0, NULL) = 11332
...
Run Code Online (Sandbox Code Playgroud)

不确定所有那些现有进程是什么,但没有一个PID匹配那些显示的少数已解散的僵尸进程docker exec $id ps aux | grep defunct.

也许诀窍就是抓住行动,看看wait4()仍然是僵尸的过程会有什么回报......

ako*_*nov 2

我还想验证我的詹金斯容器奴隶是否可以生成僵尸。

由于我的图像运行scl二进制文件,而该二进制文件又启动 java JLNP 客户端,因此我在 jenkins 从属 groovy 脚本控制台中执行了以下操作:

def process=new ProcessBuilder("bash", '-c', 'sleep 10 </dev/null &>/dev/null & disown').redirectErrorStream(true).start()
println process.inputStream.text
println " ps -ef".execute().text
Run Code Online (Sandbox Code Playgroud)

僵尸已经产生。也就是说,scl最终的 PID 为 1。

然后我看了你的问题,决定尝试一下bash。我的第一次尝试是将 ENTRYPOINT 更改为:

bash -c "/usr/bin/scl enable rh-ror42 -- /usr/local/bin/run-jnlp-client $1 $2" --
Run Code Online (Sandbox Code Playgroud)

然后查看ps输出,我意识到 PID 1 不是bash,但实际上 PID 1 仍然是scl二进制文件。最后将命令更改为:

bash -c "/usr/bin/scl 启用 rh-ror42 -- /usr/local/bin/run-jnlp-client $1 $2 ; ls" --

那是在命令之后添加一些随机的第二个命令scl。瞧 - bash 变成了 PID 1 并且不再生成僵尸了。

查看您的示例,我发现您运行了bash -c多个命令。因此,在您的测试台上,您正在运行类似于我的最后一个命令的命令。但在您的工作容器中,您可能bash -c只使用一个命令运行,并且它看起来bash足够聪明,可以有效地执行exec. 并且可能在您生成僵尸的工作容器中,bash实际上 PID 1 并不与您的预期相反。

也许您可以ps -ef在现有的工作容器中验证我的猜测是否正确。