> ## Content Index
> Fetch the complete content index at: https://blog.vercanti.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Docker Compose 完全指南
- URL: https://blog.vercanti.com/docker-compose-wan-quan-zhi-nan/
- Published: 2026-08-28T14:34:20.000Z
- Updated: 2026-08-28T14:56:18.000Z
- Description: Docker Compose 是用于定义和运行多容器应用的工具。用一个 compose.yaml 文件描述所有服务、网络、存储卷，一条命令启动整个应用。 推荐使用 compose.yaml（新规范），也兼容 docker-compose.yml（旧版）。 使用 .env 文件存储敏感信息，并将 .env 加入 .gitignore： depends_on 只等待容器启动，不等待服务实际就绪（如 PostgreSQL 接受连接）。必须配合 healthcheck + condition: service_healthy 使用。 同一 compose 内的服
- Author: yellowdog
- Tags: DevOps, Docker

> 官方文档：<https://docs.docker.com/compose/>  
> 最后更新：2026-03-29

---

## 1\. 基础概念

### Docker Compose 是什么

Docker Compose 是用于定义和运行多容器应用的工具。用一个 `compose.yaml` 文件描述所有服务、网络、存储卷，一条命令启动整个应用。

| 功能      | 说明                             |
| ------- | ------------------------------ |
| 多服务编排   | Web + DB + Redis + Worker 一起启动 |
| 网络自动创建  | 同一 compose 内的服务可直接用服务名通信       |
| 依赖管理    | depends\_on 控制启动顺序             |
| 环境隔离    | 项目名作为前缀，多项目互不干扰                |
| 开发/生产分离 | 多个 compose 文件叠加（override）      |

### 文件命名

推荐使用 `compose.yaml`（新规范），也兼容 `docker-compose.yml`（旧版）。

---

## 2\. compose.yaml 结构

```yaml
# compose.yaml
name: myapp  # 项目名（默认为目录名）

services:
  web:
    # ... 服务配置

  db:
    # ... 服务配置

volumes:
  db_data:  # 命名卷

networks:
  backend:  # 自定义网络

```

---

## 3\. 服务配置参数

| 参数              | 说明                 |
| --------------- | ------------------ |
| image           | 使用的镜像              |
| build           | 从 Dockerfile 构建    |
| container\_name | 容器名（不设则自动生成）       |
| ports           | 端口映射 "主机:容器"       |
| environment     | 环境变量               |
| env\_file       | 从文件读取环境变量          |
| volumes         | 挂载卷                |
| networks        | 加入的网络              |
| depends\_on     | 依赖的服务（等待启动，不等待就绪）  |
| restart         | 重启策略               |
| command         | 覆盖容器默认命令           |
| healthcheck     | 健康检查               |
| deploy          | 资源限制（CPU/内存）       |
| profiles        | 分组（指定 profile 才启动） |

---

## 4\. 完整示例

### FastAPI + PostgreSQL + Redis + Celery Worker

```yaml
# compose.yaml
name: myapp

services:
  # FastAPI Web 服务
  web:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: myapp-web
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql+asyncpg://myuser:mypassword@db:5432/mydb
      - REDIS_URL=redis://redis:6379/0
      - SECRET_KEY=${SECRET_KEY}
    env_file:
      - .env
    volumes:
      - ./src:/app/src          # 开发时热重载
      - ./logs:/app/logs
    depends_on:
      db:
        condition: service_healthy   # 等待 db 健康检查通过
      redis:
        condition: service_started
    restart: unless-stopped
    networks:
      - backend

  # Celery Worker
  worker:
    build:
      context: .
    command: celery -A src.celery_app worker -l info --concurrency=4
    environment:
      - DATABASE_URL=postgresql+asyncpg://myuser:mypassword@db:5432/mydb
      - REDIS_URL=redis://redis:6379/0
    env_file:
      - .env
    depends_on:
      - db
      - redis
    restart: unless-stopped
    networks:
      - backend

  # Celery Beat（定时任务调度器）
  beat:
    build:
      context: .
    command: celery -A src.celery_app beat -l info
    env_file:
      - .env
    depends_on:
      - redis
    restart: unless-stopped
    networks:
      - backend

  # PostgreSQL
  db:
    image: postgres:16-alpine
    container_name: myapp-db
    environment:
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: mypassword
      POSTGRES_DB: mydb
    volumes:
      - db_data:/var/lib/postgresql/data
      - ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql  # 初始化脚本
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U myuser -d mydb"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    restart: unless-stopped
    networks:
      - backend

  # Redis
  redis:
    image: redis:7-alpine
    container_name: myapp-redis
    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-}
    volumes:
      - redis_data:/data
    restart: unless-stopped
    networks:
      - backend

  # Nginx 反向代理
  nginx:
    image: nginx:alpine
    container_name: myapp-nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/ssl:/etc/nginx/ssl
      - ./static:/var/www/static
    depends_on:
      - web
    restart: unless-stopped
    networks:
      - backend

volumes:
  db_data:
  redis_data:

networks:
  backend:
    driver: bridge

```

