Mastering Docker: Defining Health Checks in Docker Compose
Tags: sre, devops, programming, docker
Published: 2023-12-29 02:49:08

In this article, we will explore an important aspect of Docker Compose, specifically focusing on health checks, an essential tool for ensuring the reliability and robustness of containerized services. We will cover everything from the basics of what they are and why they are important, to how to practically implement them in your Docker Compose definitions.

## Context on Docker and Docker Compose

In the dynamic world of information technology, virtualization and containerization have revolutionized the way we deploy and manage applications. Docker emerges as a leading solution, enabling developers and system administrators to package, distribute, and manage applications efficiently. Docker encapsulates applications in containers, lightweight and portable environments that ensure consistency across different development, testing, and production environments.

Docker Compose, a tool within the Docker ecosystem, simplifies the process of defining and sharing multi-container applications. With Docker Compose, you can define all the services that make up your application in a single docker-compose.yml file. This tool facilitates the orchestration of multiple containers, allowing them to work together harmoniously to form complex applications.

### Basic Concepts
#### What are Health Checks?

In the context of containerization and Docker, health checks are periodic tests or verifications used to determine whether a container is functioning correctly. Essentially, a health check is a way to automatically monitor the health status of a service or application within a container. If a service fails or stops responding, the health check can detect it and take appropriate actions, such as restarting the container or alerting the operations team.

Health checks are essential in production and development environments because they ensure that applications continue to function optimally and reliably. In an environment with multiple services and containers, health checks provide an additional layer of security by ensuring that each individual service is functioning as expected.

#### How They Work in Docker

Docker incorporates a health check system that allows users to define commands or instructions to check the status of a container. These commands can be as simple as an HTTP request to an application endpoint or a script that checks the availability of an internal service.

When a health check fails, Docker marks the container as unhealthy. This information can be used by orchestration and monitoring tools to make automated decisions, such as restarting the container or redistributing load among healthy containers.

Docker's health checks offer flexibility in terms of configuration, allowing users to define intervals between checks, the number of attempts before considering a service unhealthy, and the maximum time a check should take before being considered failed.

This functionality is crucial for maintaining high availability and reliability of applications, especially in microservices environments where multiple interdependent services must operate in sync.

In the next section, we will delve into how health checks are integrated and configured in Docker Compose, providing practical examples for their implementation.

## Health Checks in Docker Compose
### Integration with Docker Compose

Docker Compose, a tool for defining and running multi-container Docker applications, natively integrates the concept of health checks into its configurations. Through docker-compose.yml files, Docker Compose enables developers and system administrators to specify how the health check for each service in their environment should be performed.

The integration of health checks in Docker Compose is crucial for automating the monitoring and management of service health. In complex environments with multiple containers, health checks become a vital tool to ensure that each service is operating correctly and to facilitate automatic recovery in case of failures.

### Configuration Parameters
In a docker-compose.yml file, the configuration of a health check is done within the definition of each service. The key parameters include:

- `test`: The command that will be executed to check the status of the service. It can be a string or a list. For example, `["CMD", "curl", "-f", "http://localhost/health"]` for a web application.
- `interval`: The time between each check. For example, `30s` indicates that the health check is performed every 30 seconds.
- `timeout`: The maximum time a health check can take before being considered failed. For example, `10s`.
- `retries`: The number of times a failed health check will be attempted before marking the service as unhealthy.
- `start_period`: An initial period during which the results of failed health checks are not counted towards the maximum number of retries. This is useful for applications that require time to start.

These parameters allow for detailed configuration tailored to the specific needs of each service, providing precise control over how and when a service should be considered unhealthy.

## Example:

```
 service1:
    build: ./web-server
    environment:
      - PORT=3000
    ports:
      - "3000:3000"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/ping"]
      interval: 2s
      timeout: 60s
      retries: 20
```

