Skip to content

Docker Compose

This chapter will introduce Docker Compose in detail, teaching you how to use YAML files to define and manage multi-container applications, achieving container orchestration and service management.

Introduction to Docker Compose

What is Docker Compose?

Docker Compose is a tool for defining and running multi-container Docker applications. By using a YAML file to configure application services, you can then create and start all services with a single command.

Core Concepts

  • Service: A component of the application, such as a web server, database, etc.
  • Project: A complete application consisting of a set of related services
  • Network: Network environment for service-to-service communication
  • Volume: Storage for data persistence and sharing

Advantages of Docker Compose

  1. Simplify deployment: Start the entire application stack with one command
  2. Environment isolation: Each project has independent networks and namespaces
  3. Service discovery: Containers can communicate with each other by service name
  4. Configuration management: Unified management of all service configurations
  5. Scalability: Easily scale the number of service instances

Installing Docker Compose

Linux Installation

bash
# Method 1: Download using curl
sudo curl -L "https://github.com/docker/compose/releases/download/v2.12.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

# Method 2: Install using pip
pip3 install docker-compose

# Method 3: Use package manager (Ubuntu)
sudo apt-get update
sudo apt-get install docker-compose-plugin

# Verify installation
docker-compose --version

Windows and macOS

Docker Desktop already includes Docker Compose, no separate installation needed.

docker-compose.yml File Structure

Basic Structure

yaml
version: '3.8'  # Compose file version

services:       # Define services
  web:
    # Service configuration
  db:
    # Service configuration

volumes:        # Define volumes (optional)
  # Volume configuration

networks:       # Define networks (optional)
  # Network configuration

Version Compatibility

Compose VersionDocker Engine Version
3.819.03.0+
3.718.06.0+
3.618.02.0+
3.517.12.0+

Service Configuration Details

Basic Service Configuration

yaml
version: '3.8'

services:
  web:
    # Use existing image
    image: nginx:latest

    # Or build image
    build: .
    # build:
    #   context: .
    #   dockerfile: Dockerfile

    # Container name
    container_name: my-web-server

    # Port mapping
    ports:
      - "8080:80"
      - "8443:443"

    # Environment variables
    environment:
      - NODE_ENV=production
      - DEBUG=false

    # Volume mounts
    volumes:
      - ./html:/usr/share/nginx/html
      - logs:/var/log/nginx

    # Networks
    networks:
      - frontend

    # Dependencies
    depends_on:
      - db

    # Restart policy
    restart: unless-stopped

Build Configuration

yaml
services:
  web:
    build:
      context: .                    # Build context
      dockerfile: Dockerfile.prod   # Specify Dockerfile
      args:                        # Build arguments
        - VERSION=1.0
        - BUILD_DATE=2023-01-01
      target: production           # Multi-stage build target
      cache_from:                  # Cache source
        - myapp:cache
    image: myapp:latest           # Image name after build

Environment Variable Configuration

yaml
services:
  web:
    # Method 1: Direct definition
    environment:
      - NODE_ENV=production
      - PORT=3000
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb

    # Method 2: Use object format
    environment:
      NODE_ENV: production
      PORT: 3000
      DATABASE_URL: postgresql://user:pass@db:5432/mydb

    # Method 3: Read from file
    env_file:
      - .env
      - .env.local

    # Method 4: Inherit from host environment
    environment:
      - NODE_ENV
      - DATABASE_URL=${DATABASE_URL}

Volume Configuration

yaml
services:
  web:
    volumes:
      # Bind mount
      - ./src:/app/src
      - ./config.json:/app/config.json:ro  # Read-only

      # Named volume
      - app-data:/app/data
      - logs:/var/log

      # Anonymous volume
      - /app/node_modules

      # tmpfs mount
      - type: tmpfs
        target: /tmp
        tmpfs:
          size: 100M

# Define named volumes
volumes:
  app-data:
    driver: local
  logs:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /host/logs

Network Configuration

yaml
services:
  web:
    networks:
      - frontend
      - backend

  db:
    networks:
      - backend

# Define networks
networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true  # Internal network, cannot access external

Dependencies and Startup Order

yaml
services:
  web:
    depends_on:
      - db
      - redis
    # Or use conditional dependencies (requires healthcheck)
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

  db:
    image: postgres:13
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s

  redis:
    image: redis:alpine

Real-World Application Examples

Example 1: Web Application + Database

yaml
version: '3.8'

