虽然我们可以在没有supervisor的情况下安装多个进程,但是当执行docker run时只能运行一个进程,停止容器时只有PID 1会收到信号,其他正在运行的进程不会优雅地停止。
是的,尽管这取决于您的主要进程如何运行(前台或后台),以及它如何收集子进程。这些详细信息在 "
Trapping signals in Docker containers" 中有详细介绍。
使用
docker stop
发送
SIGTERM
信号停止正在运行的容器,让主进程处理该信号,在优雅的期间使用
SIGKILL
终止应用程序。发送给容器的信号由正在运行的主进程(PID 1)处理。
如果应用程序在前台,也就是说,应用程序是容器中的主进程(PID1),则可以直接处理信号。
但是:
要发送信号的进程可能是后台进程,您不能直接发送任何信号。在这种情况下,一种解决方案是设置一个shell脚本作为入口点,并在该脚本中编排所有信号处理。
该问题在 "Docker和PID 1僵尸回收问题" 中有更详细的说明。
Unix被设计成父进程必须显式“等待”子进程终止,以收集其退出状态。使用waitpid()
系统调用族来执行此操作,直到父进程调用waitpid()处理子进程,才会消除僵尸进程。
调用waitpid()对子进程进行清理以消除其僵尸状态,称为“回收”。
init
进程-- PID 1 -- 有一个特殊任务。它的任务是“领养”孤儿子进程。
![https://blog.phusion.nl/wp-content/uploads/2015/01/adoption.png](https://istack.dev59.com/C594P.webp)
操作系统希望 init 进程也能够清理掉被收养的子进程。
Docker 的问题在于:
我们看到很多人只在容器中运行一个进程,并认为当他们运行这个单一进程时,就完成了任务。但是,最有可能的情况是,这个进程并没有像正常的 init 进程一样去处理被收养的进程,而是期望另一个 init 进程来完成该工作,这是合理的。
使用像
phusion/baseimage-docker
这样的镜像可以帮助管理一个(或多个)进程,同时保持主进程符合 init 标准。
它使用
runit
而不是 supervisord
来进行多进程管理。
{
Runit并不是用来解决收割问题的,而是为了支持多个进程。通过进程和用户隔离,我们鼓励使用多个进程来提高安全性。
Runit使用的内存比Supervisord少,因为Runit是用C语言编写的,而Supervisord是用Python编写的。
在某些情况下,容器中的进程重启优于整个容器的重启。
}
该图像包括一个my_init
脚本,它负责“收割”问题。
{
在baseimage-docker中,我们鼓励在单个容器中运行多个进程。不一定是多个服务。
一个逻辑服务可以由多个操作系统进程组成,并且我们提供了方便的工具来实现这一点。
}