---

## 5\. 常用命令

```bash
# 启动所有服务（后台运行）
docker compose up -d

# 启动并强制重新构建镜像
docker compose up -d --build

# 停止并删除容器（保留数据卷）
docker compose down

# 停止并删除容器 + 数据卷（清空数据）
docker compose down -v

# 查看服务状态
docker compose ps

# 查看日志
docker compose logs
docker compose logs -f web          # 跟踪 web 服务日志
docker compose logs -f web worker   # 跟踪多个服务

# 在容器内执行命令
docker compose exec web bash
docker compose exec web python manage.py migrate
docker compose exec db psql -U myuser -d mydb

# 重启单个服务
docker compose restart web

# 仅重启修改过的服务
docker compose up -d --no-deps web

# 查看服务的环境变量
docker compose exec web env

# 伸缩服务（启动多个副本）
docker compose up -d --scale worker=3

# 拉取最新镜像
docker compose pull

```

---

## 6\. 环境分离（开发 / 生产）

```yaml
# compose.yaml（基础配置）
services:
  web:
    build: .
    environment:
      - APP_ENV=production

```

```yaml
# compose.override.yaml（开发覆盖，本地自动合并）
services:
  web:
    build:
      target: development      # 构建开发阶段（多阶段 Dockerfile）
    volumes:
      - .:/app                 # 挂载源码，热重载
    environment:
      - APP_ENV=development
      - DEBUG=true
    command: uvicorn src.main:app --reload --host 0.0.0.0

```

```yaml
# compose.prod.yaml（生产配置）
services:
  web:
    image: myapp:${VERSION:-latest}   # 使用发布的镜像而非构建
    deploy:
      replicas: 2
      resources:
        limits:
          cpus: "1"
          memory: 512M
    restart: always

```

```bash
# 开发：自动合并 compose.yaml + compose.override.yaml
docker compose up -d

# 生产：指定配置文件
docker compose -f compose.yaml -f compose.prod.yaml up -d

```

---

## 7\. 健康检查与依赖顺序

```yaml
services:
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
      interval: 10s      # 每 10 秒检查一次
      timeout: 5s        # 超时时间
      retries: 5         # 失败重试次数
      start_period: 30s  # 容器启动后等待多久开始检查

  web:
    depends_on:
      db:
        condition: service_healthy  # 等待 db 健康检查通过
      redis:
        condition: service_started  # 只等待启动，不等健康

```

---

## 8\. 常用代码段

### 本地开发数据库（只启动依赖服务）

```bash
# 只启动 db 和 redis，web 和 worker 在本地运行
docker compose up -d db redis

```

### 一次性任务容器（数据库迁移）

```yaml
services:
  migrate:
    build: .
    command: alembic upgrade head
    env_file: .env
    depends_on:
      db:
        condition: service_healthy
    profiles:
      - tools  # 只有 --profile tools 时才启动

```

```bash
docker compose --profile tools run --rm migrate

```

### 查看容器资源占用

```bash
docker compose stats

```

---

## 9\. 最佳实践

### 不要在 compose 文件中硬编码密码

使用 `.env` 文件存储敏感信息，并将 `.env` 加入 `.gitignore`：

```bash
# .env（不提交到 git）
POSTGRES_PASSWORD=secret123
SECRET_KEY=my-secret-key
REDIS_PASSWORD=redis-pass

# .env.example（提交到 git，作为模板）
POSTGRES_PASSWORD=
SECRET_KEY=
REDIS_PASSWORD=

```

### 数据卷用命名卷而非绑定挂载存储数据库数据

```yaml
volumes:
  - db_data:/var/lib/postgresql/data  # 命名卷（推荐）
  - ./data:/var/lib/postgresql/data   # 绑定挂载（权限问题多）

```

### 生产环境用 restart: unless-stopped 或 always

