在 docker 中使用 Supervisor

Use of Supervisor in docker

我不是在询问有关 docker 的 supervisor 的使用,只是想让我的理解得到验证。

我知道 docker 运行 是一个进程,但它是 运行。此外,当我们需要 运行 容器内的多个进程时,会使用 supervisor。

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

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

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

请确认我对使用 supervisord 的理解是否正确。

while we can install multiple process without supervisor, only one process can be run when docker run is issued and when container is stopped only the PID 1 will be sent signals and other running process will not be stopped gracefully.

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

这就是“Trapping signals in Docker containers

中详述的内容

docker stop stops a running container by sending it SIGTERM signal, let the main process process it, and after a grace period uses SIGKILL to terminate the application.

The signal sent to container is handled by the main process that is running (PID 1).

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

但是:

The process to be signaled could be the background one and you cannot send any signals directly. In this case one solution is to set up a shell-script as the entrypoint and orchestrate all signal processing in that script.

此问题在“Docker and the PID 1 zombie reaping problem

中有进一步详细说明

Unix is designed in such a way that parent processes must explicitly "wait" for child process termination, in order to collect its exit status. The zombie process exists until the parent process has performed this action, using the waitpid() family of system calls.

The action of calling waitpid() on a child process in order to eliminate its zombie, is called "reaping".

The init process -- PID 1 -- has a special task. Its task is to "adopt" orphaned child processes.

The operating system expects the init process to reap adopted children too.

Docker 的问题:

We see that a lot of people run only one process in their container, and they think that when they run this single process, they're done.
But most likely, this process is not written to behave like a proper init process.
That is, instead of properly reaping adopted processes, it's probably expecting another init process to do that job, and rightly so.

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

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

Runit is not there to solve the reaping problem. Rather, it's to support multiple processes. Multiple processes are encouraged for security (through process and user isolation).
Runit uses less memory than Supervisord because Runit is written in C and Supervisord in Python.
And in some use cases, process restarts in the container are preferable over whole-container restarts.

该图像包含一个 my_init script 来处理“收割”问题。

In baseimage-docker, we encourage running multiple processes in a single container. Not necessarily multiple services though.
A logical service can consist of multiple OS processes, and we provide the facilities to easily do that.

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

Arnaud Porterie just twitted:

[] Just merged: with docker run --init, Rick Grimes will take care of all your zombies.

(commit eabae09)

参见 PR 26061: "Add init process for zombie fighting and signal handling" (and PR 26736)

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

You can also override the daemon option or specify this on a per container basis with docker run --init=true|false.

You can test this by running a process like this as the pid 1 in a container and see the extra zombie that appears in the container as it is running.

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

Run an init inside containers to forward signals and reap processes

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

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

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