Docker Compose 多文件配置

概述

Docker Compose 支持使用多个配置文件来管理复杂的应用部署。通过多文件配置,可以实现环境分离、配置复用、模块化管理等功能。本文将详细介绍 Docker Compose 多文件配置的各种方法和最佳实践。

多文件配置方法

1. Override 文件

Docker Compose 会自动查找并合并以下文件:

  • docker-compose.yml(基础配置)
  • docker-compose.override.yml(覆盖配置)

2. 指定多个文件

# 使用 -f 参数指定多个文件
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up

# 文件顺序很重要,后面的文件会覆盖前面的配置
docker-compose -f base.yml -f override.yml -f local.yml up

3. 环境变量指定

# 设置 COMPOSE_FILE 环境变量
export COMPOSE_FILE=docker-compose.yml:docker-compose.override.yml:docker-compose.local.yml
docker-compose up

基础配置文件

docker-compose.yml

version: '3.8'

services:
  web:
    image: nginx:alpine
    ports:
      - "80"
    volumes:
      - ./html:/usr/share/nginx/html:ro
    depends_on:
      - app
    networks:
      - frontend
      - backend

  app:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://user:pass@db:5432/myapp
    volumes:
      - ./app:/usr/src/app
      - node_modules:/usr/src/app/node_modules
    depends_on:
      - db
      - redis
    networks:
      - backend

  db:
    image: postgres:13-alpine
    environment:
      - POSTGRES_DB=myapp
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - backend

  redis:
    image: redis:6-alpine
    volumes:
      - redis_data:/data
    networks:
      - backend

volumes:
  postgres_data:
  redis_data:
  node_modules:

networks:
  frontend:
  backend:

Override 文件配置

docker-compose.override.yml(开发环境)

version: '3.8'

services:
  web:
    ports:
      - "8080:80"  # 明确指定端口映射
    volumes:
      - ./nginx/dev.conf:/etc/nginx/conf.d/default.conf:ro

  app:
    build:
      target: development  # 使用开发阶段的 Dockerfile
    environment:
      - NODE_ENV=development
      - DEBUG=app:*
      - HOT_RELOAD=true
    volumes:
      - ./app:/usr/src/app  # 开发时挂载源码
      - /usr/src/app/node_modules  # 排除 node_modules
    ports:
      - "3000:3000"  # 暴露调试端口
    command: npm run dev

  db:
    ports:
      - "5432:5432"  # 开发时暴露数据库端口
    environment:
      - POSTGRES_DB=myapp_dev
    volumes:
      - ./db/init-dev.sql:/docker-entrypoint-initdb.d/init.sql:ro

  redis:
    ports:
      - "6379:6379"  # 开发时暴露 Redis 端口

  # 开发环境专用服务
  mailhog:
    image: mailhog/mailhog
    ports:
      - "1025:1025"  # SMTP
      - "8025:8025"  # Web UI
    networks:
      - backend

环境特定配置

docker-compose.prod.yml(生产环境)

version: '3.8'

services:
  web:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/prod.conf:/etc/nginx/conf.d/default.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
      - static_files:/usr/share/nginx/html/static:ro
    restart: unless-stopped
    deploy:
      replicas: 2
      resources:
        limits:
          cpus: '0.5'
          memory: 512M

  app:
    image: myapp:${APP_VERSION:-latest}
    environment:
      - NODE_ENV=production
      - DATABASE_URL=${DATABASE_URL}
      - REDIS_URL=${REDIS_URL}
      - SECRET_KEY=${SECRET_KEY}
    restart: unless-stopped
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '1.0'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 512M
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  db:
    image: postgres:13-alpine
    environment:
      - POSTGRES_DB=${POSTGRES_DB}
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./backup:/backup
    restart: unless-stopped
    deploy:
      resources:
        limits:
          cpus: '2.0'
          memory: 2G

  redis:
    image: redis:6-alpine
    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis_data:/data
    restart: unless-stopped
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M

volumes:
  postgres_data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /opt/myapp/data/postgres
  redis_data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /opt/myapp/data/redis
  static_files:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /opt/myapp/static

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

docker-compose.test.yml(测试环境)

