Docker - init,僵尸 - 为什么重要?

时间:2018-03-07 22:35:44

标签: docker

我读过这篇文章:https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/

设置一些上下文:文章是关于容器中僵尸的问题,它试图说服我们这是一个真正的问题。

一般来说,我的感情很复杂。为什么这有关系 ?毕竟,即使万一在变装主机操作系统中的僵尸能够释放/杀死这个僵尸。我们知道容器中的进程是从主机操作系统正常进程的角度来看的(并且通常在容器中的进程是具有一些命名空间和cgroup的正常进程)。

此外,我们还可以找到信息,为了避免僵尸问题,我们应该使用bash -c ...。为什么?也许,更好的选择是使用--init

有人可以尝试解释这些事吗?

2 个答案:

答案 0 :(得分:8)

有关init进程为您提供的简短但有用的解释,请查看tini,这是Docker在您指定--init

时使用的内容
  

使用Tini有几个好处:

     
      
  • 它可以保护您免受意外创建僵尸进程的软件的攻击,这些软件可能(随着时间推移!)使您的整个系统陷入困境   (并使其无法使用)。
  •   
  • 确保默认信号处理程序适用于您在Docker镜像中运行的软件。例如,使用Tini,SIGTERM正常   即使您没有明确安装信号,也会终止您的流程   处理程序。
  •   

这些问题都会影响容器。容器中的进程仍然是主机上的进程,因此它占用主机上的PID。无论你在容器中运行什么是PID 1,这意味着它必须安装一个信号处理程序来获取该信号。

Bash恰好包含了一个进程收割器,因此在bash -c下运行命令可以防止僵尸。除非你trap,否则Bash默认不会将信号处理为PID 1。

僵尸

要理解的第一件事是init进程并没有神奇地移除僵尸。 A(普通)init旨在收获僵尸,当无法等待它们的父进程退出并且僵尸徘徊时。然后,init进程成为僵尸父进程,可以清除它们。

接下来是Docker容器是它们自己的PID命名空间中的一组进程。当容器停止时,该cgroup被清理。在stop上删除容器中的所有僵尸。他们无法联系到主持人init

第三是Docker容器的使用方式。大多数运行一个主要过程,没有别的。如果产生了另一个进程,它通常是该主进程的子进程。所以在父母退出之前,僵尸将存在。然后看第2点。

在容器中运行Node.js,Go或Java app服务器往往不会过分依赖于分叉或产生进程。

运行类似于Jenkins工作程序的东西会产生大量涉及shell的临时作业,这会导致更糟糕的情况。

信号

init进程可以提供的另一个角色是安装信号处理程序,以便从主机发送的信号可以传递到容器进程。 PID 1有点特殊,因为它需要进程监听接收信号。

如果您可以在PID 1进程中安装SIGINTSIGTERM信号处理程序,那么init进程在此处不会添加太多内容。

何时使用init

如果要在容器中运行多个服务

应在init进程下运行多个进程。当Docker启动时,init管理它们应该如何启动。容器实际上需要什么才能运行"对于它所代表的服务。当容器停止时,应该如何将其传递到每个进程。您可能需要更传统的init系统,s6 via s6-overlay为多进程管理提供了许多有用的容器功能。

当您运行产生大量子进程的单个进程时

特别是当过程是孩子或更远的孩子时。 CI工作者(如Jenkins)示例是第一个想到Java生成命令或生成其他命令的shell的例子。

当您无法将信号处理程序添加到以PID 1运行的进程时。

sleep就是一个简单的例子。 docker run busybox sleep 60无法用ctrl-c中断或停止,它将在默认的10秒docker stop超时后被终止。 docker run --init busybox sleep 60按预期工作。

每当

tini是非常小的开销并且被广泛使用,所以为什么不在大多数时间使用--init

有关详细信息,请参阅this github comment,其中回答了"为什么?"来自蒂尼的创造者的问题。

答案 1 :(得分:1)

我在" Use of Supervisor in docker"

中引用了该文章

自2016年9月和docker 1.12以来,docker run --init正在通过添加init进程帮助对抗僵尸进程。

通常解决following issue

  

我们无法使用docker start,因为我们需要传递端口映射和env变量等内容。所以我们使用docker run   但是当upstart向SIGINT客户端进程发送docker run时,容器不会死,只有客户端才会死。然后当upstart开始备份时,它已经运行,并且端口映射失败。

this issue

  

在执行脚本中生成子进程时,Docker似乎挂起了。

基本上,您希望docker容器终止所有子进程,以便清理所述子进程使用的资源(端口,文件处理程序......)。