Docker: Compose高级编排

最后更新:2026-08-26

开发、测试、生产——三套环境一个 Compose 文件搞定。

1. 你将学到


2. 一个技术负责人的真实故事

(1) 痛点:三套环境三套配置文件

Charlie 需要为开发/测试/生产三套环境维护不同的 Compose config:开发要热重载和调试工具,测试要完整的服务栈,生产要资源限制和多副本。三个独立的 YAML 文件有大量重复代码,改一处要改三处。

(2) Profiles + Override 的解法

Charlie 用 profiles 和 override 文件,实现了"一个基础配置 + 环境差异覆盖"的方案。

BASH
# Development: activate dev profile + dev overrides
docker compose --profile dev -f docker-compose.yml -f docker-compose.dev.yml up -d

# Production: prod overrides only
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

(3) 收益:3 个文件→1+2 个文件

从 3 个完全独立的文件变成 1 个基础文件 + 2 个差异覆盖文件。公共配置只需维护一份。


3. Profiles 多环境配置

(1) Profile 机制

100%
graph TB
    BASE["docker-compose.yml<br/>Base services"] --> DEV["--profile dev<br/>+ dev tools (hot-reload, adminer)"]
    BASE --> TEST["--profile test<br/>+ test runners (selenium)"]
    BASE --> PROD["--profile prod<br/>+ monitoring (prometheus, grafana)"]

▶ 示例:--profile dev 激活(难度⭐⭐)

YAML
# docker-compose.yml with profiles
services:
  api:
    build: .
    ports:
      - "5000:5000"
    environment:
      - FLASK_ENV=development

  db:
    image: postgres:15-alpine
    volumes:
      - pg-data:/var/lib/postgresql/data

  # Dev-only: database management UI
  adminer:
    image: adminer
    ports:
      - "8081:8080"
    profiles: ["dev"]
    depends_on: [db]

  # Dev-only: hot-reload with flask debug
  api-dev:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - ./src:/app
    ports:
      - "5000:5000"
      - "5678:5678"   # Debug port
    environment:
      - FLASK_DEBUG=1
    profiles: ["dev"]

  # Prod-only: Prometheus monitoring
  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"
    profiles: ["prod"]

volumes:
  pg-data:
BASH
# Development: base services + dev profile
docker compose --profile dev up -d

# Production: base services + prod profile
docker compose --profile prod up -d

# Only base services (no profile)
docker compose up -d

(1) Profiles vs Override 对比

维度 Profiles Override Files
文件数 1 个文件 2-3 个文件
激活方式 --profile xxx -f base.yml -f override.yml
适合场景 增减少量服务 大量配置差异
可组合性 多个 profile 可组合 按顺序覆盖

4. Override 文件合并

▶ 示例:override 文件覆盖配置(难度⭐⭐⭐)

YAML
# docker-compose.yml (base configuration)
services:
  api:
    build: .
    environment:
      FLASK_ENV: production
    restart: unless-stopped

  db:
    image: postgres:15-alpine
    volumes:
      - pg-data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  pg-data:
YAML
# docker-compose.dev.yml (dev overrides)
services:
  api:
    environment:
      FLASK_ENV: development
      FLASK_DEBUG: "1"
    volumes:
      - ./src:/app     # Hot-reload: mount source code
    ports:
      - "5678:5678"    # Debug port

  # Dev-only service
  adminer:
    image: adminer
    ports:
      - "8081:8080"
YAML
# docker-compose.prod.yml (prod overrides)
services:
  api:
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
      interval: 30s
      timeout: 5s
      retries: 3

  # Prod-only: Nginx reverse proxy
  nginx:
    image: nginx:1.25-alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.prod.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - api
BASH
# Development
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d

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

# Preview merged config (dry-run)
docker compose -f docker-compose.yml -f docker-compose.prod.yml config

5. 水平扩展(Scaling)

▶ 示例:--scale 扩展(难度⭐⭐)

BASH
# Scale the API service to 3 replicas
docker compose up -d --scale api=3

# Scale dynamically on a running stack
docker compose up -d --scale api=5

(1) Scaling 注意事项