version: '3.8'

services:
  app:
    build:
      target: test
    environment:
      - NODE_ENV=test
      - DATABASE_URL=postgresql://test:test@test_db:5432/test_db
    command: npm test
    depends_on:
      - test_db
      - test_redis

  test_db:
    image: postgres:13-alpine
    environment:
      - POSTGRES_DB=test_db
      - POSTGRES_USER=test
      - POSTGRES_PASSWORD=test
    tmpfs:
      - /var/lib/postgresql/data  # 使用内存存储提高测试速度

  test_redis:
    image: redis:6-alpine
    tmpfs:
      - /data

  # 测试工具
  selenium:
    image: selenium/standalone-chrome
    ports:
      - "4444:4444"
    shm_size: 2gb

模块化配置

docker-compose.base.yml(基础服务)

version: '3.8'

services:
  db:
    image: postgres:13-alpine
    environment:
      - POSTGRES_DB=${POSTGRES_DB:-myapp}
      - POSTGRES_USER=${POSTGRES_USER:-user}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - backend

  redis:
    image: redis:6-alpine
    volumes:
      - redis_data:/data
    networks:
      - backend

  rabbitmq:
    image: rabbitmq:3-management-alpine
    environment:
      - RABBITMQ_DEFAULT_USER=${RABBITMQ_USER:-admin}
      - RABBITMQ_DEFAULT_PASS=${RABBITMQ_PASS:-password}
    volumes:
      - rabbitmq_data:/var/lib/rabbitmq
    networks:
      - backend

volumes:
  postgres_data:
  redis_data:
  rabbitmq_data:

networks:
  backend:

docker-compose.web.yml(Web 服务)

version: '3.8'

services:
  nginx:
    image: nginx:alpine
    ports:
      - "${WEB_PORT:-80}:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - static_files:/usr/share/nginx/html/static:ro
    depends_on:
      - app
    networks:
      - frontend
      - backend

  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: ${BUILD_TARGET:-production}
    environment:
      - NODE_ENV=${NODE_ENV:-production}
      - DATABASE_URL=${DATABASE_URL}
      - REDIS_URL=${REDIS_URL}
    volumes:
      - static_files:/usr/src/app/static
    networks:
      - backend

volumes:
  static_files:

networks:
  frontend:
  backend:
    external: true

docker-compose.monitoring.yml(监控服务)

version: '3.8'

services:
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'
    networks:
      - monitoring
      - backend

  grafana:
    image: grafana/grafana
    ports:
      - "3001:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}
    volumes:
      - grafana_data:/var/lib/grafana
      - ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards:ro
      - ./monitoring/grafana/datasources:/etc/grafana/provisioning/datasources:ro
    networks:
      - monitoring

  node_exporter:
    image: prom/node-exporter
    ports:
      - "9100:9100"
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)'
    networks:
      - monitoring

volumes:
  prometheus_data:
  grafana_data:

networks:
  monitoring:
  backend:
    external: true

配置继承和扩展

使用 extends(已弃用,但仍可参考)

# common.yml
version: '3.8'

services:
  web_base:
    image: nginx:alpine
    volumes:
      - ./html:/usr/share/nginx/html:ro
    networks:
      - frontend
# docker-compose.yml
version: '3.8'

services:
  web:
    extends:
      file: common.yml
      service: web_base
    ports:
      - "80:80"

networks:
  frontend:

现代化的配置复用

# docker-compose.yml
version: '3.8'

x-common-variables: &common-variables
  NODE_ENV: ${NODE_ENV:-production}
  DATABASE_URL: ${DATABASE_URL}
  REDIS_URL: ${REDIS_URL}

x-app-base: &app-base
  build:
    context: .
    dockerfile: Dockerfile
  environment:
    <<: *common-variables
  networks:
    - backend
  restart: unless-stopped

services:
  web_app:
    <<: *app-base
    ports:
      - "3000:3000"
    environment:
      <<: *common-variables
      SERVICE_TYPE: web

  api_app:
    <<: *app-base
    ports:
      - "3001:3000"
    environment:
      <<: *common-variables
      SERVICE_TYPE: api

  worker_app:
    <<: *app-base
    environment:
      <<: *common-variables
      SERVICE_TYPE: worker
    command: npm run worker

