docker中Supervisor的使用

Posted

技术标签:

【中文标题】docker中Supervisor的使用【英文标题】:Use of Supervisor in docker 【发布时间】:2016-01-12 01:17:35 【问题描述】:

我不是在询问是否在码头工人中使用主管,而只是想验证我的理解。

我了解 docker 在运行时会运行一个进程。另外,当我们需要在容器内运行多个进程时,使用supervisor。

我见过几个例子,其中一个容器从基础镜像启动并安装了几个服务,并且容器承诺形成一个新的镜像,所有这些都没有主管。

所以,我的基本疑问是这两种方法有什么区别。

我的理解是,当 docker 容器停止时,它会向 PID 1 的进程发送终止信号,PID 1 管理子进程并停止所有子进程,这正是主管所做的,而我们可以安装多个进程而无需当发出docker run时,supervisor只能运行一个进程,当容器停止时,只会发送PID 1信号,其他正在运行的进程不会被优雅地停止。

请确认我对使用supervisord的理解在多大程度上是正确的。

【问题讨论】:

2016 年 9 月更新:请参阅下面的 my new answer:docker 守护进程可以在 docker 1.12 中为您处理那些僵尸进程。 【参考方案1】:

虽然我们可以在没有supervisor的情况下安装多个进程,但发出docker run时只能运行一个进程,当容器停止时,只会发送PID 1信号,其他正在运行的进程不会被优雅地停止。

是的,尽管这取决于您的主进程如何运行(前台或后台),以及它如何收集子进程。

这就是“Trapping signals in Docker containers”中详述的内容

docker stop 通过发送SIGTERM 信号停止正在运行的容器,让主进程处理它,并在宽限期后使用SIGKILL 终止应用程序。

发送到容器的信号由正在运行的主进程 (PID 1) 处理。

如果应用程序在前台,即应用程序是容器(PID1)中的主进程,它可以直接处理信号。

但是:

要发出信号的进程可以是后台进程,您不能直接发送任何信号。在这种情况下,一种解决方案是设置一个 shell 脚本作为入口点,并在该脚本中编排所有信号处理。

“Docker and the PID 1 zombie reaping problem”中进一步详细说明了该问题

Unix 的设计方式是父进程必须显式“等待”子进程终止,以便收集其退出状态。僵尸进程一直存在,直到父进程使用waitpid() 系列系统调用执行此操作。

对子进程调用 waitpid() 以消除其僵尸的行为称为“收割”。

init 进程——PID 1——有一个特殊的任务。它的任务是“采用”孤立的子进程。

操作系统希望 init 进程也能获得收养的孩子。

Docker 的问题:

我们看到很多人在他们的容器中只运行一个进程,他们认为当他们运行这个单一进程时,他们就完成了。但很可能,这个进程并没有写入表现得像一个适当的初始化进程。 也就是说,它可能期望另一个 init 进程来完成这项工作,而不是正确地收获采用的流程,这是正确的。

使用像phusion/baseimage-docker 这样的图像有助于管理一个(或多个)进程,同时保持主进程的初始化兼容。

它使用runit instead of supervisord,用于多进程管理:

Runit 不是用来解决收割问题的。相反,它是为了支持多个进程。为安全起见,鼓励使用多个进程(通过进程和用户隔离)。 Runit 使用的内存比 Supervisord 少,因为 Runit 是用 C 编写的,而 Supervisord 是用 Python 编写的。 在某些用例中,容器中的进程重启比整个容器重启更可取。

该图片包含一个my_init script,它负责处理“收割”问题。

在 baseimage-docker 中,我们鼓励在单个容器中运行多个进程。但不一定是多种服务。 一个逻辑服务可以由多个操作系统进程组成,我们提供了可以轻松做到这一点的工具。

【讨论】:

感谢您的详尽回答。我正在尝试 phusion 图像,据我所知,每当容器启动时,它都会运行 /etc/init.d 中的任何内容。但是,我在 init.d 中有一项服务没有在容器启动时启动。你能帮忙吗? 当然:您能提出一个新问题,并提供新设置的详细信息吗?这样,我(可能还有其他人)可以看看。 哦..我的错误是 /etc/my_init.d 这里的关键点是phusion/baseimage-docker【参考方案2】:

2016 年 9 月更新 docker 1.12(2016 年第四季度/2017 年第一季度)

Arnaud Porterie 只是twitted:

[?] 刚刚合并:与docker run --init,Rick Grimes 将照顾你所有的僵尸。

(commit eabae09)

参见PR 26061:“为僵尸战斗和信号处理添加初始化进程”(和PR 26736)

这添加了一个用于对抗僵尸的小型 C 二进制文件。它安装在 /dev/init 并添加到用户指定的参数前面。你 通过守护进程标志 dockerd --init 启用它,因为它被禁用 默认为向后兼容。

您还可以覆盖守护程序选项或在每个 docker run --init=true|false 的容器基础。

您可以通过将类似这样的进程作为 pid 1 在 容器并查看容器中出现的额外僵尸 正在运行。

int main(int argc, char ** argv) 
    pid_t pid = fork();
    if (pid == 0) 
        pid = fork();
        if (pid == 0) 
            exit(0);
        
        sleep(3);
        exit(0);
    
    printf("got pid %d and exited\n", pid);
    sleep(20);

docker daemon 现在有选项

--init

在容器内运行一个 init 来转发信号和获取进程

【讨论】:

【参考方案3】:

Docker 文档中的这篇文章展示了一个运行多个进程并同时使用 supervisord 的示例。

https://docs.docker.com/config/containers/multi-service_container/

我的工作正常,但我们可能会简单地将工作进程卸载到另一个容器中,并且每个容器中只处理一个进程。 在这一点上感觉像是一种更简单的方法。

【讨论】:

以上是关于docker中Supervisor的使用的主要内容,如果未能解决你的问题,请参考以下文章

Docker使用 Supervisor 来管理进程

docker之使用supervisor管理多个进程

supervisor 使用实践

docker supervisor管理进程

Docker Supervisor

docker技术剖析--docker supervisor管理进程