Docker Compose 多环境编排实战:开发、测试与生产配置的优雅管理

Docker Compose 已经成为本地开发与单机容器编排的事实标准。然而,当项目从开发走向测试再到生产时,许多团队仍然在复制粘贴 docker-compose.yml 文件,然后手动修改端口、环境变量和卷挂载。这种做法不仅容易出错,还会导致“在我机器上能跑”的经典问题。本文将介绍一套基于 Docker Compose 的多环境配置管理方案,让你用一套代码优雅地覆盖开发、测试与生产三种场景。

为什么需要多环境编排?

在真实项目中,不同环境对容器的要求差异巨大:

  • 开发环境:需要源码热重载、调试端口暴露、数据库密码简单、可能包含 Adminer 等辅助工具。
  • 测试环境:需要接近生产的配置,但资源限制较松,日志级别更详细,可能使用内存数据库或临时卷。
  • 生产环境:要求安全加固、资源限制、持久化卷、日志驱动、健康检查、不暴露不必要的端口。

如果为每个环境维护独立的 Compose 文件,任何基础镜像或依赖变更都需要同步修改三处,维护成本极高。

核心策略:基础文件 + 覆盖文件 + 环境变量

Docker Compose 官方推荐的方式是使用多个 Compose 文件叠加。基本思路是:

  1. docker-compose.yml:定义所有环境共用的服务、网络和基础配置。
  2. docker-compose.override.yml:开发环境的默认覆盖(Compose 会自动加载)。
  3. docker-compose.test.yml:测试环境专用覆盖。
  4. docker-compose.prod.yml:生产环境专用覆盖。
  5. .env 文件:存放环境变量,结合 env_file 或变量替换实现动态配置。

启动时通过 -f 参数指定文件组合:

# 开发环境(自动加载 override)
docker compose up

# 测试环境
docker compose -f docker-compose.yml -f docker-compose.test.yml up

# 生产环境
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

实战:一个 Node.js + PostgreSQL 项目

1. 基础文件 docker-compose.yml

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    depends_on:
      db:
        condition: service_healthy
    environment:
      - NODE_ENV=${NODE_ENV:-development}
      - DB_HOST=db
      - DB_PORT=5432
      - DB_USER=${DB_USER:-app}
      - DB_PASSWORD=${DB_PASSWORD:-secret}
      - DB_NAME=${DB_NAME:-appdb}
    networks:
      - backend

  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_USER=${DB_USER:-app}
      - POSTGRES_PASSWORD=${DB_PASSWORD:-secret}
      - POSTGRES_DB=${DB_NAME:-appdb}
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-app}"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - backend

networks:
  backend:
    driver: bridge

基础文件只保留共性:服务定义、网络、健康检查、环境变量占位符。

2. 开发环境 docker-compose.override.yml

services:
  app:
    build:
      target: development
    command: npm run dev
    volumes:
      - .:/app
      - /app/node_modules
    ports:
      - "3000:3000"
      - "9229:9229"   # Node.js 调试端口
    environment:
      - LOG_LEVEL=debug

  db:
    ports:
      - "5432:5432"   # 方便本地用客户端连接

  adminer:
    image: adminer:latest
    ports:
      - "8080:8080"
    networks:
      - backend

开发覆盖文件关注:源码挂载、热重载命令、调试端口、辅助工具。

3. 测试环境 docker-compose.test.yml

services:
  app:
    build:
      target: test
    command: npm run test:ci
    environment:
      - NODE_ENV=test
      - LOG_LEVEL=verbose
    tmpfs:
      - /tmp

  db:
    tmpfs:
      - /var/lib/postgresql/data   # 测试数据不持久化,加速且干净

测试环境强调可重复性:使用临时文件系统、固定命令、详细日志。

4. 生产环境 docker-compose.prod.yml

services:
  app:
    build:
      target: production
    restart: unless-stopped
    ports:
      - "80:3000"
    environment:
      - NODE_ENV=production
      - LOG_LEVEL=warn
    deploy:
      resources:
        limits:
          cpus: "1.5"
          memory: 512M
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 5s
      retries: 3

  db:
    restart: unless-stopped
    volumes:
      - pgdata:/var/lib/postgresql/data
    deploy:
      resources:
        limits:
          memory: 1G

volumes:
  pgdata:

生产覆盖文件关注:安全、资源限制、持久化、日志轮转、健康检查。

环境变量的分层管理

除了 Compose 文件叠加,环境变量也应分层:

  • .env:默认值,提交到仓库,供所有环境使用。
  • .env.development.env.test.env.production:各环境特定值,通过 --env-file 指定。
docker compose --env-file .env.production -f docker-compose.yml -f docker-compose.prod.yml up -d

注意:Compose 的变量替换发生在解析阶段,而 env_file 是注入容器内部。两者用途不同,建议敏感信息(如生产数据库密码)通过 CI/CD 的密钥管理注入,不要硬编码在文件中。

用 Makefile 或脚本封装命令

为了避免团队成员记错文件组合,可以用 Makefile 封装:

COMPOSE = docker compose

dev:
	$(COMPOSE) up

test:
	$(COMPOSE) -f docker-compose.yml -f docker-compose.test.yml up --build --abort-on-container-exit

prod:
	$(COMPOSE) --env-file .env.production -f docker-compose.yml -f docker-compose.prod.yml up -d

down:
	$(COMPOSE) down -v

这样,make devmake testmake prod 就是统一入口,降低认知负担。

常见陷阱与最佳实践

  1. 不要在生产覆盖文件中使用 buildtarget 之外的自定义逻辑。生产镜像应该在 CI 中构建并推送到镜像仓库,Compose 只负责拉取运行。可以将 build 替换为 image: registry.example.com/app:${TAG}

  2. 注意 ports 的合并行为。Compose 叠加时,ports 是合并而非替换。如果基础文件暴露了端口,覆盖文件无法删除,只能通过 !reset!override(Compose 2.24+)处理。建议基础文件不写 ports

  3. 使用 profiles 控制辅助服务。Adminer、Mailhog 等工具可以标记 profiles: [dev],只在开发时启动。

  4. 保持基础文件最小化。只放所有环境都需要的配置,任何有差异的内容都放到覆盖文件中。

  5. 在 CI 中验证所有组合。至少运行 docker compose -f ... config 检查语法和合并结果,避免部署时才发现错误。

总结

Docker Compose 的多环境管理并不需要复杂的工具链。通过“基础文件 + 覆盖文件 + 环境变量 + 命令封装”四层策略,你可以用一套声明式配置覆盖开发、测试和生产。关键在于:基础文件保持纯净,覆盖文件只写差异,敏感信息外置,命令统一入口。这样既减少了重复,又让每个环境的意图一目了然。下次当你忍不住要复制 docker-compose.yml 时,不妨试试这套方案。

未经允许不得转载:任鹏个人博客 » Docker Compose 多环境编排实战:开发、测试与生产配置的优雅管理

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