```yaml
restart: unless-stopped  # 除非手动停止，否则自动重启（推荐）
restart: always           # 总是重启（包括 docker stop 后 docker start 时）
restart: on-failure       # 仅在非零退出码时重启

```

---

## 10\. 踩坑与注意事项

### depends\_on 不等待服务就绪

`depends_on` 只等待容器启动，不等待服务实际就绪（如 PostgreSQL 接受连接）。必须配合 `healthcheck` \+ `condition: service_healthy` 使用。

### 服务间通信用服务名，不用 localhost

同一 compose 内的服务通过服务名互相通信（DNS 自动解析）：

```yaml
# web 服务连接 db 服务
DATABASE_URL=postgresql://user:pass@db:5432/mydb
#                                    ^^
#                           服务名，不是 localhost

```

### 修改 compose.yaml 不会自动重建容器

修改配置后需要 `docker compose up -d` 让 Compose 检测变化并重建：

```bash
# 重建并重启变化的服务
docker compose up -d --build

```

---

## 最佳实践

**用 `depends_on` \+ `condition: service_healthy` 等待依赖服务真正就绪**：`depends_on` 默认只等容器启动（进程跑起来），不等服务可用。配合 `healthcheck` \+ `condition: service_healthy` 可让应用真正等待数据库、消息队列初始化完成再启动。

**环境变量通过 `.env` 文件注入，不在 compose.yaml 中硬编码密码**：`.env` 文件加入 `.gitignore`，`compose.yaml` 中只引用变量名（`${DB_PASSWORD}`），避免敏感信息进入版本控制。

**为每个项目使用独立的 project name（`-p` 或 `COMPOSE_PROJECT_NAME`）**：同一台机器上运行多个项目时，默认用目录名作为 project name 可能冲突；显式设置 project name 确保容器名、网络名、卷名不重叠。

**开发环境用 bind mount 挂载源码，生产环境用 named volume 持久化数据**：开发时 `volumes: - ./src:/app/src` 实现代码热更新；生产时数据目录应用具名 volume 而非 bind mount，便于备份和迁移。

**用 `profiles` 分离开发工具服务（如 PgAdmin、Flower）**：将只在开发环境需要的服务加 `profiles: [dev]`，正常 `docker compose up` 不会启动它们，用 `--profile dev` 才启动，避免生产环境不必要的服务暴露。

---

## 常见陷阱

### 陷阱：`depends_on` 不能保证服务已就绪

**现象：** 应用容器启动后立即报数据库连接错误，尽管 `depends_on: [db]` 已配置。

**原因：** `depends_on` 默认只等待目标容器的进程启动，不等待服务就绪（数据库接受连接需要额外几秒初始化时间）。

**解决：** 为 `db` 服务配置 `healthcheck`，在 `depends_on` 中指定 `condition: service_healthy`。

```yaml
services:
  db:
    image: postgres:16
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 3s
      timeout: 5s
      retries: 5
  app:
    depends_on:
      db:
        condition: service_healthy

```

### 陷阱：修改 compose.yaml 后旧容器没有更新

**现象：** 修改了服务的环境变量或端口映射，`docker compose up -d` 后看起来正常，但旧配置仍然生效。

**原因：** `up -d` 只重启状态为 stopped 的容器；运行中的容器如果镜像和配置没变，Compose 判定为"无需更新"，保留旧实例。

**解决：** 用 `docker compose up -d --force-recreate` 强制重建容器；或先 `docker compose down` 再 `up`。

### 陷阱：`.env` 文件变量未生效

**现象：** `.env` 里定义了 `DB_PASSWORD=secret`，但 compose.yaml 读到的是空字符串或报错。

**原因：** Compose 只自动加载项目根目录（`compose.yaml` 同级）的 `.env` 文件；在子目录运行或文件名不对（如 `.env.local`）时不会自动加载。

**解决：** 确保 `.env` 与 `compose.yaml` 在同一目录；或用 `docker compose --env-file .env.local up` 显式指定文件。

---

## 参见

- [Docker初级指南](https://blog.vercanti.com/docker-chu-ji-zhi-nan/)
- [Docker中级指南](https://blog.vercanti.com/docker-zhong-ji-zhi-nan/)
- [Docker高级指南](https://blog.vercanti.com/docker-gao-ji-zhi-nan/)
- [Nginx完全指南](https://blog.vercanti.com/nginx-wan-quan-zhi-nan/)
- [GitHub Actions完全指南](https://blog.vercanti.com/github-actions-wan-quan-zhi-nan/)