@@ -11,7 +11,8 @@ aliases:
1111 - /config/containers/multi-service_container/
1212---
1313
14- A container's main running process is the ` ENTRYPOINT ` and/or ` CMD ` at the
14+ A container's main running process is the [ ` ENTRYPOINT ` ] ( https://docs.docker.com/reference/dockerfile/#entrypoint )
15+ and/or [ ` CMD ` ] ( https://docs.docker.com/reference/dockerfile/#cmd ) at the
1516end of the ` Dockerfile ` . It's best practice to separate areas of concern by
1617using one service per container. That service may fork into multiple
1718processes (for example, Apache web server starts multiple worker processes).
@@ -22,16 +23,17 @@ shared volumes.
2223
2324The container's main process is responsible for managing all processes that it
2425starts. In some cases, the main process isn't well-designed, and doesn't handle
25- "reaping" (stopping) child processes gracefully when the container exits. If
26- your process falls into this category, you can use the ` --init ` option when you
26+ "reaping" (stopping) child processes gracefully when the container exits or signal forwarding.
27+ If your process falls into this category, you can use the
28+ [ ` --init ` option] ( https://docs.docker.com/reference/cli/docker/container/run/#init ) when you
2729run the container. The ` --init ` flag inserts a tiny init-process into the
2830container as the main process, and handles reaping of all processes when the
2931container exits. Handling such processes this way is superior to using a
3032full-fledged init process such as ` sysvinit ` or ` systemd ` to handle process
3133lifecycle within your container.
3234
3335If you need to run more than one service within a container, you can achieve
34- this in a few different ways.
36+ this in a few different ways. Bear in mind that this always comes with a trade-off.
3537
3638## Use a wrapper script
3739
@@ -66,6 +68,11 @@ COPY my_wrapper_script.sh my_wrapper_script.sh
6668CMD ./my_wrapper_script.sh
6769```
6870
71+ If you combine this approach with the ` --init ` flag mentioned above, you will get
72+ the benefits of reaping zombie processes but no signal forwarding. Subprocesses may
73+ not terminate gracefully. If your application requires a gracefull shutdown, be aware
74+ of this pitfall.
75+
6976## Use Bash job controls
7077
7178If you have one main process that needs to start first and stay running but you
@@ -104,14 +111,18 @@ CMD ./my_wrapper_script.sh
104111
105112## Use a process manager
106113
107- Use a process manager like ` supervisord ` . This is more involved than the other
108- options, as it requires you to bundle ` supervisord ` and its configuration into
109- your image (or base your image on one that includes ` supervisord ` ), along with
110- the different applications it manages. Then you start ` supervisord ` , which
114+ This is more involved than the other options, as it requires you to
115+ bundle the process manager binary and its configuration into your image
116+ (or base your image on one that includes it ), along with
117+ the different applications it manages. Then you start the process manager as PID 1 , which
111118manages your processes for you.
112119
113- The following Dockerfile example shows this approach. The example assumes that
114- these files exist at the root of the build context:
120+ As with process manager, you do not need the ` --init ` parameter.
121+
122+ ### supervisord
123+
124+ The following Dockerfile example shows this approach for [ ` supervisord ` ] ( https://supervisord.org/ ) .
125+ The example assumes that these files exist at the root of the build context:
115126
116127- ` supervisord.conf `
117128- ` my_first_process `
@@ -142,3 +153,73 @@ stdout_logfile=/dev/fd/1
142153stdout_logfile_maxbytes =0
143154redirect_stderr =true
144155```
156+
157+ The obvious downside to this approach is that your container will run indefinetly
158+ as ` supervisord ` is not designed to terminate when a supervised process terminates.
159+ If you aim for a small image size of your container, this might be an issue to, as it
160+ requires to have a full python runtime available.
161+
162+ ### s6 and s6-overlay
163+
164+ [ s6-overlay] ( https://github.com/just-containers/s6-overlay ) is a layer that can be installed
165+ on top of your container and uses the [ s6] ( https://skarnet.org/software/s6/overview.html )
166+ process manager.
167+
168+ To make you of it in your container, you have to pull it into your dockerfile first:
169+
170+ ``` dockerfile
171+ # Use your favorite image
172+ FROM ubuntu
173+ ARG S6_OVERLAY_VERSION=3.2.1.0
174+
175+ RUN apt-get update && apt-get install -y nginx xz-utils
176+ RUN echo "daemon off;" >> /etc/nginx/nginx.conf
177+
178+ ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
179+ RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
180+ ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
181+ RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz
182+
183+ ENTRYPOINT ["/init" ]
184+ ```
185+
186+ Depending on your target cpu architecture you may need to pull in different releases.
187+
188+ For every service that should run in parallel you then create a shell script
189+ that contains instructions to run or stop the process.
190+
191+ The following example will start an nginx server and a dummy process.
192+
193+ In ` services/nginx/ ` create a file ` run `
194+
195+ ``` sh
196+ #! /usr/bin/with-contenv sh
197+ echo >&2 " Starting: 'nginx'"
198+ exec /usr/sbin/nginx̃
199+ ```
200+
201+ A file ` finish ` in the same directory
202+
203+ ``` sh
204+ #! /usr/bin/env sh
205+ echo >&2 " Exit(code=${1} ): 'nginx'"
206+ ```
207+
208+ In ` services/hello-world/ ` create a file ` run `
209+
210+ ``` sh
211+ #! /usr/bin/with-contenv sh
212+ echo >&2 " Starting: 'hello-world'"
213+ exec sleep 3600
214+ ```
215+
216+ A file ` finish ` in the same directory
217+
218+ ``` sh
219+ #! /usr/bin/env sh
220+ echo >&2 " Exit(code=${1} ): 'hello-world'"
221+ ```
222+
223+ This example will behave the same way as the ` supervisord ` and not terminate,
224+ if all supervised process are stopped. Using ` exec s6-svscanctl -t /var/run/s6/services `
225+ at the end of a ` finish ` script can enable this behavior.
0 commit comments