Community for developers to learn, share their programming knowledge. Register!
Docker Compose: Simplifying Multi-Container Applications

Setting Up a Example Multi-Container Application in Docker


In this article, you can get training on setting up a multi-container application using Docker. Docker is a powerful tool that simplifies the process of managing multiple services within a single application. By using Docker and Docker Compose, developers can streamline deployment and environment configuration, making it easier to develop and manage applications.

Creating a docker-compose.yml File

To get started with Docker Compose, the first step is to create a compose file. This file defines the services that make up your application, their configurations, and the relationships between them. The structure of the compose file is crucial as it provides Docker with the necessary instructions to build and run your application.

Here’s a basic example of what a docker-compose.yml file might look like for a simple web application with a frontend and a backend service:

services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
    networks:
      - my-network
  
  db:
    image: postgres:latest
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    networks:
      - my-network
 
  backend:
    build: ./backend
    ports:
      - "3000:3000"
    networks:
      - my-network

In this example, we define three services: web, backend, and db (the database).

Defining Services in Your Application

Each service in the docker-compose.yml file represents a specific component of your application. You can define various properties, such as image, build, ports, and volumes.

  • Image: Specifies which Docker image to use for the service. In the frontend, we use the official Nginx image.
  • Build: Indicates the context for building a custom Docker image. In this case, the backend service will build an image from the Dockerfile located in the ./backend directory.
  • Ports: Maps the container's ports to the host machine's ports. This allows you to access the services from your local machine.
  • Volumes: Used to persist data or share files between the host and the container. For instance, the frontend service mounts the local ./frontend directory to the container’s HTML directory.

Example of a Dockerfile for the Backend Service

To build the backend service, you need a Dockerfile. Here’s a simple example using Flask:

# Use the official Python image from Docker Hub
FROM python:3.8-slim

# Set the working directory
WORKDIR /app

# Copy requirements.txt and install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy the application code
COPY . .

# Expose the port the app runs on
EXPOSE 5000

# Command to run the application
CMD ["python", "app.py"]

This Dockerfile sets up a minimal Python environment for a Flask application. The dependencies are installed, and the application is copied into the container before it starts.

Configuring Networking for Multi-Container Setup

Docker Compose creates a default network for your application, allowing all services to communicate with each other. This is particularly useful for microservices architectures, where different services need to interact.

To configure networking, you can add a networks section to your docker-compose.yml file:

services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
    networks:
      - my-network
  
  db:
    image: postgres:latest
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    networks:
      - my-network

  backend:
    build: ./backend
    ports:
      - "3000:3000"
    networks:
      - my-network

networks:
  my-network:
    driver: bridge

In this example, all services are connected to a custom network named my-network. This allows them to communicate with each other while keeping them isolated from other Docker containers on different networks.

Each service in the docker-compose.yml can communicate with others using their service names. For example, if the backend service needs to connect to the database, it can use db as the hostname:

import os
import psycopg2

connection = psycopg2.connect(
    dbname=os.environ['POSTGRES_DB'],
    user=os.environ['POSTGRES_USER'],
    password=os.environ['POSTGRES_PASSWORD'],
    host='db'  # Referring to the db service
)

This configuration simplifies connection management and reduces the need for hardcoding IP addresses.

Managing Data with Volumes

Volumes are essential for managing persistent data in Docker containers. They allow you to store data outside of the container's filesystem, ensuring that it isn’t lost when containers are stopped or removed.

In our example, you can add a volume to the database service to persist PostgreSQL data:

version: '3.8'

services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
  
  db:
    image: postgres:latest
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      - db_data:/var/lib/postgresql/data

  backend:
    build: ./backend
    ports:
      - "3000:3000"

volumes:
  db_data:

This configuration will create a named volume called db_data that stores the database’s data files, ensuring data persistence across container restarts.

Building and Running Your Multi-Container Application

To build and run the entire application, navigate to the directory containing your compose file and execute the following command:

docker compose up --build
docker-compose up --build -d # To run the containers in detached mode

The --build flag ensures that Docker Compose rebuilds the images if there have been any changes to the Dockerfiles or application code. Once the containers are running, you can access the frontend service by navigating to http://localhost in your web browser.

To stop the application, you can simply press CTRL+C in the terminal. If you want to remove the containers and networks created by Docker Compose, you can use:

docker compose down

Scaling Services with Docker Compose

One of the powerful features of Docker Compose is the ability to scale services easily. If you want to run multiple instances of your backend service for load balancing, you can use the --scale option:

docker compose up --scale backend=3

This command will launch three instances of the backend service, allowing it to handle more requests simultaneously. You can also define a replicas key within the service definition in the compose file for more permanent scaling configurations.

backend:
  deploy:
    replicas: 3

Summary

In this article, we walked through the process of setting up a multi-container application using Docker. We began by creating a docker-compose.yml file to define our services and their configurations. We then explored networking, data management using volumes, and how to build and run the application.

Last Update: 21 Jan, 2025

Topics:
Docker