1. Overview
docker-compose is a popular tool for defining and running multi-container applications.
In this tutorial, we’ll focus on how to use docker-compose to check whether a container is running.
2. Setup
In this section, we’ll build a sample project that uses a docker-compose.yml file for containerization.
First, let’s use the exa command to look at the project’s directory structure in a tree format:
$ exa --tree .
.
├── backend
│ ├── app1
│ │ ├── app1.sh
│ │ └── Dockerfile
│ └── app2
│ ├── app2.sh
│ └── Dockerfile
└── docker-compose.yml
Next, let’s write two scripts, namely, app1.sh and app2.sh, representing two different demo applications:
$ tail -n +1 backend/app1/app1.sh backend/app2/app2.sh
==> backend/app1/app1.sh <==
#!/bin/bash
while :
do
echo "app1 is running ..."
sleep 60
done
==> backend/app2/app2.sh <==
#!/bin/bash
while :
do
echo "app2 is running ..."
sleep 60
done
Now, we’ll use Ubuntu’s base image for writing the Dockerfile for these two applications:
$ tail -n +1 backend/app{1,2}/Dockerfile
==> backend/app1/Dockerfile <==
FROM ubuntu
COPY --chmod=700 app1.sh app1.sh
ENTRYPOINT /app1.sh
==> backend/app2/Dockerfile <==
FROM ubuntu
COPY --chmod=700 app2.sh app2.sh
ENTRYPOINT /app2.sh
Moving on, let’s see the docker-compose.yml to orchestrate the containerization of these two services:
$ cat docker-compose.yml
version: '2'
services:
app1:
build: backend/app1
container_name: demo_app1_container
app2:
build: backend/app2
container_name: demo_app2_container
Finally, let’s use the docker-compose build command to build the services:
$ docker-compose build --no-cache
Let’s note that we’ll reuse this demo project to simulate different scenarios in this tutorial.
3. Using docker-compose ps
Let’s start by spawning up the services using the docker-compose up command:
$ docker-compose up -d
[+] Running 3/3
⠿ Network lnx-1370_default Created 0.0s
⠿ Container demo_app2_container Started 0.4s
⠿ Container demo_app1_container Started 0.4s
On execution, the containers are running in detached mode. So, we don’t have any visibility into the current state of the services.
Next, let’s see how we can use the docker-compose ps command to list all the containers:
$ docker-compose ps --all
NAME COMMAND SERVICE STATUS PORTS
demo_app1_container "/bin/sh -c /app1.sh" app1 running
demo_app2_container "/bin/sh -c /app2.sh" app2 running
We must note that the output contains the STATUS column denoting the current status of the container.
Finally, let’s also see how we can use the –status filter to show the running containers for the app1 service:
$ docker-compose ps --status=running app1
NAME COMMAND SERVICE STATUS PORTS
demo_app1_container "/bin/sh -c /app1.sh" app1 running
Great! With this, we can selectively see the running containers for a given service.
4. Using docker-compose exec
In this section, we’ll see how to use the docker-compose exec command to check whether a service has a running container.
First, let’s simulate a scenario where one of the services is down by manually stopping the app2 service:
$ docker-compose stop app2
[+] Running 1/1
⠿ Container demo_app2_container Stopped 10.2s
Next, let’s use the docker-compose exec command to execute a trivial echo command inside the container for service app1:
$ docker-compose exec app1 echo "app1 is up"
app1 is up
We can see that the service app1 is still running, so the echo command was completed successfully.
Finally, let’s also try to execute the echo command inside the container for service app2:
$ docker-compose exec app2 echo "app2 is up"
service "app2" is not running container #1
$ echo $?
1
As expected, the echo command couldn’t execute because there were no running containers for service app2. Additionally, the exit status of the docker-compose exec command is non-zero.
5. Using healthcheck in docker-compose.yml
Using the docker-compose ps and docker-compose exec commands, we can get to know the current status of the container. However, it doesn’t guarantee that the target application running inside the container is in a healthy state. In this section, we’ll learn to solve this well-known problem by adding a healthcheck for the target application within the docker-compose.yml file.
First, let’s determine a valid healthcheck for our demo application by checking if an active process is associated with the app1.sh script:
$ ps -elf | grep 'app1.sh' | grep -v grep || exit 1
It’s important to exclude the process running grep itself from the output using the grep -v command in the pipeline. Further, we’re exiting with a non-zero status if we fail to find any active process for app1.sh.
Now, let’s define this healthcheck for the app1 service in the docker-compose.yml file:
$ cat docker-compose.yml
version: '2'
services:
app1:
build: backend/app1
container_name: demo_app1_container
healthcheck:
test: ["CMD-SHELL", "ps -elf | grep 'app1.sh' | grep -v grep || exit 1"]
app2:
build: backend/app2
container_name: demo_app2_container
We must note that the CMD-SHELL will execute the healthcheck command using the /bin/sh shell.
Next, let’s shutdown the services and bring them up again using the docker-compose down and docker-compose up commands, respectively:
$ docker-compose down
[+] Running 3/2
⠿ Container demo_app1_container Removed ... 10.2s
⠿ Container demo_app2_container Removed ... 10.2s
⠿ Network lnx-1370_default Removed ... 0.0s
$ docker-compose up -d
[+] Running 3/3
⠿ Network lnx-1370_default Created ... 0.0s
⠿ Container demo_app1_container Started ... 0.4s
⠿ Container demo_app2_container Started ... 0.3s
Finally, let’s use the docker-compose ps commands to check the status of the services:
$ docker-compose ps
NAME COMMAND SERVICE STATUS PORTS
demo_app1_container "/bin/sh -c /app1.sh" app1 running (healthy)
demo_app2_container "/bin/sh -c /app2.sh" app2 running
Although the STATUS column for service app1 also shows the health status. However, this output format can change depending on the docker-compose version. So, let’s learn a machine-friendly approach to get the health status in the next section.
6. Using docker inspect
When we know the container ID of the target service, we can use the docker inspect command to get the status of the container’s current state.
First, let’s see how we can use the -q option with the docker-compose ps command to retrieve the container ID for the app1 and app2 services:
$ docker-compose ps -q app1
04fa0e3dffd6e7119c8be6194a59bd5fef2b9598d096331357a4c51dfd010831
$ docker-compose ps -q app2
f88fdf249b31483bd8cd5353a6d11d2ff3f5a0f0c2756cc805e4c67246ee91b5
Next, let’s use the docker inspect command to get the status of the app1 service:
$ docker inspect --format "{{.State.Status}}" 04fa0e3dffd6e7119c8be6194a59bd5fef2b9598d096331357a4c51dfd010831
running
$ docker inspect --format "{{.State.Health.Status}}" 04fa0e3dffd6e7119c8be6194a59bd5fef2b9598d096331357a4c51dfd010831
healthy
Great! This approach successfully retrieves the container’s current state and health status for app1.
Finally, let’s see if we can use the same approach to get the status of the app2 service:
$ docker inspect --format "{{.State.Status}}" f88fdf249b31483bd8cd5353a6d11d2ff3f5a0f0c2756cc805e4c67246ee91b5
running
$ docker inspect --format "{{.State.Health.Status}}" f88fdf249b31483bd8cd5353a6d11d2ff3f5a0f0c2756cc805e4c67246ee91b5
Template parsing error: template: :1:8: executing "" at <.State.Health.Status>: map has no entry for key "Health"
$ echo $?
1
Unfortunately, the attempt to get the health status for the app2 service fails with a non-zero exit status because we haven’t defined its healthcheck.
7. Conclusion
In this tutorial, we learned to use the docker-compose command to check whether a container is running. Additionally, we learned how to make such checks more effective by adding a healthcheck for the services.