Ensuring Containers Are Always Running with Docker’s Restart Policy
Getting a notification that Docker containers are down in production is one of the worst ways to spend your night. In today’s article, we’ll discuss how to use Docker’s restart policy to automatically restart containers and avoid those late-night notifications.
What Happens When an Application Crashes?
Before we get started with Docker’s restart policy, let’s understand a bit more about how Docker behaves when an application crashes. To facilitate this, we’ll create a Docker container that executes a simple bash script named crash.sh
.
#/bin/bash sleep 30 exit 1
The above script is simple; when started, it will sleep
for 30
seconds, and then it will exit
with an exit code of 1
indicating an error.
Building and running a custom container
In order to run this script within a container, we’ll need to build a custom Docker container which includes the crash.sh
script. In order to build a custom container, we first need to create a simple Dockerfile
.
$ vi Dockerfile
The Dockerfile
will contain the following three lines:
FROM ubuntu:14.04 ADD crash.sh / CMD /bin/bash /crash.sh
The above Dockerfile
will build a container based on the latest ubuntu:14.04
image. It will also add the crash.sh
script into the /
directory of the container. The final line tells Docker to execute the crash.sh
script when the container is started.
With the Dockerfile
defined, we can now build our custom container using the docker build
command.
$ sudo docker build -t testing_restarts ./ Sending build context to Docker daemon 3.072 kB Step 1 : FROM ubuntu:14.04 ---> e36c55082fa6 Step 2 : ADD crash.sh / ---> eb6057d904ef Removing intermediate container 5199db00ba76 Step 3 : CMD /bin/bash /crash.sh ---> Running in 01e6f5e12c3f ---> 0e2f4ac52f19 Removing intermediate container 01e6f5e12c3f Successfully built 0e2f4ac52f19
This build command created a Docker image with a tagged name of testing_restarts
. We can now start a container using the testing_restarts
image by executing docker run
.
$ sudo docker run -d --name testing_restarts testing_restarts a35bb16634a029039c8b34dddba41854e9a5b95222d32e3ca5624787c4c8914a
From the above, it appears that Docker was able to start a container named testing_restarts
. Let’s check the status of that container by running docker ps
.
$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS
The docker ps
command doesn’t show any running containers. The reason for this is because docker ps
by default only shows running containers. Let’s take a look at running and non-running containers by using the -a
flag.
$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a35bb16634a0 testing_restarts "/bin/sh -c '/bin/bas" 9 minutes ago Exited (1) 8 minutes ago
With the docker ps
results, we can see that when an application within a Docker container exits, that container is also stopped. This means that, by default, if an application that is running within a container crashes, the container stops and that container will remain stopped until someone or something restarts it.
!New Call-to-action
Changing Docker’s Default Behavior
It’s possible to automatically restart crashed containers by specifying a restart policy when initiating the container. To understand restart policies better, let’s see what happens when we use the always
restart policy with this same container.
$ sudo docker run -d --name testing_restarts --restart always testing_restarts 8320e96172e4403cf6527df538fb7054accf3a55513deb12bb6a5535177c1f19
In the above command, we specified that Docker should apply the always
restart policy to this container via the --restart
flag. Let’s see what effect this has on our container by executing a docker ps
again.
$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8320e96172e4 testing_restarts "/bin/sh -c '/bin/bas" About a minute ago Up 21 seconds
This time we can see that the container is up and running but only for 21
seconds. If we run docker ps
again, we will see something interesting.
$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8320e96172e4 testing_restarts "/bin/sh -c '/bin/bas" About a minute ago Up 19 seconds
The second run shows the container has only been up for 19
seconds. This means that even though our application (crash.sh
) continues to exit with an error, Docker is continuously restarting the container every time it exits.
Now that we understand how restart policies can be used to change Docker’s default behavior, let’s take a look at what restart policies Docker has available.
Docker’s Restart Policy(ies)
Docker currently has four restart policies:
no
on-failure
unless-stopped
always
The no
policy is the default restart policy and simply does not restart a container under any circumstance.
Restarting on failure but stopping on success
The on-failure
policy is a bit interesting as it allows you to tell Docker to restart a container if the exit code indicates error but not if the exit code indicates success. You can also specify a maximum number of times Docker will automatically restart the container.
Let’s try this restart policy out with our testing_restarts
container and set a limit of 5
restarts.
$ sudo docker run -d --name testing_restarts --restart on-failure:5 testing_restarts 85ff2f096bac9965a9b8cffbb73c1642bf7b64a2173bbd145961231861b95819
If we run docker ps
within a minute of launching the container, we will see that the container is running and has been recently started.
$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 85ff2f096bac testing_restarts "/bin/sh -c '/bin/bas" About a minute ago Up 8 seconds
The same will not be true, however, if we run the docker ps
command 3
minutes after launching the container.
$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 85ff2f096bac testing_restarts "/bin/sh -c '/bin/bas" 3 minutes ago Exited (1) 20 seconds ago
We can see from the above that after 3
minutes the container is stopped. This is due to the fact that the container has been restarted more than our max-retries
setting.
With success
The benefit of on-failures
is that when an application exits with a successful exit code, the container will not be restarted. Let’s see this in action by making a quick minor change to the crash.sh
script.
$ vi crash.sh
The change will be to set the exit code to 0
.
#/bin/bash sleep 30 exit 0
By setting the script to exit with a 0
exit code, we will be removing the error indicator from the script. Meaning as far as Docker can tell, this script will execute successfully every time.
With the script changed, we will need to rebuild the container before we can run it again.
$ sudo docker build -t testing_restarts ./ Sending build context to Docker daemon 3.072 kB Step 1 : FROM ubuntu:14.04 ---> e36c55082fa6 Step 2 : ADD crash.sh / ---> a4e7e4ad968f Removing intermediate container 88115fe05456 Step 3 : CMD /bin/bash /crash.sh ---> Running in fc8bbaffd9b9 ---> 8aaa3d99f432 Removing intermediate container fc8bbaffd9b9 Successfully built 8aaa3d99f432
With the container image rebuilt, let’s launch this container again with the same on-failures
and max-retries
settings.
$ sudo docker run -d --name testing_restarts --restart on-failure:5 testing_restarts f0052e0c509dfc1c1b112c3b3717c23bc66db980f222144ca1c9a6b51cabdc19
This time, when we perform a docker ps -a
execution, we should see some different results.
$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f0052e0c509d testing_restarts "/bin/sh -c '/bin/bas" 41 seconds ago Exited (0) 11 seconds ago
Since the crash.sh
script exited with a successful exit code (0
), Docker understood this as a success and did not restart the container.
Always restart the container
If we wanted the container to be restarted regardless of the exit code, we have a couple of restart policies we could use:
always
unless-stopped
The always
restart policy tells Docker to restart the container under every circumstance. We experimented with the always
restart policy earlier, but let’s see what happens when we restart the current container with the always
restart policy.
$ sudo docker run -d --name testing_restarts --restart always testing_restarts 676f12c9cd4cac7d3dd84d8b70734119ef956b3e5100b2449197c2352f3c4a55
If we wait for a few minutes and run docker ps -a
again, we should see that the container has been restarted even with the exit code showing success.
$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9afad0ccd068 testing_restarts "/bin/sh -c '/bin/bas" 4 minutes ago Up 22 seconds
What’s great about the always
restart policy is that even if our Docker host was to crash on boot, the Docker service will restart our container. Let’s see this in action to fully appreciate why this is useful.
$ sudo reboot
By default or even with on-failures
, our container would not be running on reboot. Which, depending on what task the container performs, may be problematic.
$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 676f12c9cd4c testing_restarts "/bin/sh -c '/bin/bas" 9 minutes ago Up 2 seconds
With the always
restart policy, that is not the case. The always
restart policy will always restart the container. This is true even if the container has been stopped before the reboot. Let’s look at that scenario in action.
$ sudo docker stop testing_restarts testing_restarts $ sudo reboot
Before rebooting our system, we simply stopped the container. This means the container is still there, just not running. Once the system is back up after our reboot however, the container will be running.
$ sudo docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 676f12c9cd4c testing_restarts "/bin/sh -c '/bin/bas" 11 minutes ago Up 24 seconds
The reason our container is running after a reboot is because of the always
policy. Whenever the Docker service is restarted, containers using the always
policy will be restarted regardless of whether they were running or now.
The problem is that restarting a container that has been previously stopped after a reboot can be a bit problematic. What if our container was stopped for a valid reason, or worse, what if the container is out of date?
The solution for this is the unless-stopped
restart policy.
Only stop when Docker is stopped
The unless-stopped
restart policy behaves the same as always
with one exception. When a container is stopped and the server is rebooted or the Docker service is restarted, the container will not be restarted.
Let’s see this in action by starting the container with the unless-stopped
policy and repeating our last example.
$ sudo docker run -d --name testing_restarts --restart unless-stopped testing_restarts fec5be52b9559b4f6421b10fe41c9c1dc3a16ff838c25d74238c5892f2b0b36
With the container running, let’s stop it and reboot the system again.
$ sudo docker stop testing_restarts testing_restarts $ sudo reboot
This time when the system restarts, we should see the container is in a stopped state.
$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fec5be52b955 testing_restarts "/bin/sh -c '/bin/bas" 2 minutes ago Exited (137) About a minute ago
One important item with unless-stopped
is that if the container was running before the reboot, the container would be restarted once the system restarted. We can see this in action by restarting our container and rebooting the system again.
$ sudo docker start testing_restarts testing_restarts $ sudo reboot
After this reboot, the container should be running.
$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fec5be52b955 testing_restarts "/bin/sh -c '/bin/bas" 5 minutes ago Up 13 seconds testing_restarts
The difference between always
and unless-stopped
may be small, but in some environments this small difference may be a critical decision.
Selecting the Best Restart Policy
When selecting the best restart policy, it’s important to keep in mind what type of workload the container is performing.
A Redis instance, for example, may be a critical component in your environment which should have an always
or unless-stopped
policy. On the other hand, a batch-processing application may need to be restarted until the process successfully completes. In this case, it would make sense to use the on-failures
policy.
Either way, with Docker’s restart policy you can now rest assured that next time a Docker host mysteriously reboots at 3 a.m., your containers will be restarted.
Reference: | Ensuring Containers Are Always Running with Docker’s Restart Policy from our JCG partner Ben Cane at the Codeship Blog blog. |