A health check is exactly what they sound like - a way of checking the health of a resource. In the case of Docker, a health check is used to determine the health of a running container.
When a health check command is created, it defines how a container can be tested to see if it is working correctly. With no health check defined, Docker cannot know whether or not the services running within your container are actually started or not.
While working with Docker, two ways of defining a health check exist:
- Docker-Compose file.
I will cover only Docker-Compose Healthchecks within this article.
2. An example using Nginx
Let’s see how health checks work by using a simple Nginx web service. To create a very simple website we need three files:
A docker-compose.very-simple-web.yml, a Dockerfile, and an index.html:
version: '3.4' services: web: image: very-simple-web build: context: ./ dockerfile: Dockerfile ports: - "80:80" restart: unless-stopped
FROM nginx COPY html /usr/share/nginx/html
<html> <head> <title>Hello World FTP</title> </head> <body> Hello World </body> </html>
The three files must be saved in this structure
Now let’s create the service:
docker-compose up -d --build
If you open a web browser to localhost you should see “Hello World”
3. Why do we need a health check?
Well, in this special case normally we don't need any health check because we are just serving a simple website with Nginx. But in a production environment, it is possible that we run multiple processes that could crash.
In this case, we could use our website, but the Docker service will still be running even though we cannot access the website.
If this service is running within a Docker Swarm, the Swarm will still think everything is working correctly because the container is in a running state. This leads to a problem that the Swarm thinks everything is fine and will not restart the container so that it will be working again.
4. Configure a health check within the compose file
As an example, the docker-compose.very-simple-web.yml will be extended by a simple
curl based health check (will be explained later):
version: '3.4' services: web: image: very-simple-web build: context: ./ dockerfile: Dockerfile restart: unless-stopped ports: - "80:80" healthcheck: test: curl --fail http://localhost || exit 1 interval: 60s retries: 5 start_period: 20s timeout: 10s
The Docker Compose Healtcheck contains five properties:
- test: This property specifies the command that will be executed and is the health check of the container. This command HAS TO be available and working if everything is set up correctly.
- interval: This property specifies the number of seconds to initially wait before executing the health check and then the frequency at which subsequent health checks will be performed.
- timeout: This property specifies the number of seconds Docker awaits for your health check command to return an exit code before declaring it as failed.
- retries: This property specifies the number of consecutive health check failures required to declare the container as unhealthy.
- start_period: This property specifies the number of seconds your container needs to bootstrap. During this period, health checks with an exit code greater than zero won’t mark the container as unhealthy; however, a status code of 0 will mark the container as healthy.
Note that this health check is based on
curl . To use this health check you have to guarantee that
curl is installed within the image used for running the service.
curl is not available another often used health check is based on
wget and can look like this:
wget --no-verbose --tries=1 --spider http://localhost || exit 1
Just replace the
curl command in the prior defined compose file.
5. Often used Docker health checks
healthcheck: test: wget --no-verbose --tries=1 --spider http://localhost || exit 1 interval: 60s retries: 5 start_period: 20s timeout: 10s
healthcheck: test: curl --fail http://localhost || exit 1 interval: 60s retries: 5 start_period: 20s timeout: 10s
5.3 What to do if CURL/WGET is not available?
If you use an image that doesn’t have
wget installed you need to install it. This can be achieved by adding an install command to your Dockerfile.
RUN apk --update --no-cache add curl
RUN apt-get update && apt-get install -y wget
But keep in mind that if you add
wget you could also add all the attack surfaces of those two tools.
A good workaround will be creating your own program which will then be included in the docker health check test command.
6. A custom health check
In comparison to
wget the custom health check gets over all the issues of using an external tool:
- You’re using the same runtime as your actual app, so there are no additional prerequisites for your health check that has to be installed
- You can decide which logic you want in your health check and it can stay private, so only the Docker platform can execute that code.
The downside is that you have to create and maintain a separate piece of code. But as this will be written in the same language as your app it should be much easier to achieve than developing a complex
wget command (If it is complex).
7. Closing Notes
I hope you learned that having your Docker container started does not necessarily mean that your application is running and works how it was designed. Docker health checks can be implemented quickly to identify possible bugs in your software before they become a real problem.
Next time you create a Docker-Compose file, consider adding a health check.