From Dockerfile to Distroless - Container LabStep 6 of 12: Orchestrating multiple containers
All Labs

Orchestrating multiple containers

So far, we only had one container running on our machine. In the next step, we'd like to have our application container and a database running there. Our Boss had this in mind and created an option to switch between a SQLite database and a MariaDB as a database backend. All we need is a good approach to make this work.

A good rule of thumb is to run one application or service per container so each can scale independently. In our case, that means running the database in a separate container from the application.

To manage multiple containers together, we'll use Docker Compose, a simple tool for defining and running multi-container applications.

Modern Docker installations include Docker Compose v2, which you run with a space:

docker compose 

Older installations may still use the legacy standalone binary, which uses a hyphen:

docker-compose
💡Quick Self Check

Before continuing, run:

docker compose version || docker-compose version
  • If the first command works → you have Compose v2, and can follow the lab exactly as written.

  • If only the second works → replace docker compose with docker-compose in all commands.

  • If neither works → follow the official installation guide before proceeding.

Docker Compose will help us to create deployments consisting of multiple containers, so that we could run our application together with a database container. The tool is typically configured using a docker-compose.yaml file that describes our application. Let's start very small and put our current app into a docker-compose.yaml file. Please open your favorite IDE and create a file called docker-compose.yaml in the root of your checked out repository. Add the following configuration in it:

services:
  learning-app:
    container_name: learning-app
    build: .
    environment:
      LOG_OUTPUT: "console"
    ports:
      - "8888:3000"

If you take a closer look at it, this configuration has everything we put into our docker command before, but in a more declarative format. After we created this file, we can run this docker-compose setup using the command:

docker compose up

When executing this one, you should see the log output and be able to navigate to the app via http://localhost:8888. You can stop this using Ctrl+C.

We would like to change the database to a MariaDB that is running alongside our learning-app. To achieve this, we need to run the database in a second container. To add this container, please change your docker-compose.yaml file that it matches this configuration:

services:
  learning-app:
    build: .
    restart: always
    environment:
      LOG_OUTPUT: "console"
      DB_TYPE: "mysql"
      DB_USER: "app"
      DB_PASSWORD: "my_password"
      DB_HOST: "mariadb"
    ports:
      - "8888:3000"
  mariadb:
    image: mariadb
    environment:
      MARIADB_USER: "app"
      MARIADB_RANDOM_ROOT_PASSWORD: "yes"
      MARIADB_PASSWORD: "my_password"
      MARIADB_DATABASE: "learning.db"

There are lots of new things inside of this configuration, let's take a closer look at them.

At first, we created a new MariaDB service to the configuration, which will start our database. According to the documentation, the container image takes a MARIADB_USER, MARIADB_PASSWORD, and a MARIADB_DATABASE as environments for configuration. In our app.js, the default name for the database defaults to "learning.db", our very secure password is my_password. Furthermore, defined services in a docker compose file are accessible with their service-name as a hostname (in our case this is mariadb), which is added as an environment variable in our learning-app. Furthermore, we can be sure that the database takes longer to start up as our application, as our application is dependent on our database, we would have the option to wait until the health check of the database is successful (which we will not do in this lab), or restart until everything is ok (which matches the behavior of kubernetes).

Additional Learning

If you are curious, try to find out how to define a health check in Compose and use and work with the depends_on in the docker-compose.yaml

Before starting this, ensure that the old deployment is stopped (Ctrl-C) and also delete the deployment including all created volumes using the command:

docker compose down -v

Afterward, start the new deployment:

docker compose up

You should see much log output, and in the end a message that your service is listening on port 3000, which is mapped to the local port 8888 in our case. When browsing to this page, you should see our learning-tracker again and be able to add new goals and move them through the columns.

🎉You finished the next milestone!

By now, you have a pretty solid setup of a container logging to stdout, using an external database. By configuring the containers via environment variables, you are also respecting the configuration factor of the Twelve-Factor App.

Well done!