I’ve been working with docker-compose a lot, and it’s a really great tool. I can’t wait to see what they do with it!
However, I found myself doing a lot of
docker compose stop,
docker compose start. Shutting down each container was taking approximately 10 seconds each, which, with my setup, meant waiting about a minute for a shutdown. This was certainly manageable, but also added up quickly.
So what happens when you call
docker stop? The container’s main process is sent a signal,
SIGTERM. It’s then given 10 seconds to do any cleanup it wants/needs to, and then it’s sent
SIGKILL and forcibly killed.
python does nothing when it gets a
SIGTERM, it has the default signal handler installed (
SIG_DFL). So, for a simple application, you can turn
SIGTERM into an immediate quit by just doing:
import sys import signal def handler(signum, frame): sys.exit(1) def main(): signal.signal(signal.SIGTERM, handler) ... your special logic here
And that’s it! Or so I thought. I started the container:
$ docker run -d --name shutdown_test shutdown_test $ time docker stop shutdown test real 0m10.367s user 0m0.136s sys 0m0.007s
Not awesome. Checking
docker ps showed the problem, though:
$ docker ps CONTAINER ID IMAGE COMMAND 272c68f3206e shutdown_test:latest "/bin/sh -c ./test.py...
Docker is actually running
sh as the primary process, which is launching my python script. This is because of the
CMD entry I was using in
This version of
CMD, as you can see, runs the process under a shell.
Converting to the argument list format fixes this:
There we go:
$ docker ps CONTAINER ID IMAGE COMMAND 8e5ef05d5389 shutdown_test:latest "./test.py"
and finally, we get the shutdown times we deserve.
$ time docker stop shutdown_test real 0m0.341s user 0m0.132s sys 0m0.007s
It took a bit more work to get all the containers in my docker-compose working. Some containers specified different commands in the
docker-compose.yml itself, so you can do the same thing there.
badcontainer: command: "thescript" goodcontainer: command: ["thescript"]
Also, if you’re using
cherrypy, their documentation reveals an important step for handling signals:
if hasattr(cherrypy.engine, 'signal_handler'): cherrypy.engine.signal_handler.subscribe()
There are a few containers left which have slow shutdowns, but they are ones I’m grabbing from upstream sources. I’m sure some pull requests will be pending!