强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

17 - Docker Compose 集成

17 - Docker Compose 集成:Compose Build、多服务构建与环境变量

17.1 Compose 中的 Build

Docker Compose 允许在 docker-compose.yml(或 compose.yml)中定义构建配置,实现一键构建和启动多服务应用。

基本语法

services:
  web:
    build: .
    ports:
      - "8080:80"

  api:
    build: ./api
    ports:
      - "3000:3000"

高级 Build 配置

services:
  web:
    build:
      context: .                    # 构建上下文
      dockerfile: Dockerfile        # Dockerfile 路径
      target: production            # 构建目标阶段
      args:                         # 构建参数
        NODE_ENV: production
        APP_VERSION: ${APP_VERSION:-1.0.0}
      cache_from:                   # 缓存来源
        - type=gha
      cache_to:                     # 缓存导出
        - type=gha,mode=max
      platforms:                    # 目标平台
        - linux/amd64
        - linux/arm64
      tags:                         # 镜像标签
        - myapp:latest
        - registry.example.com/myapp:latest
      labels:                       # 元数据标签
        com.example.version: "1.0"
      ssh:                          # SSH 转发
        - default
      secrets:                      # Secret 挂载
        - npmrc
      no_cache: false               # 是否禁用缓存
      pull: true                    # 是否拉取最新基础镜像

Build 与 Image 的关系

services:
  # 使用 build:从 Dockerfile 构建
  web:
    build: .
    image: myapp:latest    # 同时指定标签(构建后打标签)

  # 使用 image:直接使用已有镜像
  database:
    image: postgres:16

17.2 多服务构建

典型的多服务架构

services:
  # 前端
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
      target: production
    ports:
      - "3000:3000"
    depends_on:
      api:
        condition: service_healthy

  # 后端 API
  api:
    build:
      context: ./api
      dockerfile: Dockerfile
      target: production
    ports:
      - "8080:8080"
    environment:
      - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
      - REDIS_URL=redis://redis:6379
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

  # 数据库
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: myapp
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  # Redis
  redis:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  pgdata:

共享 Dockerfile,不同 Target

services:
  app:
    build:
      context: .
      target: production
    ports:
      - "8080:8080"

  worker:
    build:
      context: .
      target: worker
    command: ["python", "worker.py"]
    depends_on:
      - redis

  scheduler:
    build:
      context: .
      target: scheduler
    command: ["python", "scheduler.py"]

  test:
    build:
      context: .
      target: tester
    command: ["pytest", "tests/"]
    profiles:
      - testing
# 共享 Dockerfile
FROM python:3.12-slim AS base
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

FROM base AS production
CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:8080"]

FROM base AS worker
CMD ["python", "worker.py"]

FROM base AS scheduler
CMD ["python", "scheduler.py"]

FROM base AS tester
COPY requirements-dev.txt .
RUN pip install --no-cache-dir -r requirements-dev.txt
CMD ["pytest"]

17.3 环境变量管理

Compose 中的环境变量

services:
  app:
    build: .
    # 方式一:直接定义
    environment:
      NODE_ENV: production
      LOG_LEVEL: info
      DATABASE_URL: postgresql://user:pass@db:5432/mydb

    # 方式二:从 .env 文件加载
    env_file:
      - .env
      - .env.production

    # 方式三:使用变量引用
    environment:
      - API_KEY=${API_KEY}
      - DB_HOST=${DB_HOST:-localhost}    # 带默认值

.env 文件层级

项目目录/
├── .env                  # Compose 自动读取(设置 COMPOSE_PROJECT_NAME 等)
├── .env.local            # 本地覆盖(不应提交到 Git)
├── .env.development      # 开发环境
├── .env.production       # 生产环境
├── docker-compose.yml
└── ...
# .env
COMPOSE_PROJECT_NAME=myapp
APP_VERSION=1.0.0
# .env.production
DB_HOST=prod-db.example.com
DB_PASSWORD=supersecret
LOG_LEVEL=warning

Build Args 与环境变量的传递

