Docker Compose 完全指南

Docker Compose 是用于定义和运行多容器应用的工具。用一个 compose.yaml 文件描述所有服务、网络、存储卷,一条命令启动整个应用。 推荐使用 compose.yaml(新规范),也兼容 docker-compose.yml(旧版)。 使用 .env 文件存储敏感信息,并将 .env 加入 .gitignore: depends_on 只等待容器启动,不等待服务实际就绪(如 PostgreSQL 接受连接)。必须配合 healthcheck + condition: service_healthy 使用。 同一 compose 内的服

分享

官方文档: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 结构

# 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

# 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. 常用命令

# 启动所有服务(后台运行)
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. 环境分离(开发 / 生产)

# compose.yaml(基础配置)
services:
  web:
    build: .
    environment:
      - APP_ENV=production
# 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
# compose.prod.yaml(生产配置)
services:
  web:
    image: myapp:${VERSION:-latest}   # 使用发布的镜像而非构建
    deploy:
      replicas: 2
      resources:
        limits:
          cpus: "1"
          memory: 512M
    restart: always
# 开发:自动合并 compose.yaml + compose.override.yaml
docker compose up -d

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

7. 健康检查与依赖顺序

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. 常用代码段

本地开发数据库(只启动依赖服务)

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

一次性任务容器(数据库迁移)

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

查看容器资源占用

docker compose stats

9. 最佳实践

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

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

# .env(不提交到 git)
POSTGRES_PASSWORD=secret123
SECRET_KEY=my-secret-key
REDIS_PASSWORD=redis-pass

# .env.example(提交到 git,作为模板)
POSTGRES_PASSWORD=
SECRET_KEY=
REDIS_PASSWORD=

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

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

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

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 自动解析):

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

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

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

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

最佳实践

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

环境变量通过 .env 文件注入,不在 compose.yaml 中硬编码密码.env 文件加入 .gitignorecompose.yaml 中只引用变量名(${DB_PASSWORD}),避免敏感信息进入版本控制。

为每个项目使用独立的 project name(-pCOMPOSE_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

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 downup

陷阱:.env 文件变量未生效

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

原因: Compose 只自动加载项目根目录(compose.yaml 同级)的 .env 文件;在子目录运行或文件名不对(如 .env.local)时不会自动加载。

解决: 确保 .envcompose.yaml 在同一目录;或用 docker compose --env-file .env.local up 显式指定文件。


参见

阅读更多

Web 安全基础

1. HTML 转义(服务端渲染必须): 2. CSP(Content Security Policy): 3. HttpOnly Cookie:防止 JS 读取会话 Cookie: 4. 前端框架防护: 攻击者在第三方网站构造一个表单,诱导已登录用户提交,浏览器会自动携带目标站的 Cookie。 触发条件: 1. 用户已登录目标网站(Cookie 有效) 2. 目标 API 仅凭 Cookie 识别用户身份 3. 请求来源未验证 1. CSRF Token(推荐): 2. SameSite Cookie: 3. 验证 Origin/Referer 头:

By yellowdog

HTTP 协议深度指南

HTTP(HyperText Transfer Protocol)是 Web 的基础传输协议,基于 TCP/IP,采用请求/响应模型。 相关文档:Web安全基础(/web-an-quan-ji-chu/) FastAPI完全指南(/fastapi-wan-quan-zhi-nan/) Nginx完全指南(/nginx-wan-quan-zhi-nan/) 幂等性:多次执行相同请求,服务器状态结果相同。PUT /users/1 多次执行结果一致;POST /users 每次创建新资源,非幂等。 浏览器直接从本地缓存读取,不向服务器发送请求。 缓存命中时,状

By yellowdog

系统设计基础

SLA 对照表: 选择建议:无状态服务(Web 层、API 层)优先水平扩展;数据库初期垂直扩展,达到瓶颈后考虑分库分表或读写分离。 缓存穿透(查询不存在的 key,每次都打到 DB): 缓存击穿(热点 key 过期,瞬间大量请求打到 DB): 缓存雪崩(大量 key 同时过期,或缓存服务宕机): 令牌桶 Python 实现: Redis 实现分布式限流(滑动窗口): URL 命名规则: Cursor 分页响应格式: 雪花算法结构(64 bit): 定义:分布式系统不能同时满足以下三个特性: 在分布式环境中 P 是必须保证的,所以实际是 CP vs AP

By yellowdog

算法思路与模板

二分查找要求序列有序,每次将搜索范围缩减一半,时间复杂度 O(log n)。 两个指针从两端向中间收缩,常用于有序数组。 滑动窗口维护一个满足条件的区间 left, right,right 不断向右扩张,条件不满足时收缩 left。 滑动窗口通用框架: 1. 确定"子问题":原问题可以分解为哪些规模更小的同类问题 2. 定义 dpi 或 dpij 的含义,要足够清晰 3. 推导状态转移方程 4. 确定初始状态(边界条件) 5. 确定计算顺序(确保依赖的子问题先计算) 每件物品最多选一次。dpj = 容量为 j 时的最大价值,逆序遍历容量防止重复选取。 每

By yellowdog