@@ -11,7 +11,8 @@ aliases:
11
11
- /config/containers/multi-service_container/
12
12
---
13
13
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
15
16
end of the ` Dockerfile ` . It's best practice to separate areas of concern by
16
17
using one service per container. That service may fork into multiple
17
18
processes (for example, Apache web server starts multiple worker processes).
@@ -22,16 +23,17 @@ shared volumes.
22
23
23
24
The container's main process is responsible for managing all processes that it
24
25
starts. 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
27
29
run the container. The ` --init ` flag inserts a tiny init-process into the
28
30
container as the main process, and handles reaping of all processes when the
29
31
container exits. Handling such processes this way is superior to using a
30
32
full-fledged init process such as ` sysvinit ` or ` systemd ` to handle process
31
33
lifecycle within your container.
32
34
33
35
If 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.
35
37
36
38
## Use a wrapper script
37
39
@@ -66,6 +68,11 @@ COPY my_wrapper_script.sh my_wrapper_script.sh
66
68
CMD ./my_wrapper_script.sh
67
69
```
68
70
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
+
69
76
## Use Bash job controls
70
77
71
78
If you have one main process that needs to start first and stay running but you
@@ -104,14 +111,18 @@ CMD ./my_wrapper_script.sh
104
111
105
112
## Use a process manager
106
113
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
111
118
manages your processes for you.
112
119
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:
115
126
116
127
- ` supervisord.conf `
117
128
- ` my_first_process `
@@ -142,3 +153,73 @@ stdout_logfile=/dev/fd/1
142
153
stdout_logfile_maxbytes =0
143
154
redirect_stderr =true
144
155
```
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