How can I automatically restart my Docker containers with container auto-delete?
I have a general purpose VPS, and am Dockerising the apps on it. There will be around 5-6 containers on it, and very little else, so the box can be trivially rebuilt as required.
For each app I have a start script. A WordPress container looks like this:
#!/bin/bash
# Get the host IP address
export DOCKER_HOSTIP=`ifconfig docker0 | grep "inet addr" | cut -d ':' -f 2 | cut -d ' ' -f 1`
echo "Connecting to database on Docker host ${DOCKER_HOSTIP}"
docker run \
--add-host=docker:${DOCKER_HOSTIP} \
--network dockernet \
--network-alias jonblog \
--detach \
--restart always \
--rm \
jonblog
However, that returns an error:
Conflicting options: --restart and --rm
There are several Docker tickets that say this is sensible, but I don't understand it. I think my meaning is clear: if a container is not running (e.g. on start-up) then I want to start it. If it dies then I expect the container to be removed, and a new fresh one created from the base image. Containers should be immutable anyway - any state I wish to preserve, like media files and logs, will be written to volumes.
So, I thought I should drop the --restart
flag, and then use a process manager to stop and start Docker containers. Could I use Monit here? I was hoping to be able to do something like:
CHECK PROCESS jonblog MATCHING jonblog
START PROGRAM = "/root/docker/jonblog/host-start.sh"
STOP PROGRAM = "docker stop jon-blog"
However, that checks the system process table, and not docker ps
, and so it won't find something matching the specified string. Can I get it to exec docker ps
periodically, and match lines in the output?
I'd be happy to use another tool if it proves robust. For example, I find Supervisor a bit heavyweight, but if that is more able to work with Docker, I am willing to use it.
Clarification on --rm
The reason why I want --rm
is that during the Dockerisation process, I stop the currently running container, load
a new version of the image, and re-run the above script. This means that Docker is being notified of a restart
policy for each container. I found that after rebooting the box, I would have 15 or so slightly-differing versions of the app running simultaneously, which is not the intention.
I suppose I could use docker update --restart never
on old containers to prevent this happening, but then when my container stops, I am left with it lying around, and I'd just as soon have it auto-delete. I could periodically clean old ones up using some sort of cron job, but that feels a bit hacky given that Docker can do it for me.
Seeking a range of answers
A very helpful comment has suggested I should look into Minikube, which apparently simplifies the setup of Kubernetes, even to the degree a suitably skilled individual can be up and running in five minutes.
I would still quite like to see more lightweight solutions proposed, so I have a range of answers to choose from. As indicated, I would like to find out the answer to whether a process supervisor like Monit would work.
Off the top off my head, I could write a shell loop to write docker ps
to a file every five seconds for a minute, and then run that on a Cron every minute. I could then scan that file using grep
and the Monit CHECK PROGRAM
system check. That's a bit hacky, but is something that I can understand easily if there is a problem with it. Any advances on that suggestion?
Solution 1:
I have an answer that is suitable for my current understanding of Docker. I was advised in the comments to try Minikube, and although undoubtedly this can be spun up quickly, I feared that this would be a rabbit-hole of learning that would get me stuck in tar for weeks. One of my engineering principles is to know when one has reached a cognitive limit for stuffing in new information!
Thus, I set out to resolve this problem in a simple fashion. I had two choices:
- Use the container auto-delete feature in Docker, and set up my own restart system
- Use the Docker restart policy, and set up my own container deletion system
I started on the first of these, with the idea that the process supervisor Monit would be nice to use, partly because it is lightweight, and partly because I am familiar with it. However, it started to feel like the wrong solution, since I'd be working around the core problem that it cannot cleanly get a Docker container process list.
In fact, the second option was much cleaner, and this was amplified by the fact that stopped container clean-up is not actually a priority - it is just to keep things tidy. Of course, I used Docker for this; here's the Dockerfile
:
# Docker build script for Docker Tidy
FROM alpine:3.6
RUN apk update
RUN apk add docker
# See this for BusyBox cron schedules
# https://gist.github.com/andyshinn/3ae01fa13cb64c9d36e7
COPY bin/docker-tidy.sh /etc/periodic/daily/
RUN chmod +x /etc/periodic/daily/docker-tidy.sh
# Start Cron in the foreground
ENTRYPOINT ["crond", "-l", "2", "-f"]
And here's bin/docker-tidy.sh
:
#!/bin/sh
#
# With thanks to:
# http://www.doublecloud.org/2015/05/simple-script-to-list-and-remove-all-stopped-docker-containers/
docker rm -v $(docker ps -a -q -f status=exited)
Finally, one drawback with my solution is that if the host is rebooted prior to a stopped container cleanup, those containers seem to restart as well. I therefore reset the restart policy on those containers prior to starting new ones.
For example, here is how I start the Docker Tidy container itself, on the host. In practice I've tidied up the policy change code into its own script, but this will give the general idea:
#!/bin/bash
# Removes the restart policy from previous containers
CONTAINER_LABEL=docker-tidy-instance
docker ps --all --filter label=$CONTAINER_LABEL --quiet | xargs --no-run-if-empty docker update --restart no
docker run \
--label $CONTAINER_LABEL \
--volume /var/run/docker.sock:/var/run/docker.sock \
--detach \
--restart always \
docker-tidy