networks:
  backend:

环境管理脚本

部署脚本

#!/bin/bash
# deploy.sh

set -e

ENVIRONMENT=${1:-development}
ACTION=${2:-up}

case $ENVIRONMENT in
  "development")
    COMPOSE_FILES="-f docker-compose.yml -f docker-compose.override.yml"
    ;;
  "staging")
    COMPOSE_FILES="-f docker-compose.yml -f docker-compose.staging.yml"
    ;;
  "production")
    COMPOSE_FILES="-f docker-compose.yml -f docker-compose.prod.yml"
    ;;
  "test")
    COMPOSE_FILES="-f docker-compose.yml -f docker-compose.test.yml"
    ;;
  *)
    echo "Unknown environment: $ENVIRONMENT"
    exit 1
    ;;
esac

echo "Deploying to $ENVIRONMENT environment..."

# 加载环境变量
if [ -f ".env.$ENVIRONMENT" ]; then
  export $(cat .env.$ENVIRONMENT | xargs)
fi

# 执行 Docker Compose 命令
docker-compose $COMPOSE_FILES $ACTION -d

echo "Deployment completed!"

环境切换脚本

#!/bin/bash
# switch-env.sh

ENVIRONMENT=$1

if [ -z "$ENVIRONMENT" ]; then
  echo "Usage: $0 <environment>"
  echo "Available environments: development, staging, production, test"
  exit 1
fi

# 停止当前环境
docker-compose down

# 切换环境变量文件
if [ -f ".env.$ENVIRONMENT" ]; then
  cp ".env.$ENVIRONMENT" ".env"
  echo "Switched to $ENVIRONMENT environment"
else
  echo "Environment file .env.$ENVIRONMENT not found"
  exit 1
fi

# 启动新环境
./deploy.sh $ENVIRONMENT

配置验证

配置检查脚本

#!/bin/bash
# validate-config.sh

ENVIRONMENT=${1:-development}

echo "Validating $ENVIRONMENT configuration..."

# 检查配置文件语法
case $ENVIRONMENT in
  "development")
    docker-compose -f docker-compose.yml -f docker-compose.override.yml config > /dev/null
    ;;
  "production")
    docker-compose -f docker-compose.yml -f docker-compose.prod.yml config > /dev/null
    ;;
  *)
    echo "Unknown environment: $ENVIRONMENT"
    exit 1
    ;;
esac

if [ $? -eq 0 ]; then
  echo "✓ Configuration is valid"
else
  echo "✗ Configuration has errors"
  exit 1
fi

# 检查必需的环境变量
required_vars=("DATABASE_URL" "REDIS_URL" "SECRET_KEY")
for var in "${required_vars[@]}"; do
  if [ -z "${!var}" ]; then
    echo "✗ Missing required environment variable: $var"
    exit 1
  else
    echo "✓ $var is set"
  fi
done

echo "All validations passed!"

配置差异检查

#!/bin/bash
# diff-configs.sh

ENV1=${1:-development}
ENV2=${2:-production}

echo "Comparing $ENV1 and $ENV2 configurations..."

# 生成配置文件
docker-compose -f docker-compose.yml -f docker-compose.$ENV1.yml config > /tmp/config-$ENV1.yml
docker-compose -f docker-compose.yml -f docker-compose.$ENV2.yml config > /tmp/config-$ENV2.yml

# 比较配置
diff -u /tmp/config-$ENV1.yml /tmp/config-$ENV2.yml

# 清理临时文件
rm /tmp/config-$ENV1.yml /tmp/config-$ENV2.yml

高级配置技巧

1. 条件配置

version: '3.8'

services:
  app:
    image: myapp
    profiles:
      - production
      - staging

  app_dev:
    build: .
    profiles:
      - development
    volumes:
      - ./src:/app/src

  debug_tools:
    image: debug-tools
    profiles:
      - debug
    depends_on:
      - app
# 启用特定 profile
docker-compose --profile development up
docker-compose --profile production --profile monitoring up

2. 动态配置生成

