Docker Compose for Multi-Container Applications

Docker Compose for Multi-Container Applications The Docker Way!

Alright, the elephant in the room multi-container applications. You know what I’m talking about no more struggling with managing multiple containers and their dependencies like a madman trying to solve a Rubik’s cube blindfolded. With Docker Compose, we can now orchestrate our multi-container apps in a breeze!

But first things first what is Docker Compose? Well, it’s basically a tool for defining and running multi-container Docker applications. It allows you to define your application as a series of services that work together in a single YAML file. This makes it easy to spin up an entire app with just one command!

Now let me show you how to use Compose to create a simple web stack consisting of a Nginx reverse proxy and a Flask app. First, we’ll need to create our directory structure:

# Create a directory named "my-app" and move into it
mkdir my-app && cd $_

# Create four files within the "my-app" directory: Dockerfile, nginx.conf, flask_app.py, and requirements.txt
touch Dockerfile nginx.conf flask_app.py requirements.txt

# Explanation: The "touch" command creates an empty file with the specified name. In this case, we are creating four files within the "my-app" directory.

# Dockerfile: This file will contain instructions for building our Docker image.
# nginx.conf: This file will contain the configuration for our Nginx reverse proxy.
# flask_app.py: This file will contain the code for our Flask app.
# requirements.txt: This file will contain a list of dependencies for our Flask app.

Next, let’s add some content to each file:

Dockerfile:

# This dockerfile sets up a python environment and runs a flask application

# Use the python 3.8 slim buster image as the base image
FROM python:3.8-slim-buster

# Set the working directory to /app
WORKDIR /app

# Copy all files from the current directory to the /app directory in the container
COPY . /app

# Install the dependencies listed in requirements.txt using pip
RUN pip install --no-cache-dir -r requirements.txt

# Set the command to run when the container is started, in this case, run the flask application
CMD ["python", "flask_app.py"]

nginx.conf:

# This is a server block that listens on port 80 and handles requests for the domain my-app.com and www.my-app.com
server {
    listen 80;
    server_name my-app.com www.my-app.com;
    
    # This location block handles requests for the root directory
    location / {
        # This directive passes the request to the Flask application running on port 5000
        proxy_pass http://flask_app:5000;
        # These directives set the headers for the request, including the host, real IP, and scheme
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
    }
}

flask_app.py:

# Importing necessary modules
from flask import Flask # Importing Flask module from flask library
import os # Importing os module

# Creating an instance of Flask
app = Flask(__name__) # Creating an instance of Flask class and assigning it to variable 'app'

# Defining a route and function
@app.route('/') # Decorator that maps the URL '/' to the function below
def hello(): # Defining a function named 'hello'
    return 'Hello, World!' # Returning a string 'Hello, World!' when the route is accessed

# Running the app
if __name__ == "__main__": # Checking if the script is being run directly
    app.run(debug=True) # Running the app in debug mode if the above condition is met

requirements.txt:

# This is a requirements file, used to specify the dependencies for a Python project.

# The following line specifies the Flask library, version 1.0.2, as a requirement for the project.
Flask==1.0.2

Now let’s create our Compose file (docker-compose.yml):

version: '3' # Specifies the version of the Compose file being used

services: # Defines the services that will be created
  flask_app: # Defines the first service named "flask_app"
    build: . # Specifies the build context for the service, in this case the current directory
    ports: # Specifies the ports to be exposed by the service
      - "5000:5000" # Maps the host port 5000 to the container port 5000
    depends_on: # Specifies the services that this service depends on
      - nginx # In this case, the flask_app service depends on the nginx service
  nginx: # Defines the second service named "nginx"
    image: nginx:latest # Specifies the image to be used for the service
    volumes: # Specifies the volumes to be mounted for the service
      - ./nginx.conf:/etc/nginx/conf.d/default.conf # Mounts the nginx.conf file from the current directory to the container's default.conf file

Explanation time! We’re defining two services here flask_app and nginx. The `build:` option tells Compose to build an image from our Dockerfile in the current directory (`.`). The `ports:` option maps port 5000 on the container to port 5000 on the host machine, so we can access it locally.

The `depends_on:` option specifies that nginx should start before flask_app. This is important because our Flask app needs to be accessed through Nginx’s reverse proxy.

Finally, for nginx, we’re using the official Nginx image and mounting our custom configuration file (nginx.conf) in a volume so that it persists across container restarts. This allows us to easily modify the configuration without having to rebuild the image or recreate the container every time.

Docker Compose is an incredibly powerful tool for managing multi-container applications, and its simplicity makes it easy to use even for beginners. By defining our application as a series of services in a YAML file, we can easily spin up and manage multiple containers with just one command!

SICORPS