This Docker Compose example illustrates the configuration of a health check for a service named `service1`, which represents a web server. The health check definition is made in the docker-compose.yml file and is composed of several key components:

### Health Check Test (`test`):
- `["CMD", "curl", "-f", "http://localhost:3004/ping"]`: This line defines the command that will be executed for the health check. It uses curl to make an HTTP GET request to the /ping path on port 3004 of localhost. The -f option causes curl to fail if the HTTP status code is 400 or higher, which is useful for detecting errors. This command checks if the web service is responding correctly.

### Interval (`interval`):
- `2s`: This specifies that the health check will be executed every 2 seconds. That is, every 2 seconds, Docker Compose will execute the curl command to check the status of the service.

### Timeout (`timeout`):
- `60s`: This is the maximum time Docker will wait for the health check command to complete. If the curl command does not respond within 60 seconds, it will be considered a health check failure.

#### Retries (`retries`):
- `20`: Indicates the number of times Docker will retry the health check before marking the service as unhealthy. In this case, if the health check fails 20 consecutive times, Docker will consider the `service1` service to be unhealthy.

## Example of a Service Dependent on Another:

```
service2:
    build: ./web-server
    environment:
      - PORT=3001
    ports:
      - "3001:3001"
    depends_on:
      service1:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3001/ping"]
      interval: 2s
      timeout: 60s
      retries: 20
```

In this Docker Compose example, we have two services: `service1` and `service2`. Both are configured with health checks, but the interesting aspect here is the dependency of `service2` on the health status of `service1`, indicated by the `depends_on` clause.

### Health Check-Based Dependency (`depends_on`):
- `service2` uses the `depends_on` option to establish a dependency on `service1`.
- `condition: service_healthy`: This line specifies that `service2` should only start after `service1` has been considered "healthy" by Docker.

### Operation of `service1`'s Health Check in Relation to `service2`:
- **Starting of `service1`**: When the set of services is initiated with Docker Compose, `service1` begins its initialization process.
- **Execution of `service1` Health Check**: While `service1` is running, Docker performs health checks at intervals of 2 seconds (as per its configuration). If the service successfully responds to the health check command (in this case, a curl to `http://localhost:3000/ping`), it continues operating normally. If it fails, Docker will retry the health check up to 20 times, with a 60-second timeout per attempt.
- **Health Status of `service1` and Its Effect on `service2`**: If `service1` passes the health check and is marked as healthy, then Docker proceeds to start `service2`. If `service1` fails its health checks and is marked as unhealthy, `service2` will not start until `service1` is considered healthy.

## Example of Starting Up When `Service1` is Healthy:


![service 1 healthy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0doac5css8j6l80bkx18.png)



## Example of Starting Up When `Service1` is Unhealthy:


![Service 2 Unhealthy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r4nwj49vnqwn4alsbu0x.png)


This health-based dependency model is crucial in environments where services need to interact with each other and where the availability of one service depends on the state of another. It ensures that `service2` will not attempt to operate until `service1`, on which it depends, is fully functional and ready to handle requests or interact with other services. This improves the stability and reliability of the overall application deployment.

In this [repository](https://github.com/jjoc007/poc_docker_compose_multiservice), you will find the example ready to be deployed with Terraform. Feel free to download and try it out.

## References:

- **Example Repository**: [https://github.com/jjoc007/poc_docker_compose_multiservice](https://github.com/jjoc007/poc_docker_compose_multiservice)
- **Official Documentation**: [Docker Compose File Reference](https://docs.docker.com/compose/compose-file/compose-file-v3/)

If you liked this article, don't hesitate to give it a 👏 and ⭐ the repository.

🤔 **Follow me on social media!** ⏬

- [YouTube](https://www.youtube.com/jjoc007)
- [Twitter](https://twitter.com/jjoc007)
- [GitHub](https://github.com/jjoc007)
- [Website](https://jjoc007.com)
- [LinkedIn](https://www.linkedin.com/in/jjoc007/)

Thank you!