Managing multiple processes in Docker containers

beld 🎩
3 min readMar 23, 2017

--

Even though the common practice states that one must create containers that have a single process running, it's common so see people facing the need for a multi-process approach.

A pretty good solution that i've found is S6 (https://skarnet.org/software/s6/) "a collection of utilities revolving around process supervision and management, logging, and system initialization" (from the description) β€” after reading a blog post from Tutum (https://blog.tutum.co/2014/12/02/docker-and-s6-my-new-favorite-process-supervisor/).

My context was simple; needed to run several processes and ensure that:

  • if process A fails, process A must be restarted
  • processes A, B and C are supposed to not be ran as daemons and might occasionally fork (creating subprocesses)
  • doesn't require many configuration files
  • SIGTERM should be properly handled

A sample demonstrating the end result can be found here: https://github.com/beldpro-ci/s6-entrypoint.

Going straight to the demonstration, the directory structure:

.
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ Makefile
β”œβ”€β”€ README.md
└── etc // added to `/etc` of image
β”œβ”€β”€ bin
β”‚ └── my-binary // main executable
└── s6
β”œβ”€β”€ lala
β”‚ β”œβ”€β”€ finish // lala β€œon-fail” script
β”‚ └── run // lala service start-up script
└── lulu
β”œβ”€β”€ finish // lulu β€œon-fail” script
└── run // lulu start-up script

As S6 has the capabilities of a proper *init*, the Dockerfile is pretty simple: just fetches S6 and then sets the entrypoint:

FROM alpineRUN apk add β€” update s6 bashADD ./etc /etcENTRYPOINT [ β€œ/bin/s6-svscan”, β€œ/etc/s6” ]`

/etc/s6 indicates a scan directory: a directory with a list of service directories β€” where scripts related to a given service reside. For instance, say that *lala* is a service (like nginx) that has a start script and that we also have *lulu*, another service, like rsyslog, which also has a start script. We'd create a directory (in this case i created /etc/s6 β€” could have any name!) and then, for each service, add a directory (with the name of the service). Each service directory then receives a *run* script (name used by s6 to locate a start script).

And that's it! That's the enough to have your processes being supervised and its zombies taken care of. In the end, it looks like this:

s6-docker-demo git:(master) βœ— makedocker run s6testSpawning lalaSpawning lulu

The two processes are spawn concurrently, with a little trick of having *s6-supervise* processes taking care of our services:

~ docker top f5993ea33b59PID USER TIME COMMAND3136 root 0:00 /bin/s6-svscan /etc/s63156 root 0:00 s6-supervise lala3157 root 0:00 s6-supervise lulu3158 root 0:00 {my-binary} /bin/sh /etc/bin/my-binary3160 root 0:00 {my-binary} /bin/sh /etc/bin/my-binary3217 root 0:00 sleep 13218 root 0:00 sleep 1

In the repository (https://github.com/beldpro-ci/s6-entrypoint) you can find more information as each little detail got documented over there.

What about you? How have you been doing multi-processes containers with Docker (if so)? Let me know if you've got a good solution!

Hey, also, please let me know if i stated something incorrectly. I'll appreciate a lot! Thanks!

--

--

beld 🎩

Working on a blog for those indie hackers trying to get servers up β€” https://ops.tips