Running a multi-service app

In most cases, applications do not consist of only one monolithic block, but rather of several application services that work together. When using Docker containers, each application service runs in its own container. When we want to run such a multi-service application, we can of course start all the participating containers with the well-known docker container run command. But this is inefficient at best. With the Docker Compose tool, we are given a way to define the application in a declarative way in a file that uses the YAML format.

Let's have a look at the content of a simple docker-compose.yml file:

version: "3.5"
services:
web:
image: fundamentalsofdocker/ch08-web:1.0
ports:
- 3000:3000
db:
image: fundamentalsofdocker/ch08-db:1.0
volumes:
- pets-data:/var/lib/postgresql/data

volumes:
pets-data:

The lines in the file are explained as follows:

Navigate to the subfolder ch08 of the labs folder and start the application using Docker Compose:

$ docker-compose up

If we enter the preceding command, then the tool will assume that there must be a file in the current directory called docker-compose.yml and it will use that one to run. In our case, this is indeed the case and the application will start. We should see the output as follows:

Running the sample application, part 1
Running the sample application, part 2

The preceding output is explained as follows:

We can now open a browser tab and navigate to localhost:3000/pet. We should be greeted by a nice cat image and some additional information about the container it came from, as shown in the following screenshot:

The sample application in the browser

Refresh the browser a few times to see other cat images. The application selects the current image randomly from a set of 12 images whose URLs are stored in the database.

As the application is running in interactive mode and thus the Terminal where we ran Docker Compose is blocked, we can cancel the application by pressing Ctrl+C. If we do so, we will see the following:

^CGracefully stopping... (press Ctrl+C again to force)
Stopping ch08_web_1 ... done
Stopping ch08_db_1 ... done

We will notice that the database service stops immediately while the web service takes about 10 seconds to do so. The reason for this being that the database service listens to and reacts to the SIGTERM signal sent by Docker while the web service doesn't, and thus Docker kills it after 10 seconds.

If we run the application again, the output will be much shorter:

Output of docker-compose up

This time, we didn't have to download the images and the database didn't have to initialize from scratch, but it was just reusing the data that was already present in the volume pets-data from the previous run.

We can also run the application in the background. All containers will run as daemons. For this, we just need to use the -d parameter, as shown in the following code:

$ docker-compose up -d

Docker Compose offers us many more commands than just up. We can use it to list all services that are part of the application:

Output of docker-compose ps

This command is similar to docker container ls, with the only difference being that it only lists containers that are part of the application.

To stop and clean up the application, we use the docker-compose down command:

$ docker-compose down
Stopping ch08_web_1 ... done
Stopping ch08_db_1 ... done
Removing ch08_web_1 ... done
Removing ch08_db_1 ... done
Removing network ch08_default

If we also want to remove the volume for the database, then we can use the following command:

$ docker volume rm ch08_pets-data

Why is there a ch08 prefix in the name of the volume? In the docker-compose.yml file, we have called the volume to use pets-data. But as we have already mentioned, Docker Compose prefixes all names with the name of the parent folder of the docker-compose.yml file plus an underscore. In this case, the parent folder is called ch08.