40  Docker Compose for Microservice

NoteDocker Compose
services:
  # AI Service 1 - Python FastAPI
  ai-service-1:
    build:
      context: ./ai-service-1
      dockerfile: Dockerfile
    container_name: acme-ai-service-1
    ports:
      - "8001:8001"
    volumes:
      # Mount source code for hot reload
      - ./ai-service-1:/app
      # Exclude virtual environment from mount (use container's packages)
      - /app/.venv
      # Exclude Python cache
      - /app/__pycache__
    environment:
      - PYTHONUNBUFFERED=1
    networks:
      - acme-network
    restart: unless-stopped

  # API Gateway - .NET ASP.NET Core
  api-gateway:
    build:
      context: ./AcmeApi/ApiGateway
      dockerfile: Dockerfile
    container_name: acme-api-gateway
    ports:
      - "5124:5124"
    volumes:
      # Mount source code for hot reload
      - ./AcmeApi/ApiGateway:/app
      # Exclude bin and obj directories (use container's build artifacts)
      - /app/bin
      - /app/obj
    environment:
      # Override service discovery to use Docker container name
      - ASPNETCORE_ENVIRONMENT=Development
      - DownstreamServices__AiService1__BaseUrl=http://ai-service-1:8001
      - ASPNETCORE_URLS=http://+:5124
      # Disable HTTPS redirect in Docker
      - ASPNETCORE_HTTPS_PORT=
    networks:
      - acme-network
    depends_on:
      - ai-service-1
    restart: unless-stopped

networks:
  acme-network:
    driver: bridge

Great! Let’s dive into Docker Compose for microservices. I’ll explain your configuration and the concepts behind it.

40.1 Architecture Overview

First, let me visualize what this Docker Compose setup creates:

┌─────────────────────────────────────────────────────────┐
│                    Host Machine                         │
│                    (Your Mac M3 Pro)                    │
│                                                         │
│  ┌────────────────────────────────────────────────────┐ │
│  │         Docker Network: acme-network               │ │
│  │              (bridge driver)                       │ │
│  │                                                    │ │
│  │  ┌─────────────────┐      ┌──────────────────┐     │ │
│  │  │  ai-service-1   │      │  api-gateway     │     │ │
│  │  │ (Python/FastAPI)│◄─────┤  (.NET/ASP.NET)  │     │ │
│  │  │                 │      │                  │     │ │
│  │  │  Port: 8001     │      │  Port: 5124      │     │ │
│  │  └─────────────────┘      └──────────────────┘     │ │
│  │         ▲                         ▲                │ │
│  └─────────┼─────────────────────────┼────────────────┘ │
│            │                         │                  │
│      localhost:8001            localhost:5124           │
└────────────┼─────────────────────────┼──────────────────┘
             │                         │
          Your Code                 Your Code
       (./ai-service-1)         (./AcmeApi/ApiGateway)

40.2 Key Docker Compose Concepts

40.2.1 1. Services Definition

Each service represents a containerized microservice. Your setup has two services:

  • ai-service-1: Python FastAPI backend (AI processing)
  • api-gateway: .NET API Gateway (entry point, routes requests)

40.2.2 2. Build Context

build:
  context: ./ai-service-1
  dockerfile: Dockerfile
  • context: Directory containing source code and Dockerfile
  • dockerfile: Instructions to build the container image
  • Docker builds the image from scratch when you run docker-compose up --build

40.2.3 3. Port Mapping

ports:
  - "8001:8001"  # HOST:CONTAINER

Format: "host_port:container_port"

Your Mac          Docker Container
   ┌───┐              ┌───┐
   │   │─── 8001 ────►│   │ Port 8001
   └───┘              └───┘

You access the service via http://localhost:8001 from your Mac.

40.2.4 4. Volumes (Critical for Development)

volumes:
  - ./ai-service-1:/app        # Source code mount
  - /app/.venv                 # Anonymous volume
  - /app/__pycache__           # Anonymous volume

