Chapter 07

Docker Compose 多服务编排

用一个 YAML 文件定义整个应用的服务栈,一条命令启动全部

什么是 Docker Compose

实际应用往往由多个服务组成:前端、后端 API、数据库、缓存、消息队列……手动 docker run 每个服务既繁琐又难以维护。

Docker Compose 是专为单主机多容器应用设计的编排工具。通过一个 compose.yml 文件声明所有服务的配置,然后用一条命令管理整个应用:


docker compose up -d    # 后台启动所有服务
docker compose down      # 停止并删除所有容器
💡

文件名约定 — 推荐使用 compose.yml(较新规范),也兼容 docker-compose.yml(旧名称)。Docker Compose V2 已内置于 Docker CLI(docker compose,无连字符),旧版独立工具是 docker-compose(有连字符)。

compose.yml 文件结构


# compose.yml 基本结构
services:           # 服务定义(必需)
  web:
    image: nginx:1.25
  db:
    image: postgres:16

networks:           # 自定义网络(可选)
  app-net:
    driver: bridge

volumes:            # 命名卷(可选)
  db-data:

核心配置项详解

image vs build


services:
  # 使用现有镜像
  nginx:
    image: nginx:1.25-alpine

  # 从 Dockerfile 构建
  api:
    build:
      context: ./api        # 构建上下文目录
      dockerfile: Dockerfile # Dockerfile 路径(相对于 context)
      args:
        NODE_ENV: production

  # 简写(context 即当前目录)
  web:
    build: .

ports — 端口映射


services:
  web:
    image: nginx
    ports:
      - "8080:80"           # 宿主机8080 → 容器80
      - "443:443"
      - "127.0.0.1:3000:3000"  # 只绑定 localhost

volumes — 卷挂载


services:
  db:
    image: postgres:16
    volumes:
      - db-data:/var/lib/postgresql/data    # 命名卷
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro  # 绑定挂载(只读)

volumes:
  db-data:   # 声明命名卷(在顶层)

environment vs env_file — 环境变量


services:
  api:
    image: myapi:1.0

    # 方式一:直接在 compose.yml 中声明(适合非敏感配置)
    environment:
      - NODE_ENV=production
      - PORT=3000
      - LOG_LEVEL=info

    # 方式二:从文件加载(敏感配置放 .env 文件,加入 .gitignore)
    env_file:
      - .env           # 默认环境文件
      - .env.local     # 本地覆盖(不提交到 git)

depends_on — 服务依赖


services:
  api:
    build: .
    depends_on:
      db:
        condition: service_healthy  # 等待 db 健康检查通过
      redis:
        condition: service_started  # 只等待 redis 容器启动(默认)

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

depends_on 只控制启动顺序,不等待服务就绪 — 即使 condition: service_started,数据库容器启动后 PostgreSQL 进程可能还没准备好接受连接。使用 condition: service_healthy 配合 healthcheck 才能真正等待服务就绪。

healthcheck — 健康检查


services:
  web:
    image: myapp
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s      # 每30秒检查一次
      timeout: 10s       # 超时10秒
      retries: 3         # 连续3次失败才标记为不健康
      start_period: 40s  # 启动后40秒内失败不计入

restart — 重启策略


services:
  api:
    restart: unless-stopped  # 推荐:除非手动停止,否则自动重启

  # 可选值:
  # no           — 不自动重启(默认)
  # always       — 总是重启(包括 docker daemon 重启)
  # on-failure   — 仅在退出码非0时重启
  # unless-stopped — 手动停止前一直重启

Compose 常用命令


# 启动所有服务(-d 后台,--build 强制重新构建)
docker compose up -d --build

# 停止并删除容器(-v 同时删除命名卷)
docker compose down
docker compose down -v    # 危险!删除数据卷

# 只停止,不删除
docker compose stop
docker compose start

# 查看服务状态
docker compose ps

# 查看日志(-f 实时跟踪)
docker compose logs -f
docker compose logs -f api     # 只看某个服务

# 进入某个服务的容器
docker compose exec api bash

# 运行一次性命令(不影响现有容器)
docker compose run --rm api npm run migrate

# 只重新构建镜像(不启动)
docker compose build api

# 扩展/缩减服务实例数量
docker compose up --scale api=3

Profile 功能

Profile 允许在不同场景下选择性地启动某些服务:


services:
  # 核心服务(总是启动)
  api:
    build: .

  db:
    image: postgres:16

  # 只在 dev profile 启动
  adminer:
    image: adminer
    profiles: ["dev"]
    ports:
      - "8080:8080"

  # 只在 test profile 启动
  test-runner:
    build: .
    profiles: ["test"]
    command: npm test

# 启动核心服务 + dev profile
docker compose --profile dev up -d

# 只运行测试
docker compose --profile test run test-runner

完整实战:全栈 Web 应用

一个包含 React 前端、Node.js API、PostgreSQL 和 Redis 的完整 compose 配置:


# compose.yml

services:
  # === 前端(React + Nginx)===
  frontend:
    build:
      context: ./frontend
    ports:
      - "80:80"
    depends_on:
      - api
    restart: unless-stopped

  # === 后端 API(Node.js)===
  api:
    build:
      context: ./api
    environment:
      - NODE_ENV=production
      - PORT=3000
    env_file:
      - .env
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped
    networks:
      - frontend-net
      - backend-net

  # === PostgreSQL 数据库 ===
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: myapp
      POSTGRES_PASSWORD: ${DB_PASSWORD}   # 从 .env 读取
      POSTGRES_DB: myapp
    volumes:
      - pg-data:/var/lib/postgresql/data
      - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U myapp"]
      interval: 5s
      retries: 5
    networks:
      - backend-net
    restart: unless-stopped

  # === Redis 缓存 ===
  redis:
    image: redis:7-alpine
    command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
    volumes:
      - redis-data:/data
    networks:
      - backend-net
    restart: unless-stopped

networks:
  frontend-net:
  backend-net:

volumes:
  pg-data:
  redis-data:

开发模式:代码热重载

创建 compose.dev.yml 覆盖生产配置,用于本地开发:


# compose.dev.yml(覆盖 compose.yml 的开发配置)
services:
  api:
    build:
      target: dev           # 构建到 dev 阶段(含开发工具)
    volumes:
      - ./api:/app           # 挂载代码(热重载)
      - /app/node_modules    # 不覆盖容器内的 node_modules
    command: npm run dev    # 覆盖启动命令
    environment:
      - NODE_ENV=development

  frontend:
    build:
      target: dev
    volumes:
      - ./frontend:/app
      - /app/node_modules
    ports:
      - "3000:3000"        # 开发服务器端口
    command: npm start

# 使用多个 compose 文件合并(dev 覆盖 prod)
docker compose -f compose.yml -f compose.dev.yml up -d

本章小结 — Docker Compose 是单机多容器应用开发和部署的首选工具。一个 compose.yml 文件完整描述应用栈,团队成员只需 docker compose up -d 即可在本地跑起完整环境,极大降低了环境配置的协作成本。