问题 原因 解决方案
端口冲突 多副本映射同一主机端口 只暴露 1 个入口(Nginx),API 不映射主机端口
数据一致性 多副本写同一存储 无状态服务才适合扩展,共享数据库
负载均衡 请求如何分发到多副本 Nginx upstream 或 Docker 内置 DNS 轮询

▶ 示例:deploy.replicas 声明式扩展(难度⭐⭐)

YAML
services:
  api:
    build: .
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "0.5"
          memory: 256M
        reservations:
          cpus: "0.25"
          memory: 128M
    # Do NOT map ports for scaled services
    # ports: ["5000:5000"]  ← This breaks with replicas > 1

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    # Nginx upstream uses DNS round-robin to api:5000

6. 资源限制

(1) deploy.resources 配置

YAML
services:
  api:
    deploy:
      resources:
        limits:        # Hard limits (kill if exceeded)
          cpus: "1.0"
          memory: 512M
        reservations:  # Soft guarantees (minimum)
          cpus: "0.5"
          memory: 256M
字段 作用 效果
limits.cpus CPU 上限 超过被限流
limits.memory 内存上限 超过被 OOM Kill
reservations.cpus CPU 最低保证 调度保证
reservations.memory 内存最低保证 调度保证

7. 完整示例:三环境 Compose 项目

YAML
# ============================================
# docker-compose.yml - Base configuration
# ============================================
services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      DATABASE_URL: postgresql://postgres:${DB_PASSWORD:-secret}@db:5432/${DB_NAME:-myapp}
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped
    networks:
      - backend

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD:-secret}
      POSTGRES_DB: ${DB_NAME:-myapp}
    volumes:
      - pg-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    networks:
      - backend

  # Dev-only: Adminer DB UI
  adminer:
    image: adminer
    ports:
      - "8081:8080"
    profiles: ["dev"]
    networks:
      - backend

volumes:
  pg-data:

networks:
  backend:
YAML
# ============================================
# docker-compose.prod.yml - Production overrides
# ============================================
services:
  api:
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
      interval: 30s
      timeout: 5s
      retries: 3

  nginx:
    image: nginx:1.25-alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.prod.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      api:
        condition: service_healthy
    restart: unless-stopped
    networks:
      - backend
BASH
# Development
docker compose --profile dev up -d

# Production (3 replicas + Nginx + resource limits)
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# Verify scaling
docker compose -f docker-compose.yml -f docker-compose.prod.yml ps

❓ 常见问题

Q profiles 和 override 文件能混用吗?
A 能。docker compose --profile dev -f docker-compose.yml -f docker-compose.dev.yml up -d 同时使用 profiles 和 override。profiles 管理增减服务,override 管理配置差异,两者互补。
Q --scale 和 deploy.replicas 哪个生效?
A --scale 命令行参数优先于 deploy.replicas YAML 声明。如果两者都设置,--scale 的值覆盖 YAML 中的值。生产环境推荐用 YAML 声明(可版本控制),调试时用 --scale 快速调整。
Q 如何控制服务启动顺序?
A depends_on + condition。三种条件:① service_started(默认,只等启动);② service_healthy(等健康检查通过);③ service_completed_successfully(等初始化任务完成)。生产环境一律用 service_healthy
Q Compose 文件里的变量怎么在不同环境间切换?
A ${VAR:-default} + .env 文件。每个环境放不同的 .env 文件:.env.dev / .env.prod。启动时用 --env-file 指定:docker compose --env-file .env.prod up -d
Q 多副本时端口怎么处理?
A 被扩展的服务不能映射主机端口(多副本会冲突)。只让 Nginx/HAProxy 映射端口,API 服务在内部网络通信。Nginx 的 upstream 使用 Docker DNS 轮询自动分发到多副本。

📖 小节


📝 作业

  1. 基础题(难度⭐):为第 12 课的应用编写 dev/prod 两套 override 文件,dev 加热重载,prod 加资源限制。
  2. 进阶题(难度⭐⭐):用 --scale api=3 扩展 API 服务到 3 副本,用 docker compose ps 验证。
  3. 挑战题(难度⭐⭐⭐):配置 Nginx 反向代理到 3 副本 API,验证请求被分发到不同副本(查看不同容器的日志)。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