multi-process docker init stack. lightweight, simple and without reinventing the wheel
The "PID1-problem", zombie processes, signal forwarding and running multiple processes in docker containers has been discussed in length. While well tested and widely supported solutions exist, running multiple processes and setting up the required environment does not come in a simple and light-weight package. The most notable "all-in-one" solutions being s6-overlay
and pushion/base-image
s' my_init.py
; which either come with a high (perceived) level of complexity or a hefty size penalty (i.e. due to the required python interpreter).
fission init simplifies the process of setting up your environment, running init scripts, supervising services with automatic logging and allows for simple introspection. All by means of a simple json file.
- A mostly POSIX compatible shell interpreter
awk
xargs
setsid
ps
andkill
variants callable byxargs
(non builtins)- staples like
cut
,tr
,ln
,mkdir
,mkfifo
,sleep
, ... (available on all tested images)
POSIX compatibility is no design goal! This project is targeting Linux containers.
tini
as PID1; or run usingdocker run --init
anddocker-init
binary will be reusedrunsvdir
(runit
) for service supervisionjq
- Fission being the scientific term for division/splitting. fission init allows you to "split" your single PID1 process of your container into multiple supervised processes.
- Runit, as well as being the core component of fission init for service supervision, is the name of an island. Runit island is a nuclear waste storage site and nuclear fission bomb testing site.
# Dockerfile
FROM <your-base>
# get fission-init (amd64/x86_64 and arm64/aarch64)
COPY --from=ghcr.io/bitmeal/fission:latest / /
#...
# use fission-init
ENTRYPOINT ["/opt/fission/bin/fission", "/opt/app/app", "app_param_mandatory"]
CMD ["app_param_optional"]
// fission.json [/etc/fission/fission.json]
{
"env": {
"FOO": "bar"
},
"init": {
"01_app_cfg": "/opt/app/init.sh"
},
"services": {
"foo": "/opt/app/services/foo"
}
}
- copy rootfs (
/
) fromfission:base
container image - configure your environment, init scripts and services in
fission.json
- add your
fission.json
configuration as/etc/fission/fission.json
in your container - call
fission
, and give your main command and its parameters as arguments, asENTRYPOINT
see
examples/
for full examples of using fission-init
As seen above, you can copy all required files form the provided docker image ghcr.io/bitmeal/fission
. Check test/platforms/<platform>/Dockerfile
to see if your platform requires additional dependencies.
Available tags to pull:
latest
: latest tagged versionv<semver>
: specific tagged versionedge
: latest commit on master
- copy
fission
to a destination of your choice within your container - install
runit
andjq
, andtini
or choose to usedocker-init
withdocker run --init
Partial compatibility with existing schemes for environment initialization and
runit
supervision is provided (see bottom of this README).
fission inits' fission
script replaces itself with tini
as popper PID1, when ran as PID1 itself. tini
is used for its signal forwarding capabilities with process group scope. E.g. fpco/pid1
or dumb-init
will break our signal forwarding or error forwarding capabilities. You can choose to use dockers own init by passing the --init
flag to docker when running your container. This will mount a statically linked variant of tini
in your container as docker-init
. We will reuse this init binary and call it with the required parameters as an additional "layer" with PID=2.
fission init adheres to the idea of one application/service per container, while this application may depend on the presence of other services. These services are thought to be tightly coupled to your application and not to be shared outside of the container!
Exiting your main process, or auxiliary process (if provided) will exit your services and PID1, and consequently stop the container.
Your main process/application and its parameters should be passed as arguments to fission
, like any proper PID1 system. Additional arguments may thus be given while creating an instance of your container. The following example will call /opt/app/app
with the argument app_param
.
# Dockerfile
# ...
ENTRYPOINT ["/opt/fission/bin/fission", "/opt/app/app", "app_param"]
fission init allows you to run an auxiliary process. Intended use for this feature is introspection into and debugging of your container. After your main process, you may pass --
followed by an additional command and its' parameters. Using the Dockerfile example from above, you may get a shell in your container (i.e. mycontainer
) in parallel to your main application by calling, e.g.:
docker run -it mycontainer -- /bin/sh
When running an auxiliary process, your main process' output will be logged to /var/log/app/
, while additionally forwarding stderr to screen. To disable stderr forwarding, configure fission init to silence stderr (see services section).
Service supervision is provided by runit
and its runsvdir
. Configure your services as a dictionary under the key services
in fission.json
. runit
compatible configurations will automatically be generated when calling fission
.
// fission.json [/etc/fission/fission.json]
{
"services": {
"foo": "/opt/app/services/foo"
}
}
In addition to tini
, dumb-init
is used to rewrite SIGTERM/SIGINT
to SIGHUP
for runsvdir
and use its signal forwarding capabilities to services.
Manually created runit
services from /etc/service
will be launched, but will not profit from any automated functionality of fission init!
A logger will be created for all services configured in your fission.json
. stdout and stderr are merged and logged in /var/log/<service_name>/
, using svlogd
with automatic log rotation.
Additionally stderr is forwarded to /dev/stderr
and will appear in your containers' output ("on screen")! To disable this "on screen" forwarding of stderr, configure fission init as "stderr": false
:
// fission.json [/etc/fission/fission.json]
{
"stderr": false
}
Environment variables are configured as a dictionary under key env
, with the variables name as key and its value as its value.
// fission.json [/etc/fission/fission.json]
{
"env": {
"FOO": "bar"
}
}
Provide init scripts, or directories containing init scripts, to be source
d in your environment as a dictionary under the key init
. The keys/names of your init scripts will be used to determine sourcing order and enable overlay functionality (see below). Ordering is based on unicode codepoint order, as implemented by jq
. Working directory while sourcing is either the scripts directory (is script path is given), or the specified directory (if directory is given).
// fission.json [/etc/fission/fission.json]
{
"init": {
"01_app_cfg": "/opt/app/init.sh"
}
}
To alter your configuration, you can mount additional json files in /etc/fission/overlays/
. Overlay configuration files will be read and merged into the original config, in the order established by shell globbing with the collating sequence for LC_COLLATE=C
. Dictionaries are "deep-merged". To remove individual items from the configuration, set their value to null
; removing items is performed after merging.
As seen in the service section, forwarding of stderr of background services can be disabled. To disable all output of fission init, configure it as "silent": true
.
// fission.json [/etc/fission/fission.json]
// silence all output
{
"silent": true,
"stderr": false
}
Compatible with pushion/base-image
s' my_init.py
; except for environment configuration.
init from/etc/rc.local
and/etc/init.d/**
- run manually created
runit
services from/etc/service/*
find tests and documentation in ./test
. Tests use bats for testing, but are "self-bootstrapping". Tests require:
- docker
- bash
uuidgen
or/proc/sys/kernel/random/uuid