dumb-init is a simple process supervisor and init system designed to run asPID 1 inside minimal container environments (such as Docker). It isdeployed as a small, statically-linked binary written in C.
Lightweight containers have popularized the idea of running a single process orservice without normal init systems like systemd orsysvinit. However, omitting an init system often leads to incorrecthandling of processes and signals, and can result in problems such ascontainers which can't be gracefully stopped, or leaking containers whichshould have been destroyed.
dumb-init
enables you to simply prefix your command with dumb-init
. It actsas PID 1 and immediately spawns your command as a child process, taking care toproperly handle and forward signals as they are received.
Normally, when you launch a Docker container, the process you're executingbecomes PID 1, giving it the quirks and responsibilities that come with beingthe init system for the container.
There are two common issues this presents:
In most cases, signals won't be handled properly.
The Linux kernel applies special signal handling to processes which run asPID 1.
When processes are sent a signal on a normal Linux system, the kernel willfirst check for any custom handlers the process has registered for thatsignal, and otherwise fall back to default behavior (for example, killingthe process on SIGTERM
).
However, if the process receiving the signal is PID 1, it gets specialtreatment by the kernel; if it hasn't registered a handler for the signal,the kernel won't fall back to default behavior, and nothing happens. Inother words, if your process doesn't explicitly handle these signals,sending it SIGTERM
will have no effect at all.
A common example is CI jobs that do docker run my-container script
: sendingSIGTERM
to the docker run
process will typically kill the docker run
command,but leave the container running in the background.
Orphaned zombie processes aren't properly reaped.
A process becomes a zombie when it exits, and remains a zombie until itsparent calls some variation of the wait()
system call on it. It remains inthe process table as a "defunct" process. Typically, a parent process willcall wait()
immediately and avoid long-living zombies.
If a parent exits before its child, the child is "orphaned", and isre-parented under PID 1. The init system is thus responsible forwait()
-ing on orphaned zombie processes.
Of course, most processes won't wait()
on random processes that happento become attached to them, so containers often end with dozens of zombiesrooted at PID 1.
dumb-init
doesdumb-init
runs as PID 1, acting like a simple init system. It launches asingle process and then proxies all received signals to a session rooted atthat child process.
Since your actual process is no longer PID 1, when it receives signals fromdumb-init
, the default signal handlers will be applied, and your process willbehave as you would expect. If your process dies, dumb-init
will also die,taking care to clean up any other processes that might still remain.
In its default mode, dumb-init
establishes asession rooted at thechild, and sends signals to the entire process group. This is useful if youhave a poorly-behaving child (such as a shell script) which won't normallysignal its children before dying.
This can actually be useful outside of Docker containers in regular processsupervisors like daemontools or supervisord forsupervising shell scripts. Normally, a signal like SIGTERM
received by ashell isn't forwarded to subprocesses; instead, only the shell process dies.With dumb-init, you can just write shell scripts with dumb-init in the shebang:
#!/usr/bin/dumb-init /bin/sh
my-web-server & # launch a process in the background
my-other-server # launch another process in the foreground
Ordinarily, a SIGTERM
sent to the shell would kill the shell but leave thoseprocesses running (both the background and foreground!). With dumb-init, yoursubprocesses will receive the same signals your shell does.
If you'd like for signals to only be sent to the direct child, you can run withthe --single-child
argument, or set the environment variableDUMB_INIT_SETSID=0
when running dumb-init
. In this mode, dumb-init iscompletely transparent; you can even string multiple together (like dumb-init dumb-init echo 'oh, hi'
).
dumb-init allows rewriting incoming signals before proxying them. This isuseful in cases where you have a Docker supervisor (like Mesos or Kubernetes)which always sends a standard signal (e.g. SIGTERM). Some apps require adifferent stop signal in order to do graceful cleanup.
For example, to rewrite the signal SIGTERM (number 15) to SIGQUIT (number 3),just add --rewrite 15:3
on the command line.
To drop a signal entirely, you can rewrite it to the special number 0
.
When running in setsid mode, it is not sufficient to forwardSIGTSTP
/SIGTTIN
/SIGTTOU
in most cases, since if the process has not addeda custom signal handler for these signals, then the kernel will not applydefault signal handling behavior (which would be suspending the process) sinceit is a member of an orphaned process group. For this reason, we set defaultrewrites to SIGSTOP
from those three signals. You can opt out of thisbehavior by rewriting the signals back to their original values, if desired.
One caveat with this feature: for job control signals (SIGTSTP
, SIGTTIN
,SIGTTOU
), dumb-init will always suspend itself after receiving the signal,even if you rewrite it to something else.
You have a few options for using dumb-init
:
Many popular Linux distributions (including Debian (since stretch
) and Debianderivatives such as Ubuntu (since bionic
)) now contain dumb-init packages intheir official repositories.
On Debian-based distributions, you can run apt install dumb-init
to installdumb-init, just like you'd install any other package.
Note: Most distro-provided versions of dumb-init are not statically-linked,unlike the versions we provide (see the other options below). This is normallyperfectly fine, but means that these versions of dumb-init generally won't workwhen copied to other Linux distros, unlike the statically-linked versions weprovide.
If you have an internal apt server, uploading the .deb
to your server is therecommended way to use dumb-init
. In your Dockerfiles, you can simplyapt install dumb-init
and it will be available.
Debian packages are available from the GitHub Releases tab, oryou can run make builddeb
yourself.
.deb
package manually (Debian/Ubuntu)If you don't have an internal apt server, you can use dpkg -i
to install the.deb
package. You can choose how you get the .deb
onto your container(mounting a directory or wget
-ing it are some options).
One possibility is with the following commands in your Dockerfile:
RUN wget https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_amd64.deb
RUN dpkg -i dumb-init_*.deb
Since dumb-init is released as a statically-linked binary, you can usually justplop it into your images. Here's an example of doing that in a Dockerfile:
RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.5/dumb-init_1.2.5_x86_64
RUN chmod +x /usr/local/bin/dumb-init
Though dumb-init
is written entirely in C, we also provide a Python packagewhich compiles and installs the binary. It can be installed fromPyPI using pip
. You'll want to firstinstall a C compiler (on Debian/Ubuntu, apt-get install gcc
is sufficient),then just pip install dumb-init
.
As of 1.2.0, the package at PyPI is available as a pre-built wheel archive and does notneed to be compiled on common Linux distributions.
Once installed inside your Docker container, simply prefix your commands withdumb-init
(and make sure that you're using the recommended JSONsyntax).
Within a Dockerfile, it's a good practice to use dumb-init as your container'sentrypoint. An "entrypoint" is a partial command that gets prepended to yourCMD
instruction, making it a great fit for dumb-init:
# Runs "/usr/bin/dumb-init -- /my/script --with --args"
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
# or if you use --rewrite or other cli flags
# ENTRYPOINT ["dumb-init", "--rewrite", "2:3", "--"]
CMD ["/my/script", "--with", "--args"]
If you declare an entrypoint in a base image, any images that descend from itdon't need to also declare dumb-init. They can just set a CMD
as usual.
For interactive one-off usage, you can just prepend it manually:
$ docker run my_container dumb-init python -c 'while True: pass'
Running this same command without dumb-init
would result in being unable tostop the container without SIGKILL
, but with dumb-init
, you can send itmore humane signals like SIGTERM
.
It's important that you use the JSON syntax for CMD
andENTRYPOINT
. Otherwise, Docker invokes a shell to run your command, resultingin the shell as PID 1 instead of dumb-init.
Often containers want to do some pre-start work which can't be done duringbuild time. For example, you might want to template out some config files basedon environment variables.
The best way to integrate that with dumb-init is like this:
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["bash", "-c", "do-some-pre-start-thing && exec my-server"]
By still using dumb-init as the entrypoint, you always have a proper initsystem in place.
The exec
portion of the bash command is important because it replaces thebash process with your server, so that the shell only exists momentarilyat start.
Building the dumb-init binary requires a working compiler and libc headers anddefaults to glibc.
$ make
Statically compiled dumb-init is over 700KB due to glibc, but musl is now anoption. On Debian/Ubuntu apt-get install musl-tools
to install the source andwrappers, then just:
$ CC=musl-gcc make
When statically compiled with musl the binary size is around 20KB.
We use the standard Debian conventions for specifying build dependencies (lookin debian/control
). An easy way to get started is to apt-get install build-essential devscripts equivs
, and then sudo mk-build-deps -i --remove
to install all of the missing build dependencies automatically. You can thenuse make builddeb
to build dumb-init Debian packages.
If you prefer an automated Debian package build using Docker, just run make builddeb-docker
. This is easier, but requires you to have Docker running onyour machine.
容器化环境中,往往直接运行应用程序,而缺少初始化系统(如systemd、sysvinit等)。这可能需要应用程序来处理系统信号,接管子进程,进而导致容器无法停止、产生僵尸进程等问题。dumb-init旨在模拟初始化系统功能,避免上述问题的发生。 容器化环境中,往往直接运行应用程序,而缺少初始化系统(如systemd、sysvinit等)。这可能需要应用程序来处理系统信号,接管子进程,进而导致容器无
本文讲的是Dumb-Init进程信号处理【编者的话】随着Docker及Kubernetes技术发展的越来越成熟稳定,越来越多的公司开始将Docker用于生产环境的部署,相比起物理机上直接部署,多了一层Docker容器的环境,这就带来一个问题:进程信号接收与处理。 【深圳站|3天烧脑式Kubernetes训练营】培训内容包括:Kubernetes概述、架构、日志和监控,部署、自动驾驶、服务发现、网络
現在越來越多環境都跑在 Docker 環境下,但不知道你是否有遇過想要停止容器,但執行 docker stop 之後卻無法立即停止的狀況?這個問題會牽扯到我在 當 .NET Core 執行在 Linux 或 Docker 容器中如何優雅的結束 文章中提到的 訊號(Signal) 是如何傳送到容器的程序。這篇文章我將更深入的探討這個問題,以及提供解決方法。 關於容器與訊號的關係 當你在執行 Dock
一、前言 ● 容器中部署的时候往往都是直接运行二进制文件或命令,这样对于容器的作用更加直观,但是也会出现新的问题,比如子进程的资源回收、释放、托管等,处理不好,便会成为可怕的僵尸进程 ● 本文主要讨论一下docker容器中进程之间信号处理以及对进程管理的问题 二、环境准备 组件 版本 OS Ubuntu 18.04.1 LTS docker 18.06.0-ce 三、测试脚本 首先准备一个测试脚本
官方README.md文档给出的定义:dumb-init is a simple process supervisor and init system designed to run as PID 1 inside minimal container environments (such as Docker). (dumb-init是一个简单的进程管理器和初始化系统,旨在在最小的容器环境(例如Do
airflow-init_1 | ERROR! Maximum number of retries (20) reached. airflow-init_1 | airflow-init_1 | Last check result: airflow-init_1 | $ airflow db check airflow-init_1 |