services:
  app:
    build:
      context: .
      args:
        APP_VERSION: ${APP_VERSION:-1.0.0}
        NODE_ENV: ${NODE_ENV:-production}
        API_URL: ${API_URL:-http://localhost:3000}
    environment:
      # 运行时环境变量
      NODE_ENV: ${NODE_ENV:-production}
      LOG_LEVEL: ${LOG_LEVEL:-info}
# Dockerfile
FROM node:20-alpine

# 接收构建参数
ARG APP_VERSION
ARG NODE_ENV
ARG API_URL

# 转为运行时环境变量
ENV APP_VERSION=${APP_VERSION}
ENV NODE_ENV=${NODE_ENV}

WORKDIR /app
COPY . .

# 构建时使用 API_URL
RUN VITE_API_URL=${API_URL} npm run build

CMD ["node", "server.js"]

17.4 Secrets 管理

Compose Secrets

services:
  app:
    build: .
    secrets:
      - db_password
      - api_key
    environment:
      DB_PASSWORD_FILE: /run/secrets/db_password
      API_KEY_FILE: /run/secrets/api_key

secrets:
  db_password:
    file: ./secrets/db_password.txt
  api_key:
    file: ./secrets/api_key.txt
# Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY . .

# 应用代码读取 secret 文件
# db_password = open('/run/secrets/db_password').read().strip()

CMD ["python", "app.py"]

使用外部 Secret Provider

services:
  app:
    build: .
    secrets:
      - db_password

secrets:
  db_password:
    environment: "DB_PASSWORD"  # 从环境变量获取

17.5 网络配置

services:
  frontend:
    build: ./frontend
    networks:
      - frontend-net
    ports:
      - "3000:3000"

  api:
    build: ./api
    networks:
      - frontend-net
      - backend-net
    # 不暴露端口到主机

  db:
    image: postgres:16
    networks:
      - backend-net
    # 不暴露端口到主机

networks:
  frontend-net:
    driver: bridge
  backend-net:
    driver: bridge
    internal: true    # 不允许外部访问

17.6 Profiles

Profiles 允许按需启动服务:

services:
  # 默认服务(无 profile 限制)
  app:
    build: .
    ports:
      - "8080:8080"

  db:
    image: postgres:16

  redis:
    image: redis:7-alpine

  # 仅开发环境
  adminer:
    image: adminer
    ports:
      - "8081:8080"
    profiles:
      - dev

  # 仅测试环境
  test-runner:
    build:
      context: .
      target: tester
    profiles:
      - test

  # 监控服务
  prometheus:
    image: prom/prometheus
    profiles:
      - monitoring
# 启动默认服务
docker compose up -d

# 启动开发环境(包含 adminer)
docker compose --profile dev up -d

# 启动测试
docker compose --profile test run --rm test-runner

# 启动所有服务
docker compose --profile dev --profile monitoring up -d

17.7 开发与生产分离

docker-compose.override.yml

# docker-compose.yml(基础配置)
services:
  app:
    build: .
    environment:
      NODE_ENV: production
    ports:
      - "8080:8080"

  db:
    image: postgres:16
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
# docker-compose.override.yml(开发覆盖,自动加载)
services:
  app:
    build:
      target: development
    environment:
      NODE_ENV: development
      DEBUG: "true"
    volumes:
      - ./src:/app/src    # 源码挂载(热重载)
    ports:
      - "8080:8080"
      - "9229:9229"       # Node.js 调试端口

  db:
    ports:
      - "5432:5432"       # 开发时暴露数据库端口
# docker-compose.prod.yml(生产配置)
services:
  app:
    build:
      target: production
    restart: always
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: "1.0"
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
# 开发(自动使用 override)
docker compose up

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

17.8 依赖管理

services:
  app:
    build: .
    depends_on:
      db:
        condition: service_healthy      # 等待健康检查通过
      redis:
        condition: service_started      # 仅等待启动
      migration:
        condition: service_completed_successfully  # 等待完成

  migration:
    build: .
    command: ["python", "manage.py", "migrate"]
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 3s
      retries: 10

  redis:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

依赖条件表

条件说明
service_started服务已启动(默认)
service_healthy健康检查通过
service_completed_successfully服务成功退出(exit code 0)

17.9 常见错误与排查

错误原因解决方案
build context not foundcontext 路径错误检查路径(相对于 compose 文件)
env variable not set.env 文件缺失或变量未定义添加默认值 ${VAR:-default}
服务启动顺序错误未配置 healthcheck 和 depends_on使用 condition: service_healthy
端口冲突主机端口被占用更换端口或使用随机端口
Volume 权限问题主机目录权限不匹配entrypoint 脚本修复权限

17.10 扩展阅读


上一章16 - 测试与验证 下一章18 - 生产最佳实践 — CI/CD 集成、安全基线与维护策略。