#!/bin/bash
# generate-config.sh

ENVIRONMENT=$1
TEMPLATE_FILE="docker-compose.template.yml"
OUTPUT_FILE="docker-compose.$ENVIRONMENT.yml"

# 使用 envsubst 替换模板中的变量
envsubst < $TEMPLATE_FILE > $OUTPUT_FILE

echo "Generated $OUTPUT_FILE for $ENVIRONMENT environment"
# docker-compose.template.yml
version: '3.8'

services:
  app:
    image: myapp:${APP_VERSION}
    replicas: ${APP_REPLICAS}
    environment:
      - DATABASE_URL=${DATABASE_URL}
      - REDIS_URL=${REDIS_URL}
    deploy:
      resources:
        limits:
          cpus: '${APP_CPU_LIMIT}'
          memory: ${APP_MEMORY_LIMIT}

3. 配置加密

version: '3.8'

services:
  app:
    image: myapp
    environment:
      - DATABASE_URL_FILE=/run/secrets/database_url
      - API_KEY_FILE=/run/secrets/api_key
    secrets:
      - database_url
      - api_key

secrets:
  database_url:
    file: ./secrets/database_url.txt
  api_key:
    external: true
    name: myapp_api_key

最佳实践

1. 文件组织结构

project/
├── docker-compose.yml              # 基础配置
├── docker-compose.override.yml     # 开发环境覆盖
├── docker-compose.prod.yml         # 生产环境配置
├── docker-compose.staging.yml      # 预发布环境配置
├── docker-compose.test.yml         # 测试环境配置
├── compose/
│   ├── base.yml                    # 基础服务
│   ├── web.yml                     # Web 服务
│   ├── monitoring.yml              # 监控服务
│   └── logging.yml                 # 日志服务
├── env/
│   ├── .env.development
│   ├── .env.staging
│   ├── .env.production
│   └── .env.test
├── scripts/
│   ├── deploy.sh
│   ├── switch-env.sh
│   └── validate-config.sh
└── README.md

2. 命名规范

# 使用一致的命名规范
services:
  # 环境前缀
  prod_web:
  prod_app:
  prod_db:

  # 或者使用标签
  web:
    labels:
      - "environment=production"
      - "service.type=web"
      - "service.version=1.0.0"

3. 配置验证

# 在每个配置文件中添加验证
version: '3.8'

# 必需的环境变量检查
x-required-env: &required-env
  - DATABASE_URL
  - REDIS_URL
  - SECRET_KEY

services:
  app:
    image: myapp
    environment: *required-env

4. 文档化

# Docker Compose 配置说明

## 文件说明

- `docker-compose.yml`: 基础配置,包含所有服务的通用设置
- `docker-compose.override.yml`: 开发环境配置,自动加载
- `docker-compose.prod.yml`: 生产环境配置
- `docker-compose.test.yml`: 测试环境配置

## 使用方法

### 开发环境
```bash
docker-compose up  # 自动加载 override 文件

生产环境

docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

测试环境

docker-compose -f docker-compose.yml -f docker-compose.test.yml up --abort-on-container-exit

环境变量

变量名 描述 默认值 必需
DATABASE_URL 数据库连接字符串 -
REDIS_URL Redis 连接字符串 -
SECRET_KEY 应用密钥 -

```

总结

Docker Compose 多文件配置是管理复杂应用部署的强大工具。通过合理使用多文件配置,可以实现:

  1. 环境分离: 不同环境使用不同的配置文件
  2. 配置复用: 基础配置可以被多个环境共享
  3. 模块化管理: 将不同功能的服务分离到不同文件
  4. 灵活部署: 根据需要组合不同的配置文件
  5. 安全管理: 敏感配置可以单独管理

关键要点:

  • 合理组织配置文件结构
  • 使用环境变量进行配置参数化
  • 建立配置验证和部署脚本
  • 遵循命名规范和最佳实践
  • 完善的文档和使用说明

通过这些方法,可以构建一个可维护、可扩展的 Docker Compose 配置体系。

powered by Gitbook© 2025 编外计划 | 最后修改: 2025-08-29 15:40:15

results matching ""

    No results matching ""