正在加载,请稍候…

Docker Compose for Production: Multi-Service Apps, Health Checks, and Secrets

Use Docker Compose in production with health checks, secrets management, resource limits, custom networks, volume management, and rolling deployments.

Docker Compose for Production

While Kubernetes handles large-scale deployments, Docker Compose is excellent for small to medium production workloads. This guide covers production-grade configurations.

Production-Ready docker-compose.yml

services:
  api:
    image: myapp/api:${TAG:-latest}
    restart: unless-stopped
    
    # Resource limits
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 512M
        reservations:
          memory: 256M
      replicas: 2
    
    # Health check
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    
    environment:
      NODE_ENV: production
      PORT: 3000
    
    # Secrets (not in environment!)
    secrets:
      - db_password
      - jwt_secret
    
    networks:
      - frontend
      - backend
    
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  postgres:
    image: postgres:15-alpine
    restart: unless-stopped
    
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5
    
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: myapp
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    
    secrets:
      - db_password
    
    networks:
      - backend

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --appendonly yes --requirepass-file /run/secrets/redis_password
    
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
    
    volumes:
      - redis_data:/data
    
    secrets:
      - redis_password
    
    networks:
      - backend

  nginx:
    image: nginx:alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./certs:/etc/nginx/certs:ro
    networks:
      - frontend
    depends_on:
      - api

secrets:
  db_password:
    file: ./secrets/db_password.txt
  jwt_secret:
    file: ./secrets/jwt_secret.txt
  redis_password:
    file: ./secrets/redis_password.txt

volumes:
  postgres_data:
  redis_data:

networks:
  frontend:
  backend:
    internal: true  # Not accessible from outside

Reading Secrets in Application

// Node.js: read Docker secrets from /run/secrets/
const fs = require('fs');

function getSecret(name) {
  const secretPath = `/run/secrets/${name}`;
  try {
    return fs.readFileSync(secretPath, 'utf8').trim();
  } catch {
    // Fallback to environment variable for local dev
    return process.env[name.toUpperCase()];
  }
}

const config = {
  dbPassword: getSecret('db_password'),
  jwtSecret: getSecret('jwt_secret'),
};

Rolling Updates

# Pull new image
docker compose pull api

# Rolling restart (update one replica at a time)
docker compose up -d --no-deps --scale api=2 api

# Or use update order
docker compose up -d api
# Compose will stop old container after new one is healthy

Backup and Restore

# Backup PostgreSQL
docker compose exec postgres pg_dump -U myapp myapp | gzip > backup-$(date +%Y%m%d).sql.gz

# Restore
gunzip -c backup-20240101.sql.gz | docker compose exec -T postgres psql -U myapp myapp

# Backup volumes
docker run --rm   -v myapp_postgres_data:/data   -v $(pwd)/backups:/backup   alpine tar czf /backup/postgres-$(date +%Y%m%d).tar.gz /data

Environment-Specific Overrides

# docker-compose.override.yml (auto-loaded in development)
services:
  api:
    build: .  # Build from source in dev
    volumes:
      - .:/app  # Hot reload
    environment:
      NODE_ENV: development
    ports:
      - "9229:9229"  # Debug port
  
  postgres:
    ports:
      - "5432:5432"  # Expose in dev
# Production: explicit file selection
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# Development: uses override automatically
docker compose up -d

Monitoring with Compose

# Add Prometheus + Grafana
  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    networks:
      - backend

  grafana:
    image: grafana/grafana
    ports:
      - "3001:3000"
    environment:
      GF_SECURITY_ADMIN_PASSWORD_FILE: /run/secrets/grafana_password
    secrets:
      - grafana_password
    networks:
      - backend

Summary

Production Docker Compose patterns:

  • Always use health checks for dependency ordering
  • Store secrets as files, not environment variables
  • Use internal networks to isolate backend services
  • Set resource limits to prevent container starvation
  • Use environment-specific override files
  • Automate backups for stateful services