当前位置: 首页 > 知识库问答 >
问题:

Docker-初始化,僵尸-为什么这很重要?

岳浩
2023-03-14

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

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

总的来说,我有复杂的感觉。为什么这很重要?毕竟,即使是在conainer主机操作系统中的僵尸能够释放/杀死这个僵尸的情况下。我们知道容器中的进程是从主机OS正常进程的角度来看的(通常容器中的进程是带有一些名称空间和cGroup的正常进程)。

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

有人能解释一下吗?

共有2个答案

燕经国
2023-03-14

我在“docker中使用主管”中引用了那篇文章

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

这通常解决以下问题

我们不能使用docker start,因为我们需要传递端口映射和env vars。所以我们使用docker run
但是当upstart向docker run客户端进程发送SIGINT时,容器不会死,只是客户端死了。然后当upstart去启动它备份时,它已经在运行,端口映射失败。

或者这个问题:

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

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

段干靖
2023-03-14

有关init进程所提供内容的简短但有用的解释,请查看tini,这是Docker在指定--init时使用的内容。

使用Tini有几个好处:

  • 它可以保护您免受意外创建僵尸进程的软件的影响,这些进程可能(随着时间的推移!)使整个系统无法使用PID(并使其无法使用)。

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

Bash碰巧包含了一个进程收割机,因此在Bash-c下运行命令可以防止僵尸。Bash在默认情况下不会将信号作为PID 1处理,除非您trap捕获它们。

首先要了解的是init进程不会神奇地移除僵尸。(正常)init设计用于在未能等待僵尸的父进程退出且僵尸挂起时捕获僵尸。然后,init进程成为僵尸的父进程,它们可以被清理。

接下来,容器是在其自己的PID命名空间中运行的进程的cgroup。当容器停止时,此cgroup将被清除。容器中的所有僵尸将在停止时移除。它们无法到达主机init

第三是容器的使用方式不同。大多数都运行一个主进程,其他什么都不运行。如果有另一个进程产生,它通常是该主进程的子进程。所以直到父节点退出,僵尸才会存在。然后见第2点(僵尸将在集装箱出口被清除)。

在容器中运行Node.js、Go或Java应用程序服务器往往不会严重依赖于进程的分叉或派生。

运行类似于Jenkins worker的程序,生成大量涉及Shell的临时工作,可能会导致更糟糕的结果,但这是短暂的,因此定期退出并清理

运行Jenkins master也会生成作业。容器可能会挂起很长一段时间,并留下许多僵尸进程,如果没有僵尸收割机,这种工作负载可能会出现问题。

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

如果您可以在PID 1进程中安装SIGINTSIGTERM信号处理程序,那么初始化进程在这里不会增加太多。

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

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

sleep就是一个简单的例子。不能使用ctrl-c中断或停止docker run busybox sleep 60,默认的10秒后,docker stop超时将被终止<代码>docker运行--init busybox sleep 60按预期工作。

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

有关更多详细信息,请参阅github评论,它回答了tini创建者提出的“为什么?”问题。

 类似资料:
  • 问题内容: 维基百科说:“一个终止但从未被其父级等待的子进程变成了僵尸进程。” 我运行此程序: 这会创建一个僵尸进程,但我不明白为什么在这里创建了僵尸进程? 该程序的输出是 但是在这种情况下,为什么“子进程终止但没有被其父进程等待”呢? 问题答案: 在您的代码中,创建了僵尸(带有以下箭头的注释): 为什么?因为你从来没有上过。调用时,它将返回有关进程的事后信息,例如其退出代码。不幸的是,当进程退出

  • 问题:给您两个非空链表,表示两个非负整数。数字按相反顺序存储,每个节点包含一个数字。将这两个数字相加,并将其作为链表返回。 您可以假设这两个数字不包含任何前导零,除了数字0本身。 下面是我在JAVA中对这个问题的解决方案: 然而,为什么代码必须是 ,而不仅仅是:“listNode l3=new ListNode(0);”我知道这是错误的。如果我使用此代码,我的答案将返回[]。我不明白为什么会这样?

  • 为什么x没有在下面初始化? 平均而言,对于一半的迭代,for循环中的将是,从而初始化。对于另一半,找到的 循环替换为 同样糟糕。只有

  • 我想存储一组/值,但是s不一定是增量的,这意味着数据可以是: 因此,我试图创建一个c#等价的

  • 僵尸进程 当一个进程完成它的工作终止之后,它的父进程需要调用wait()或者waitpid()系统调用取得子进程的终止状态。 一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程。 理解了孤儿进程和僵尸进程,我们临时加了守护进程这一小节,守护进程就是后台进程吗?没那么简单。

  • 僵尸增量是一款放置游戏,你需要控制一群僵尸破坏小镇...