Docker Compose
Docker Compose 使用 YAML 文件定义和管理多容器应用,实现一键启动、停止和编排。
概述
# docker-compose.yaml
services:
web:
build: .
ports:
- "8080:80"
depends_on:
- db
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:# 启动所有服务
docker compose up -d
# 查看日志
docker compose logs -f
# 停止并删除容器和网络(不删除卷)
docker compose down
# 停止并删除包括卷
docker compose down -v💡 Docker Compose V2(
docker compose,无连字符)已取代 V1(docker-compose)。V2 是 Docker CLI 的插件,用 Go 重写,性能更好。
Compose 文件结构
完整文件布局
version: "3.8" # ⚠️ 已弃用:Docker Compose V2 不再需要此字段
name: my_project # 项目名称(V2.0+)
services: # 服务定义(容器)
app:
...
networks: # 自定义网络
frontend:
...
volumes: # 自定义卷
db_data:
...
configs: # 配置(Swarm 模式)
app_config:
...
secrets: # 密钥(Swarm 模式)
db_password:
...🚨 注意:
version字段在 Compose V2 中已弃用,写不写都一样。新项目建议省略。
Services 详解
build — 从 Dockerfile 构建
services:
app:
build:
context: ./app # 构建上下文目录
dockerfile: Dockerfile.prod # Dockerfile 文件名
args: # 构建参数(ARG)
APP_VERSION: "1.0"
NODE_ENV: production
cache_from: # 外部缓存源
- my_app:latest
labels:
- "com.example.build=true"
image: my_app:1.0 # 构建后给镜像打标签image — 使用已有镜像
services:
redis:
image: redis:7-alpine
custom:
image: my-registry.example.com/my_app:latest💡 同时使用
build和image:build指定如何构建,image指定构建后的镜像名称/标签。
ports — 端口映射
services:
app:
ports:
- "8080:80" # 短语法:宿主机:容器
- "192.168.1.100:8081:81" # 绑定到特定 IP
- target: 443 # 长语法
published: 8443
protocol: tcp
mode: hostvolumes — 挂载卷
services:
db:
volumes:
- db_data:/var/lib/mysql # 命名卷(短语法)
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro # 绑定挂载 + 只读
- type: bind # 长语法
source: ./data
target: /app/data
consistency: cached
- type: tmpfs # 内存临时卷
target: /app/cache
tmpfs:
size: 128Menvironment / env_file — 环境变量
services:
app:
environment:
DB_HOST: postgres
DB_PORT: "5432"
REDIS_URL: redis://redis:6379
# 或者从文件加载
env_file:
- .env
- .env.production
# 单独设置某个变量(覆盖 env_file 中的值)
environment:
- NODE_ENV=production💡 优先级:命令行
--env> Compose 文件environment>env_file> 镜像默认值。
depends_on — 启动顺序
services:
web:
depends_on:
- db
- redis
db:
image: postgres:16
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
web_with_condition:
depends_on:
db:
condition: service_healthy # 等 db 健康后才启动
redis:
condition: service_started # redis 启动即开始(默认)| 条件 | 说明 |
|---|---|
service_started |
依赖服务启动即可(默认) |
service_healthy |
依赖服务健康检查通过 |
service_completed_successfully |
依赖服务执行完毕且退出码为 0(一次性任务) |
🚨 陷阱:
depends_on只控制启动顺序,不保证依赖服务内部的应用程序已就绪。必须配合healthcheck+condition: service_healthy才能真正等待就绪。
健康检查
services:
api:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s # 启动缓冲期restart — 重启策略
services:
app:
restart: unless-stopped| 值 | 说明 |
|---|---|
no |
不自动重启(默认) |
always |
无论退出码,总是重启 |
on-failure |
仅非正常退出时重启 |
unless-stopped |
除非手动 docker compose stop,否则始终重启 |
资源限制
services:
app:
deploy:
resources:
limits:
cpus: "2"
memory: 512M
reservations:
cpus: "0.5"
memory: 256M⚠️ 从 Compose V2 开始,
deploy.resources在非 Swarm 模式下也生效。
extra_hosts — 自定义 hosts
services:
app:
extra_hosts:
- "internal.service:192.168.1.100"
- "auth.local:10.0.0.1"profiles — 条件启动
services:
app:
profiles: ["production"] # 默认不启动
debug:
profiles: ["debug"] # 默认不启动
db:
# 没有 profiles,始终启动# 默认只启动 db
docker compose up -d
# 启动 production profile
docker compose --profile production up -d
# 启动多个 profile
docker compose --profile production --profile debug up -d网络配置
services:
web:
networks:
- frontend
- backend
api:
networks:
- backend
db:
networks:
- backend
networks:
frontend: # 使用默认 driver(bridge)
backend:
driver: bridge
driver_opts:
com.docker.network.bridge.name: br_backend
ipam:
config:
- subnet: 172.28.0.0/16Docker Compose 会自动为每个项目创建一个网络,项目中的服务默认能通过服务名互相访问。
extends — 继承配置
# docker-compose.base.yaml
services:
app_base:
build: .
environment:
LOG_LEVEL: info
restart: unless-stopped
# docker-compose.yaml
services:
web:
extends:
file: docker-compose.base.yaml
service: app_base
ports:
- "8080:80"
environment:
SERVICE_NAME: web # 会覆盖或合并 base 中的值
worker:
extends:
file: docker-compose.base.yaml
service: app_base
environment:
SERVICE_NAME: workerconfigs 和 secrets(Swarm)
services:
app:
secrets:
- db_password
- source: api_key
target: /run/secrets/api_key.txt
mode: 0400
configs:
- app_config
secrets:
db_password:
file: ./secrets/db_password.txt
configs:
app_config:
file: ./config/production.yamlCLI 常用命令
# 构建/重建镜像
docker compose build
docker compose build --no-cache web
# 启动
docker compose up -d # 后台启动所有服务
docker compose up -d web api # 只启动指定服务
docker compose up --scale web=3 # 启动 3 个 web 实例(需去除端口冲突)
# 查看
docker compose ps # 查看服务状态
docker compose logs -f # 实时日志
docker compose logs -f web # 指定服务
docker compose top # 查看进程
docker compose images # 列出镜像
# 停止/删除
docker compose stop # 停止服务
docker compose down # 停止并删除容器和网络
docker compose down -v # 也包括卷
docker compose down --rmi all # 也包括镜像
# 在服务中执行命令
docker compose exec web sh
docker compose exec db psql -U postgres
# 其他
docker compose restart # 重启服务
docker compose pull # 拉取最新镜像
docker compose config # 验证并查看合并后的配置
docker compose convert # 将 Compose 文件转换为平台 API 格式💡 最佳实践:使用
docker compose config验证 Compose 文件是否有语法错误,查看环境变量替换后的最终配置。
环境变量替换
services:
app:
image: ${REGISTRY:-docker.io}/my_app:${TAG:-latest}
ports:
- "${PORT:-8080}:3000"# 默认环境变量来自:
# 1. Shell 环境变量
# 2. .env 文件(与 docker-compose.yaml 同目录)
# 3. --env-file 指定的文件
# .env 文件示例
REGISTRY=my-registry.example.com
TAG=1.0.5
PORT=80典型模板
前后端分离项目
services:
nginx:
image: nginx:1.25-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
- api
restart: unless-stopped
networks:
- frontend
api:
build: ./api
environment:
DB_HOST: db
REDIS_HOST: redis
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
networks:
- frontend
- backend
worker:
build: ./worker
environment:
DB_HOST: db
REDIS_HOST: redis
restart: unless-stopped
networks:
- backend
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: myapp
volumes:
- pgdata:/var/lib/postgresql/data
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 10s
retries: 5
restart: unless-stopped
networks:
- backend
redis:
image: redis:7-alpine
volumes:
- redisdata:/data
restart: unless-stopped
networks:
- backend
networks:
frontend:
backend:
internal: true # 内部网络,不与外部通信
volumes:
pgdata:
redisdata:开发环境配置
# docker-compose.dev.yaml
services:
api:
build:
context: ./api
target: development
volumes:
- ./api/src:/app/src # 代码热加载
environment:
- NODE_ENV=development
ports:
- "9229:9229" # 调试端口
command: npm run dev常见陷阱
| 陷阱 | 说明 | 正确做法 |
|---|---|---|
depends_on 不等待就绪 |
只等容器启动,不等应用就绪 | 配合 healthcheck + condition: service_healthy |
忘记 .env 中的变量 |
变量未设置时 Compose 报错 | 使用 ${VAR:-default} 提供默认值 |
| 卷数据残留 | docker compose down 不删除卷 |
需要时使用 -v 参数 |
| 端口冲突 | 多个副本 scale 时端口冲突 | 只 scale 不映射端口或使用动态端口 |
| 忽视网络隔离 | 所有服务在同一个网络 | 使用 frontend/backend 网络分层 |
| Compose V1/V2 混用 | docker-compose 和 docker compose 行为略有不同 |
统一使用 V2 |