Volume Types Explained:

┌─────────────────────────────────────────────────┐
│ Host Machine                                    │
│                                                 │
│  ./ai-service-1/                                │
│  ├── main.py          ─────┐                    │
│  ├── requirements.txt      │                    │
│  └── .venv/                │ (excluded)         │
│      └── (packages)        │                    │
└────────────────────────────┼────────────────────┘
                             │ Bind Mount
┌────────────────────────────┼────────────────────┐
│ Container                  │                    │
│                            ▼                    │
│  /app/                                          │
│  ├── main.py          (from host)               │
│  ├── requirements.txt (from host)               │
│  └── .venv/           (from container only!)    │
│      └── (packages)                             │
└─────────────────────────────────────────────────┘

Why exclude .venv and __pycache__?

  • .venv: Container has its own Python environment (possibly different OS/architecture)
  • __pycache__: Avoid conflicts between host and container bytecode
  • Your M3 Mac is ARM64, but container might be different architecture

This enables hot reload - edit code on your Mac, changes reflect immediately in container!

40.2.5 5. Environment Variables

environment:
  - ASPNETCORE_ENVIRONMENT=Development
  - DownstreamServices__AiService1__BaseUrl=http://ai-service-1:8001

Key insight: http://ai-service-1:8001

Inside the Docker network, services communicate using container names as hostnames:

api-gateway container can call:
  http://ai-service-1:8001/predict
  
NOT localhost:8001 (that would be api-gateway itself!)

40.2.6 6. Networks

networks:
  acme-network:
    driver: bridge

Bridge Network: Creates a private network where containers can discover each other by name.

┌──────────────────────────────────────────┐
│  acme-network (172.18.0.0/16)            │
│                                          │
│  ai-service-1                            │
│  IP: 172.18.0.2                          │
│  Hostname: ai-service-1                  │
│                                          │
│  api-gateway                             │
│  IP: 172.18.0.3                          │
│  Hostname: api-gateway                   │
│                                          │
│  (Docker DNS resolves names to IPs)      │
└──────────────────────────────────────────┘

40.2.7 7. Dependencies

depends_on:
  - ai-service-1
  • Docker Compose starts ai-service-1 before api-gateway
  • Note: Doesn’t wait for service to be “ready”, just “started”
  • For production, you might need health checks

40.2.8 8. Restart Policy

restart: unless-stopped

Container restarts automatically if it crashes, unless you explicitly stop it.

40.3 Request Flow Example

Let’s trace a request through your architecture:

1. External Client
   │
   │ HTTP GET http://localhost:5124/ai/predict
   │
   ▼
2. Host Machine (Port 5124)
   │
   │ Port mapping
   │
   ▼
3. api-gateway Container (Port 5124)
   │
   │ Internal routing logic
   │ Uses: DownstreamServices__AiService1__BaseUrl
   │
   ▼
4. Docker Network (acme-network)
   │
   │ DNS resolution: ai-service-1 → 172.18.0.2
   │
   ▼
5. ai-service-1 Container (Port 8001)
   │
   │ Process request with FastAPI
   │
   ▼
6. Response flows back through same path

40.4 Common Docker Compose Commands

# Start all services
docker-compose up

# Start in background (detached)
docker-compose up -d

# Rebuild images and start
docker-compose up --build

# Stop all services
docker-compose down

# View logs
docker-compose logs -f

# View logs for specific service
docker-compose logs -f ai-service-1

# Execute command in running container
docker-compose exec ai-service-1 bash

# List running services
docker-compose ps

40.5 Advantages of This Setup

1. Isolation: Each service runs independently with its own dependencies

2. Reproducibility: Same environment everywhere (Mac, Linux, CI/CD)

3. Development Speed: Hot reload - no need to rebuild containers for code changes

4. Service Discovery: Services find each other by name automatically

5. Easy Scaling: Can add more services easily