2015-08-07 00:24:27 +03:00
|
|
|
dumb-init
|
|
|
|
========
|
|
|
|
|
2015-10-09 23:23:17 +03:00
|
|
|
[![Circle CI](https://circleci.com/gh/Yelp/dumb-init.svg?style=svg)](https://circleci.com/gh/Yelp/dumb-init)
|
|
|
|
[![PyPI version](https://badge.fury.io/py/dumb-init.svg)](https://pypi.python.org/pypi/dumb-init)
|
2015-08-14 08:10:55 +03:00
|
|
|
|
|
|
|
|
2016-07-16 08:25:01 +03:00
|
|
|
**dumb-init** is a simple process supervisor and init system designed to run as
|
2016-10-12 11:02:55 +03:00
|
|
|
PID 1 inside minimal container environments (such as [Docker][docker]). It is
|
2015-10-09 23:23:17 +03:00
|
|
|
deployed as a small, statically-linked binary written in C.
|
2015-08-07 00:24:27 +03:00
|
|
|
|
2015-10-09 23:23:17 +03:00
|
|
|
Lightweight containers have popularized the idea of running a single process or
|
|
|
|
service without normal init systems like [systemd][systemd] or
|
|
|
|
[sysvinit][sysvinit]. However, omitting an init system often leads to incorrect
|
|
|
|
handling of processes and signals, and can result in problems such as
|
|
|
|
containers which can't be gracefully stopped, or leaking containers which
|
|
|
|
should have been destroyed.
|
2015-08-07 00:24:27 +03:00
|
|
|
|
2015-10-09 23:23:17 +03:00
|
|
|
`dumb-init` enables you to simply prefix your command with `dumb-init`. It acts
|
|
|
|
as PID 1 and immediately spawns your command as a child process, taking care to
|
|
|
|
properly handle and forward signals as they are received.
|
2015-08-07 00:24:27 +03:00
|
|
|
|
|
|
|
|
2015-10-09 23:23:17 +03:00
|
|
|
## Why you need an init system
|
2015-08-07 00:24:27 +03:00
|
|
|
|
2015-10-09 23:23:17 +03:00
|
|
|
Normally, when you launch a Docker container, the process you're executing
|
|
|
|
becomes PID 1, giving it the quirks and responsibilities that come with being
|
|
|
|
the init system for the container.
|
2015-08-07 00:24:27 +03:00
|
|
|
|
2015-10-09 23:23:17 +03:00
|
|
|
There are two common issues this presents:
|
|
|
|
|
|
|
|
1. In most cases, signals won't be handled properly.
|
|
|
|
|
|
|
|
The Linux kernel applies special signal handling to processes which run as
|
|
|
|
PID 1.
|
|
|
|
|
|
|
|
When processes are sent a signal on a normal Linux system, the kernel will
|
|
|
|
first check for any custom handlers the process has registered for that
|
|
|
|
signal, and otherwise fall back to default behavior (for example, killing
|
|
|
|
the process on `SIGTERM`).
|
|
|
|
|
|
|
|
However, if the process receiving the signal is PID 1, it gets special
|
|
|
|
treatment 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. In
|
|
|
|
other 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`: sending
|
|
|
|
`SIGTERM` to the `docker run` process will typically kill the `docker run` command,
|
|
|
|
but leave the container running in the background.
|
|
|
|
|
|
|
|
2. Orphaned zombie processes aren't properly reaped.
|
|
|
|
|
|
|
|
A process becomes a zombie when it exits, and remains a zombie until its
|
|
|
|
parent calls some variation of the `wait()` system call on it. It remains in
|
|
|
|
the process table as a "defunct" process. Typically, a parent process will
|
|
|
|
call `wait()` immediately and avoid long-living zombies.
|
|
|
|
|
|
|
|
If a parent exits before its child, the child is "orphaned", and is
|
|
|
|
re-parented under PID 1. The init system is thus responsible for
|
|
|
|
`wait()`-ing on orphaned zombie processes.
|
|
|
|
|
|
|
|
Of course, most processes *won't* `wait()` on random processes that happen
|
2015-10-16 00:36:38 +03:00
|
|
|
to become attached to them, so containers often end with dozens of zombies
|
2015-10-09 23:23:17 +03:00
|
|
|
rooted at PID 1.
|
2015-08-07 00:24:27 +03:00
|
|
|
|
|
|
|
|
|
|
|
## What `dumb-init` does
|
|
|
|
|
|
|
|
`dumb-init` runs as PID 1, acting like a simple init system. It launches a
|
2015-10-09 23:23:17 +03:00
|
|
|
single process and then proxies all received signals to a session rooted at
|
|
|
|
that child process.
|
2015-08-07 00:24:27 +03:00
|
|
|
|
|
|
|
Since your actual process is no longer PID 1, when it receives signals from
|
|
|
|
`dumb-init`, the default signal handlers will be applied, and your process will
|
2015-10-09 23:23:17 +03:00
|
|
|
behave 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.
|
2015-09-04 20:26:47 +03:00
|
|
|
|
|
|
|
|
2015-10-09 23:23:17 +03:00
|
|
|
### Session behavior
|
2015-09-04 20:26:47 +03:00
|
|
|
|
2015-10-09 23:23:17 +03:00
|
|
|
In its default mode, `dumb-init` establishes a
|
2015-10-29 09:26:13 +03:00
|
|
|
[session](http://man7.org/linux/man-pages/man2/setsid.2.html) rooted at the
|
2015-10-09 23:23:17 +03:00
|
|
|
child, and sends signals to the entire process group. This is useful if you
|
2015-09-04 20:26:47 +03:00
|
|
|
have a poorly-behaving child (such as a shell script) which won't normally
|
|
|
|
signal its children before dying.
|
|
|
|
|
|
|
|
This can actually be useful outside of Docker containers in regular process
|
|
|
|
supervisors like [daemontools][daemontools] or [supervisord][supervisord] for
|
2015-10-09 23:23:17 +03:00
|
|
|
supervising shell scripts. Normally, a signal like `SIGTERM` received by a
|
|
|
|
shell 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:
|
2015-09-04 20:26:47 +03:00
|
|
|
|
|
|
|
#!/usr/bin/dumb-init /bin/sh
|
|
|
|
my-web-server & # launch a process in the background
|
|
|
|
my-other-server # launch another process in the foreground
|
2015-08-07 00:24:27 +03:00
|
|
|
|
2015-10-09 23:23:17 +03:00
|
|
|
Ordinarily, a `SIGTERM` sent to the shell would kill the shell but leave those
|
|
|
|
processes running (both the background and foreground!). With dumb-init, your
|
|
|
|
subprocesses will receive the same signals your shell does.
|
2015-09-04 20:26:47 +03:00
|
|
|
|
2015-10-09 23:23:17 +03:00
|
|
|
If you'd like for signals to only be sent to the direct child, you can run with
|
|
|
|
the `--single-child` argument, or set the environment variable
|
|
|
|
`DUMB_INIT_SETSID=0` when running `dumb-init`. In this mode, dumb-init is
|
|
|
|
completely transparent; you can even string multiple together (like `dumb-init
|
|
|
|
dumb-init echo 'oh, hi'`).
|
2015-08-07 00:24:27 +03:00
|
|
|
|
|
|
|
|
2016-06-14 00:28:57 +03:00
|
|
|
### Signal rewriting
|
|
|
|
|
|
|
|
dumb-init allows rewriting incoming signals before proxying them. This is
|
2017-01-16 15:45:55 +03:00
|
|
|
useful in cases where you have a Docker supervisor (like Mesos or Kubernetes)
|
2016-06-14 00:28:57 +03:00
|
|
|
which always sends a standard signal (e.g. SIGTERM). Some apps require a
|
|
|
|
different 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.
|
|
|
|
|
2016-06-14 21:34:52 +03:00
|
|
|
To drop a signal entirely, you can rewrite it to the special number `0`.
|
|
|
|
|
2016-06-14 20:54:03 +03:00
|
|
|
|
|
|
|
#### Signal rewriting special case
|
|
|
|
|
|
|
|
When running in setsid mode, it is not sufficient to forward
|
|
|
|
`SIGTSTP`/`SIGTTIN`/`SIGTTOU` in most cases, since if the process has not added
|
|
|
|
a custom signal handler for these signals, then the kernel will not apply
|
|
|
|
default signal handling behavior (which would be suspending the process) since
|
|
|
|
it is a member of an orphaned process group. For this reason, we set default
|
|
|
|
rewrites to `SIGSTOP` from those three signals. You can opt out of this
|
|
|
|
behavior 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.
|
2016-06-14 00:28:57 +03:00
|
|
|
|
|
|
|
|
2015-08-07 00:24:27 +03:00
|
|
|
## Installing inside Docker containers
|
|
|
|
|
|
|
|
You have a few options for using `dumb-init`:
|
|
|
|
|
|
|
|
|
2015-10-09 23:23:17 +03:00
|
|
|
### Option 1: Installing via an internal apt server (Debian/Ubuntu)
|
2015-08-07 00:24:27 +03:00
|
|
|
|
|
|
|
If you have an internal apt server, uploading the `.deb` to your server is the
|
|
|
|
recommended way to use `dumb-init`. In your Dockerfiles, you can simply
|
|
|
|
`apt-get install dumb-init` and it will be available.
|
|
|
|
|
2015-09-04 20:26:47 +03:00
|
|
|
Debian packages are available from the [GitHub Releases tab][gh-releases], or
|
|
|
|
you can run `make builddeb` yourself.
|
|
|
|
|
2015-08-07 00:24:27 +03:00
|
|
|
|
2015-10-09 23:23:17 +03:00
|
|
|
### Option 2: Installing the `.deb` package manually (Debian/Ubuntu)
|
2015-08-07 00:24:27 +03:00
|
|
|
|
|
|
|
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).
|
|
|
|
|
2015-09-04 20:26:47 +03:00
|
|
|
One possibility is with the following commands in your Dockerfile:
|
|
|
|
|
2016-07-28 23:53:25 +03:00
|
|
|
```Dockerfile
|
2017-12-01 21:21:45 +03:00
|
|
|
RUN wget https://github.com/Yelp/dumb-init/releases/download/v1.2.1/dumb-init_1.2.1_amd64.deb
|
2015-09-04 20:26:47 +03:00
|
|
|
RUN dpkg -i dumb-init_*.deb
|
|
|
|
```
|
|
|
|
|
2015-08-07 00:24:27 +03:00
|
|
|
|
2016-01-07 21:59:44 +03:00
|
|
|
### Option 3: Downloading the binary directly
|
|
|
|
|
|
|
|
Since dumb-init is released as a statically-linked binary, you can usually just
|
|
|
|
plop it into your images. Here's an example of doing that in a Dockerfile:
|
|
|
|
|
2016-07-28 23:53:25 +03:00
|
|
|
```Dockerfile
|
2017-12-01 21:21:45 +03:00
|
|
|
RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.1/dumb-init_1.2.1_amd64
|
2016-01-07 21:59:44 +03:00
|
|
|
RUN chmod +x /usr/local/bin/dumb-init
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Option 4: Installing from PyPI
|
2015-08-12 01:29:07 +03:00
|
|
|
|
2015-10-09 23:23:17 +03:00
|
|
|
Though `dumb-init` is written entirely in C, we also provide a Python package
|
|
|
|
which compiles and installs the binary. It can be installed [from
|
2017-06-27 02:29:47 +03:00
|
|
|
PyPI](https://pypi.python.org/pypi/dumb-init) using `pip`. You'll want to first
|
2015-10-09 23:23:17 +03:00
|
|
|
install a C compiler (on Debian/Ubuntu, `apt-get install gcc` is sufficient),
|
|
|
|
then just `pip install dumb-init`.
|
2015-08-12 01:29:07 +03:00
|
|
|
|
2017-06-27 02:56:38 +03:00
|
|
|
As of 1.2.0, the package at PyPI is available as a pre-built wheel archive and does not
|
2017-12-01 21:21:45 +03:00
|
|
|
need to be compiled on common Linux distributions.
|
2017-06-27 02:29:47 +03:00
|
|
|
|
2015-08-12 01:29:07 +03:00
|
|
|
|
2015-08-07 00:24:27 +03:00
|
|
|
## Usage
|
|
|
|
|
|
|
|
Once installed inside your Docker container, simply prefix your commands with
|
2016-08-16 00:14:07 +03:00
|
|
|
`dumb-init` (and make sure that you're using [the recommended JSON
|
|
|
|
syntax][docker-cmd-json]).
|
2016-07-26 08:02:24 +03:00
|
|
|
|
|
|
|
Within a Dockerfile, it's a good practice to use dumb-init as your container's
|
|
|
|
entrypoint. An "entrypoint" is a partial command that gets prepended to your
|
|
|
|
`CMD` instruction, making it a great fit for dumb-init:
|
|
|
|
|
2016-07-28 23:53:25 +03:00
|
|
|
```Dockerfile
|
2016-07-26 08:02:24 +03:00
|
|
|
# Runs "/usr/bin/dumb-init -- /my/script --with --args"
|
|
|
|
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
|
2016-12-13 17:16:21 +03:00
|
|
|
|
|
|
|
# or if you use --rewrite or other cli flags
|
|
|
|
# ENTRYPOINT ["dumb-init", "--rewrite", "2:3", "--"]
|
|
|
|
|
2016-07-26 08:02:24 +03:00
|
|
|
CMD ["/my/script", "--with", "--args"]
|
|
|
|
```
|
|
|
|
|
|
|
|
If you declare an entrypoint in a base image, any images that descend from it
|
|
|
|
don'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:
|
2015-08-07 00:24:27 +03:00
|
|
|
|
|
|
|
$ docker run my_container dumb-init python -c 'while True: pass'
|
|
|
|
|
|
|
|
Running this same command without `dumb-init` would result in being unable to
|
2015-10-09 23:23:17 +03:00
|
|
|
stop the container without `SIGKILL`, but with `dumb-init`, you can send it
|
|
|
|
more humane signals like `SIGTERM`.
|
2015-08-07 00:24:27 +03:00
|
|
|
|
2016-08-16 00:14:07 +03:00
|
|
|
It's important that you use [the JSON syntax][docker-cmd-json] for `CMD` and
|
|
|
|
`ENTRYPOINT`. Otherwise, Docker invokes a shell to run your command, resulting
|
|
|
|
in the shell as PID 1 instead of dumb-init.
|
|
|
|
|
2016-05-20 19:50:59 +03:00
|
|
|
|
2017-05-09 21:38:32 +03:00
|
|
|
### Using a shell for pre-start hooks
|
|
|
|
|
|
|
|
Often containers want to do some pre-start work which can't be done during
|
|
|
|
build time. For example, you might want to template out some config files based
|
|
|
|
on environment variables.
|
|
|
|
|
|
|
|
The best way to integrate that with dumb-init is like this:
|
|
|
|
|
|
|
|
```Dockerfile
|
|
|
|
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 init
|
|
|
|
system in place.
|
|
|
|
|
|
|
|
The `exec` portion of the bash command is important because it [replaces the
|
|
|
|
bash process][exec] with your server, so that the shell only exists momentarily
|
|
|
|
at start.
|
|
|
|
|
|
|
|
|
2016-01-07 21:45:32 +03:00
|
|
|
## Building dumb-init
|
|
|
|
|
2016-05-20 19:50:59 +03:00
|
|
|
Building the dumb-init binary requires a working compiler and libc headers and
|
|
|
|
defaults to glibc.
|
2016-01-07 21:45:32 +03:00
|
|
|
|
|
|
|
$ make
|
|
|
|
|
|
|
|
|
2016-05-20 19:50:59 +03:00
|
|
|
### Building with musl
|
|
|
|
|
|
|
|
Statically compiled dumb-init is over 700KB due to glibc, but musl is now an
|
|
|
|
option. On Debian/Ubuntu `apt-get install musl-tools` to install the source and
|
|
|
|
wrappers, then just:
|
2016-01-07 21:45:32 +03:00
|
|
|
|
|
|
|
$ CC=musl-gcc make
|
|
|
|
|
|
|
|
When statically compiled with musl the binary size is around 20KB.
|
2015-08-07 00:24:27 +03:00
|
|
|
|
2016-05-20 19:50:59 +03:00
|
|
|
|
|
|
|
### Building the Debian package
|
|
|
|
|
|
|
|
We use the standard Debian conventions for specifying build dependencies (look
|
|
|
|
in `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 then
|
|
|
|
use `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 on
|
|
|
|
your machine.
|
|
|
|
|
|
|
|
|
2015-08-07 00:24:27 +03:00
|
|
|
## See also
|
|
|
|
|
|
|
|
* [Docker and the PID 1 zombie reaping problem (Phusion Blog)](https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/)
|
|
|
|
* [Trapping signals in Docker containers (@gchudnov)](https://medium.com/@gchudnov/trapping-signals-in-docker-containers-7a57fdda7d86)
|
2016-09-29 19:01:55 +03:00
|
|
|
* [tini](https://github.com/krallin/tini), an alternative to dumb-init
|
2016-11-14 06:36:31 +03:00
|
|
|
* [pid1](https://github.com/fpco/pid1), an alternative to dumb-init, written in Haskell
|
2015-09-04 20:26:47 +03:00
|
|
|
|
|
|
|
|
|
|
|
[daemontools]: http://cr.yp.to/daemontools.html
|
2017-05-09 21:38:32 +03:00
|
|
|
[docker-cmd-json]: https://docs.docker.com/engine/reference/builder/#run
|
|
|
|
[docker]: https://www.docker.com/
|
|
|
|
[exec]: https://en.wikipedia.org/wiki/Exec_(system_call)
|
2015-09-04 20:26:47 +03:00
|
|
|
[gh-releases]: https://github.com/Yelp/dumb-init/releases
|
2017-05-09 21:38:32 +03:00
|
|
|
[supervisord]: http://supervisord.org/
|
2015-10-09 23:23:17 +03:00
|
|
|
[systemd]: https://wiki.freedesktop.org/www/Software/systemd/
|
|
|
|
[sysvinit]: https://wiki.archlinux.org/index.php/SysVinit
|