services:
  # Web application service
  web:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
    depends_on:
      - db
    volumes:
      - ./uploads:/app/uploads
    networks:
      - app-network
    restart: unless-stopped

  # PostgreSQL database
  db:
    image: postgres:13
    environment:
      - POSTGRES_DB=myapp
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    networks:
      - app-network
    restart: unless-stopped

  # Redis cache
  redis:
    image: redis:alpine
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data
    networks:
      - app-network
    restart: unless-stopped

volumes:
  postgres_data:
  redis_data:

networks:
  app-network:
    driver: bridge

Example 2: Microservices Architecture

yaml
version: '3.8'

services:
  # Nginx reverse proxy
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - api
      - web
    networks:
      - frontend
    restart: unless-stopped

  # Frontend application
  web:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    networks:
      - frontend
    restart: unless-stopped

  # API service
  api:
    build:
      context: ./backend
      dockerfile: Dockerfile
    environment:
      - DATABASE_URL=postgresql://postgres:password@db:5432/api
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis
    networks:
      - frontend
      - backend
    restart: unless-stopped

  # Database
  db:
    image: postgres:13
    environment:
      - POSTGRES_DB=api
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
    volumes:
      - db_data:/var/lib/postgresql/data
    networks:
      - backend
    restart: unless-stopped

  # Cache
  redis:
    image: redis:alpine
    volumes:
      - redis_data:/data
    networks:
      - backend
    restart: unless-stopped

volumes:
  db_data:
  redis_data:

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    internal: true

Docker Compose Commands

Basic Commands

bash
# Start services
docker-compose up

# Start in background
docker-compose up -d

# Start specific services
docker-compose up web db

# Build images
docker-compose build

# Build and start
docker-compose up --build

# Stop services
docker-compose stop

# Stop and remove containers
docker-compose down

# Stop and remove containers, networks, volumes
docker-compose down -v

# View service status
docker-compose ps

# View logs
docker-compose logs

# View logs in real-time
docker-compose logs -f

# View specific service logs
docker-compose logs web

# Enter container
docker-compose exec web bash

# Run one-time command
docker-compose run web npm install

# Scale service instances
docker-compose up --scale web=3

# Restart service
docker-compose restart

# Pause service
docker-compose pause

# Resume service
docker-compose unpause

Advanced Commands

bash
# Validate configuration file
docker-compose config

# Display configuration (after variable resolution)
docker-compose config --services

# Pull images
docker-compose pull

# Push images
docker-compose push

# View port mapping
docker-compose port web 80

# View service processes
docker-compose top

# Send signal
docker-compose kill -s SIGINT web

# Remove stopped containers
docker-compose rm

# Force remove
docker-compose rm -f

Environment Management

Multi-Environment Configuration

bash
# Development environment
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up

# Production environment
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up

# Testing environment
docker-compose -f docker-compose.yml -f docker-compose.test.yml up

Configuration File Examples

docker-compose.yml (base configuration):

yaml
version: '3.8'

services:
  web:
    build: .
    volumes:
      - ./app:/app
    networks:
      - app-network

  db:
    image: postgres:13
    networks:
      - app-network

networks:
  app-network:

docker-compose.dev.yml (development environment override):

yaml
version: '3.8'

services:
  web:
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
    command: npm run dev

  db:
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_DB=devdb
      - POSTGRES_USER=dev
      - POSTGRES_PASSWORD=devpass

docker-compose.prod.yml (production environment override):

yaml
version: '3.8'

services:
  web:
    ports:
      - "80:3000"
    environment:
      - NODE_ENV=production
    restart: unless-stopped

  db:
    environment:
      - POSTGRES_DB=proddb
      - POSTGRES_USER=prod
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    volumes:
      - prod_db_data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  prod_db_data:

Health Checks and Dependency Management

Health Checks

yaml
version: '3.8'

services:
  web:
    image: nginx
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s

  db:
    image: postgres
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 30s
      timeout: 5s
      retries: 5

Chapter Summary

This chapter comprehensively introduced Docker Compose usage:

Key Points:

  • Service orchestration: Use YAML files to define multi-container applications
  • Network management: Service discovery and network isolation
  • Data management: Volumes and persistent storage
  • Environment management: Multi-environment configuration and variable management
  • Scalability: Service scaling and load balancing
  • Monitoring and debugging: Log management and troubleshooting

Best Practices:

  • Use version control to manage Compose files
  • Reasonably design service dependencies
  • Use health checks to ensure service availability
  • Separate configuration and code
  • Regularly backup important data

In the next chapter, we will learn about Docker security best practices, including image security, container security, and network security.

Further Reading

Content is for learning and research only.