Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ


Choose your language

InfoQ Homepage News Yelp Releases dumb-init, a Minimal init System for Docker Containers

Yelp Releases dumb-init, a Minimal init System for Docker Containers

Yelp released a minimal init system for Docker containers called dumb-init which acts as a signal handling proxy and performs other init functions like reaping orphaned zombie processes. This tool can be specified in the Dockerfile before the command to be run and works by registering handlers for all signals that can be caught and forwarding them to the actual process running in the container.

Docker containers are usually used to run a single process which runs with process id (PID) of 1. In Unix and Linux based systems, the process with PID 1 - called init - has a special significance. It is spawned by the kernel and all other processes are spawned as its children. When any child terminates, it turns into a "zombie" process, since in Unix, parent processes are designed so that they have to explicitly wait for their children to terminate using the "waitpid" family of system calls. A child process will remain in a zombie state until the parent invokes waitpid (or any of its variants). This mechanism is called “reaping”.

In cases where the parent process itself terminates, the process with PID 1 adopts its children so they are not orphaned and can be reaped correctly. The kernel expects this functionality from the process which is running with PID 1.

In Docker, the process to run can be specified in two different ways in the Dockerfile - run it in a shell or run it directly. Inside the container, the PID 1 process which is either a shell running the process or the process itself, must take on the responsibilities of init. However, neither a shell nor most processes like web servers, databases are designed to do so. When running in a shell, any signals sent using the ‘docker signal’ command won’t be forwarded to the actual process, since the shell would not understand it. When running directly, the process usually won’t handle signals as init is supposed to do.

Due to such behaviour, the process running inside the container would not exit cleanly and behave as expected when a docker stop or a docker signal command is sent and perform the job of reaping orphaned zombie processes. This might lead to problems like

  • Filling up the operating system process table with zombie process entries due to which the OS might not be able to create new processes.
  • Data inconsistencies in the application if it’s using a database.
  • Containers created by CI systems such as Jenkins won’t be removed after the test suite run by Jenkins aborts.

dumb-init aims to solve these problems. It can be specified before the command to be run in the Dockerfile. It runs with PID 1 and handles all possible signals by registering signal handlers. It also takes care of reaping orphaned zombie processes.

There are existing tools that solve the same problem like Baseimage-docker and tini. On being asked about what makes dumb-init unique, Chris Kuehl, software engineer at Yelp said:

The most important difference with other tools  is that dumb-init is designed to be as transparent as possible. At Yelp we run our unit tests inside containers, which is great for ensuring a consistent environment between developer machines and CI. But we still want developers to be able to work with unit tests like any other non-Dockerized process. This means being able to drop into an interactive Python debugger (and give input on stdin), hit ^C to signal the process, etc. Ideally we want dumb-init to do as little as necessary to make the process it's watching behave just like it would outside of a container..

Kuehl also commented on the specific differences between existing tools and dumb-init:

runit (used by Phusion's init) and s6 are both inspired by daemontools, which is a very light-weight init system but is designed for spawning and supervising multiple processes. If you're running unit tests, you don't want your process supervised; if the tests exit with an error code, you want that error code returned to the user, not for the tests to be respawned. tini is very similar to dumb-init; the implementations are somewhat different but the behavior is almost the same.

Could dumb-init have been built on top of something existing like BaseImage? Kuehl said:

Baseimage is a good choice for some, but it doesn't work in all use cases. Again, the unit test suite is a good example - you don't want cron or syslog in that container, but baseimage will run them. You also lose the ability to target specific images, which means we can't easily run the tests against four different versions of Ubuntu.

According to the Yelp article, the primary motivation behind creating dumb-init was to make it easier for developers to move to containers. Kuehl elaborated on this:

dumb-init is really good for interactive use by engineers because it allows you to take a process which doesn't currently run inside Docker and move it into a container without changing its behavior. It's also important for non-interactive server processes because it ensures the process can be terminated cleanly, without, for example, leaving connections open to a database or dying in the middle of serving a request to a user.

Rate this Article