Docker Compose
Docker Compose is a technology that allows you to locally run multi-container applications, i.e., one or more containers simultaneously. Creating a Docker Compose setup is a typical task for a DevOps engineer.
docker-compose is a plugin for Docker, as well as a file with a specifically defined notation. This plugin used to be a separate application, but Docker acquired it and integrated it with the rest of the system. docker-compose allows you to define containers that need to be started, dependencies between them, networks, etc.
docker-compose can be considered your first container orchestrator. An orchestrator is additional software that helps manage containers when there are many of them, or to configure some non-standard behavior.
Structure of docker-compose
docker-compose is a YML file. The notation of such a file is quite simple and somewhat similar to JSON, but without braces, for example:
docker-compose.yml
In this file, you first write a field, then a colon, then the field value. In the example, you can see the following fields:
version— the version of thedocker-composeplugin;networks— an optional field that describes Docker networks;services— the description and configuration of which container to run and which configurations to apply;volumes— volumes that can be attached in theservicessection.
Adding a network
Now let's make it so that a Docker network is created from docker-compose, which the containers will use:
Under the networks section, add a tab and write the network name. Be sure to put a colon so you don't break the syntax. Below, write the network driver type.
💡 bridge is the network type that Docker uses by default. Containers connected to this type of network will receive an IP address allocated from the common IP pool of that network. Containers connected to the same bridge network can communicate with each other using the internal IP address or container name.
💡 We may not know in advance what specific IP a container will receive, so the interaction between applications in different containers should be organized using domain names (the container name will serve as the domain name).
Adding a volume
First, let's define our volume in the corresponding section:
Volumes have different driver types. We will use the default driver type — local, which allows creating volumes on the container's host machine. To use the default driver type, you don't need to specify anything additional.
Adding a database
Let's add a database to services:
mysql:
image: my-sql:1.2
build:
context: .
dockerfile: Dockerfile.mysql
container_name: my-sql
ports:
- "3306:3306"
volumes:
- db-data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=1234
- MYSQL_DATABASE=app_db
- MYSQL_USER=app_user
- MYSQL_PASSWORD=1234
networks:
- db-data-net
Here:
- my-sql:1.2 — the Image name to download from Docker Hub or find locally.
- build — instructions that will be executed if the Image is not found locally. In other words, we describe the command, set the context and the path to the Dockerfile relative to docker-compose.
- volumes - we specify the driver and the path where to mount in the system
- ports - port mapping
- environment - we define environment variables.
- networks - we specify which network to use from the general networks section
Adding a Python application
For our application, we will set:
- Image, and if it doesn't exist, then
buildparameters. - Port mappings.
- The environment variable that we defined earlier in the
Dockerfilefor the Python application. - Network.
- The
depends_onparameter, which tellsdocker-composeto initiate the database startup first, but it will not wait for it to fully start, so we also add arestartpolicy.
pythonapp:
image: app:1.2
build:
context: .
dockerfile: Dockerfile.app
ports:
- "8080:8080"
environment:
- APP_ENV=Development
networks:
- db-data-net
depends_on:
- mysql
restart: unless-stopped
We also modify the connection string in the application:
There are two reasons why having a hardcoded container IP is not very convenient: 1. The IP may change. 2. We are running two containers at once. We will use the container name for the database:Time to run it all, but first let's clear all cache:
In the terminal, navigate to the directory with docker-compose and run the following command:
The -d parameter is equivalent to --detach and means that log output will be hidden. If you don't specify this parameter, the logs of both containers will be shown in the console.
Container status:
Container logs: To stop all containers: You can restart in one line: