How To Use Tini Init system in Docker Containers

Posted on 103 views

An init process can be defined as the top-most process started by the kernel at the system boot. They are responsible for starting the rest of the services on the system. These services include the SSH daemon, Nginx/Apache e.t.c. Each of the started processes may further give rise to child processes. A Zombie is a child process that terminates but has not waited for. The kernel maintains information about the Zombie. This information can be, PID, resource usage information, or termination status. These Zombies are harmful and should be removed via a wait. They consume slots in the kernel process table which fills and prevents the creation of further processes.

Tini is a tiny and simplest init available for containers. It works by spawning a single child and waiting for it to exit while reaping the zombie processes as well as performing signal forwarding.

It is mainly preferred due to the following benefits.

  • Protects the kernel from software that accidentally creates zombie processes that starve the system PIDs
  • Transparent and can be used on any container that works without Tini with no changes required.
  • It ensures that the default signal handlers work for the software you run in your Docker image. For example, using SIGTERM terminates the process even if it did not install a signal handler for it.

This guide provides the required knowledge on how to use the Tini Init system in Docker Containers.

Add Tini to your Container.

Tini exists in Docker 1.13 and greater versions. It can easily be enabled using the --init flag while running the docker container.

Using Prebuilt Tini Images

There are also prebuilt images for Tini for Ubuntu and CentOS base which can as well be used as a drop-in replacement of your base. All you do is modify your Dockerfile to use the Tini images instead.

For example:

# Change this:
FROM ubuntu:trusty

# To this:
FROM krallin/ubuntu-tini:trusty

With this image, the ENTRYPOINT shipped should point to Tini. If you already have an ENTRYPOINT configured, modify it as below.

# Change this:
ENTRYPOINT ["/docker-entrypoint.sh"]

# To this:
ENTRYPOINT ["/usr/local/bin/tini", "--", "/docker-entrypoint.sh"]

Remember if you aren’t using an ENTRYPOINT, don’t make the above changes.

Using Tini Packages

There are Tini packages available for Alpine Linux and NixOS and can be added to your Docker file as below.

Alpine Linux Package

RUN apk add --no-cache tini
# Tini is now available at /sbin/tini
ENTRYPOINT ["/sbin/tini", "--"]

NixOS Package

nix-env --install tini

Manually add Tini

You can also add Tini to your container and make it executable. Proceed and invoke Tini and pass the program and arguments.

# Add Tini
ENV TINI_VERSION v0.19.0
ADD https://github.com/krallin/tini/releases/download/$TINI_VERSION/tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]

# Run your program under Tini
CMD ["/your/program", "-and", "-its", "arguments"]
# or docker run your-image /your/program ...

Alternatively, you can use signed binaries when adding Tini to your container above.

# Add Tini
ENV TINI_VERSION v0.19.0
ADD https://github.com/krallin/tini/releases/download/$TINI_VERSION/tini /tini
ADD https://github.com/krallin/tini/releases/download/$TINI_VERSION/tini.asc /tini.asc
RUN gpg --batch --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 595E85A6B1B4779EA4DAAEC70B588DFF0527A9B7 \
 && gpg --batch --verify /tini.asc /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]

# Run your program under Tini
CMD ["/your/program", "-and", "-its", "arguments"]
# or docker run your-image /your/program ...

To use the above command, ensure gpg is installed from your package manager.

How to Use Tini Init system in Docker Containers

Here I will demonstrate how to use Tini from Docker itself by using the --init flag.

A container run without passing the init flag sets the CMD as PID 1. In this case, the CMD is /bin/bash.

$ docker run -ti --rm ubuntu:20.04 /bin/bash
Unable to find image 'ubuntu:20.04' locally
20.04: Pulling from library/ubuntu
d5fd17ec1767: Pull complete 
Digest: sha256:47f14534bda344d9fe6ffd6effb95eefe579f4be0d508b7445cf77f61a0e5724
Status: Downloaded newer image for ubuntu:20.04
[email protected]:/#

Check the PIDs.

[email protected]:/# ps -fA
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 09:19 pts/0    00:00:00 /bin/bash
root           8       1  0 09:20 pts/0    00:00:00 ps -fA

The output above shows the CMD as PID 1. Now let’s see what happens when the –init flag is invoked.

$ docker run -ti --init --rm ubuntu:20.04 /bin/bash
[email protected]:/# ps -fA
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 09:22 pts/0    00:00:00 /sbin/docker-init -- /bin/ba
root           6       1  0 09:22 pts/0    00:00:00 /bin/bash
root           9       6  0 09:22 pts/0    00:00:00 ps -fA

When Tini init is used, it becomes PID 1 as shown above. Now from here, Tini with PID 1 will reap forked child processes correctly.

Another example using the prebuilt Tini images:

$ docker run -ti krallin/ubuntu-tini:trusty /bin/bash
[email protected]:/# ps -fA
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 09:39 pts/0    00:00:00 /usr/bin/tini -- /bin/bash
root           6       1  0 09:39 pts/0    00:00:00 /bin/bash
root          16       6  0 09:39 pts/0    00:00:00 ps -fA

You can as well execute these commands to a running container with Tini as below.

To check the PIDs.

docker exec -it  ps -fA

For example.

How-To-Use-Tini-Init-system-in-Docker-Containers-1024x198

Check the Tini version.

$ docker exec -it semaphore tini --version
tini version 0.19.0

Subreaping

For Tini to reap zombies, it should run as PID 1. This ensures that zombies are re-parented to Tini. If Tini cannot run as PID 1, you need to register Tini as a process subreaper. This can be done in two ways:

  • Passing the -s argument to Tini (tini -s -- …)
  • invoke the variable TINI_SUBREAPERfor example export TINI_SUBREAPER=

Remapping exit codes

Tini reuses the child’s exit code when exiting. This may not be exactly what you prefer. For example, if a child process exits with 143 after receiving SIGTERM, This issue is notable in Java apps. You can use the -e flag to remap the code to 0.

tini -e 143 -- ...

There are several options when using Tini, get help as below:

[email protected]:/# tini -h
tini (tini version 0.19.0 - git.fec3683)
Usage: tini [OPTIONS] PROGRAM -- [ARGS] | --version

Execute a program under the supervision of a valid init process (tini)

Command line options:

  --version: Show version and exit.
  -h: Show this help message and exit.
  -s: Register as a process subreaper (requires Linux >= 3.4).
  -p SIGNAL: Trigger SIGNAL when parent dies, e.g. "-p SIGKILL".
  -v: Generate more verbose output. Repeat up to 3 times.
  -w: Print a warning when processes are getting reaped.
  -g: Send signals to the child's process group.
  -e EXIT_CODE: Remap EXIT_CODE (from 0 to 255) to 0.
  -l: Show license and exit.

Environment variables:

  TINI_SUBREAPER: Register as a process subreaper (requires Linux >= 3.4).
  TINI_VERBOSITY: Set the verbosity level (default: 1).
  TINI_KILL_PROCESS_GROUP: Send signals to the child's process group.

Closing Thoughts.

That marks the end of this guide on how to use the Tini Init system in Docker Containers. You can now use Tini to reap the zombie processes as well as perform signal forwarding. I hope this was significant to you.

coffee

Gravatar Image
A systems engineer with excellent skills in systems administration, cloud computing, systems deployment, virtualization, containers, and a certified ethical hacker.