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 文件加入 .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。
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 显式指定文件。