Learning docker compose
Learning docker compose
In this post, I will explain how to do a simple application using docker-compose. It will be a Flask web application accessing a MariaDB database.
It will be simple, without data or any migration with the database. The focus in this post is to understand the docker-compose.
Flask application
I will use Flask to make a simple small application. It will have 2 endpoints. One is a “hello” static message and the other do a simple request to the database.
The structure will be the following:
├── application
│ ├── Dockerfile
│ └── src
│ ├── app.py
│ └── requirements.txt
└── docker-compose.yml
- Dockerfile contains our image code.
- app.py is our application.
- requirements.txt have all packages required by our application.
- docker-compose.yml orchestrate our two services (application and the database).
The example code is hosted in GitLab
app.py
Our application will have these 2 endpoints: /
and /db
.
import os
import pymysql.cursors
from flask import Flask
connection = pymysql.connect(host='database',
user=os.getenv('MYSQL_USER'),
password=os.getenv('MYSQL_PASSWORD'),
db='application_db',
charset='utf8mb4',
cursorclass=pymysql.cursors.DictCursor)
app = Flask(__name__)
@app.route('/')
def hello():
return f'Application /'
@app.route('/db')
def db():
cur = connection.cursor()
cur.execute("SELECT VERSION() AS db_version")
version = cur.fetchone()
return (f'Database /db<br />\n'
f'Version of database: {version["db_version"]}')
The application uses the first examples found for flask and pymysql.
The /
endpoint is just printing a simple text and /db
is connecting to the server and getting the version.
requirements.txt
Flask==1.1.1
PyMySQL==0.9.3
This file only contains the project dependencies.
Dockerfile
The Dockerfile has comments to help understand what it is doing. It is copying the src
code to the image, installing the requirements and setting the startup command.
FROM python:3.7
# Copy full application to the image
# For real environments, consider customize .dockerignore
COPY src /opt/application
# This is to make it easier. The default PWD where you
# start when using your application
WORKDIR /opt/application
RUN pip install -r requirements.txt
# Run default flask, but allow remote access
CMD flask run --host=0.0.0.0
docker-compose.yml
This file will list our services and define how it integrates.
version: "3.7"
services:
application:
build: application
links:
- database
ports:
- "5000:5000"
environment:
FLASK_DEBUG: "1"
MYSQL_USER: "application"
MYSQL_PASSWORD: "password"
MYSQL_DATABASE: "application_db"
volumes:
- ./application/src:/opt/application
database:
image: mariadb:10.4
environment:
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
MYSQL_DATABASE: "application_db"
MYSQL_USER: "application"
MYSQL_PASSWORD: "password"
For the database service, we are setting the image used and the environment variables that should be used. We don’t need modifications on it.
But we do need modifications in our application (described in our Dockerfile).
build: application
defines where our Dockerfile is located. It will create an image based on it.links:
is a list of the connections between the services. Our application needs to connect to the database.ports:
is a list of the ports open to your host (your computer). In the example, if I accesslocalhost:5000
at my local browser it will respond from thelocalhost:5000
of the container.environment:
is a list of all the environment variables passed to the container.volumes:
is the list of volumes used. Here, I am mounting the source code in the container image. The reason for that is because I did it as a development environment. If I change it in my host, it will change in the container, too. It reloads in flask also because of my env varFLASK_DEBUG: "1"
.
Running
With all files there, you just need to run docker-compose up
.
Static page
Page requesting to the database
Some tricks
Run in background
You can run docker-compose up -d
. So, your terminal is not locked to the docker. If you want to see the logs, just run docker-compose logs
(to see all logs), docker-compose logs -f
(to see messages and follow the output) or docker-compose logs application
(to filter the logs of a service).
Compose network
Not sure if you realized that, but we didn’t need to map the database host. Docker already created an internal network and mapped properly.
It happened because we linked the database:
links:
- database
And we used the same name in the database hostname in the application.
host='database',
Debug container
You can also connect to the container to inspect it. You can run docker-compose exec application bash
.
Change ports
You don’t need to be using port 5000 in your host. You can set a different one in any of those. (e.g. in docker-compose.yml
file, could change to 8000:5000
)
Changing the Dockerfile
If you make changes that affect the image, you will need to re-build the image.
For example, if you start the project (docker-compose up
) and update the requirements.txt
.
You can do that running docker-compose build
.