docker run 一次只能起一个容器,而真实的应用几乎都是「多容器协作」:前端、后端、数据库、缓存、消息队列……靠一条条 docker run 命令启动,命令会越来越长,忘记参数、端口冲突、启动顺序错乱是家常便饭。
Docker Compose 就是来解决这个问题的:用一个 YAML 文件声明整组服务,一条命令启动、停止、重建整个应用栈。本文从零到实战,讲清 Compose 的核心概念、完整案例和生产级最佳实践。
安装与版本:认准 Compose V2
Compose V1(docker-compose,带横杠的命令)已于 2023 年 7 月停止更新,请一律使用 V2:
# V2 正确用法:docker compose(空格分隔)
docker compose version
# 未安装时,Docker Desktop / Docker Engine 插件方式
# Linux 上安装插件:
# sudo apt install docker-compose-pluginV2 用 Go 语言重写,性能更好、原生支持 BuildKit,容器命名自动带项目前缀(如 myapp-web-1)。V1 时代的 version 字段已弃用,新写的 compose 文件直接省略即可:
# 正确:无需 version 字段
services:
web:
image: nginx:alpine配置文件默认叫 compose.yaml 或 compose.yml(兼容旧名 docker-compose.yml)。
核心配置逐项拆解
一个 compose 文件的骨架只有三块:services(必需)、volumes、networks。
services:
web:
image: nginx:alpine # 直接用镜像
app:
build: ./app # 或从 Dockerfile 构建
volumes:
db_data: # 命名卷
networks:
app_net: # 自定义网络image 与 build
image: 拉取现成镜像build: 指定构建上下文(context),可配合dockerfile和target做多阶段构建- 两者可同时写:先构建,再用
image给构建结果命名
ports 与 expose
ports:
- "8080:80" # 宿主机8080 → 容器80
- "127.0.0.1:3306:3306" # 仅本机可访问(数据库标配)
- "8000-8010:8000-8010" # 端口范围
expose:
- "3306" # 仅容器间可见, 不映射到宿主机生产原则:数据库、Redis 等内部服务的端口只绑定 127.0.0.1,或干脆不映射——容器间通过服务名直连,根本不需要宿主端口。
volumes:三种挂载方式
volumes:
- db_data:/var/lib/mysql # 命名卷: Docker 管理, 数据持久化, 数据库首选
- ./src:/app # 绑定挂载: 开发热更新神器
- type: tmpfs
target: /tmp # 内存盘: 临时文件environment 与 env_file
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:?请设置数据库密码} # 必填校验
- DEBUG=${DEBUG:-false} # 默认值
env_file:
- .env敏感信息永远不要硬编码进 compose 文件并提交 Git——用 .env 文件(加入 .gitignore)或 secrets 管理。
depends_on 与 healthcheck:正确的启动顺序
depends_on 只保证启动顺序,不保证服务就绪——MySQL 容器起来了不代表能接受连接。正确姿势是配合健康检查:
services:
db:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 10
start_period: 30s # 启动宽限期, 初始化慢的镜像要给够
app:
build: ./app
depends_on:
db:
condition: service_healthy # 真正等 db 就绪restart 与资源限制
services:
app:
restart: unless-stopped # 生产推荐: 崩溃自动拉起, 手动停止不拉起
deploy:
resources:
limits:
cpus: "1.0" # 单容器最多 1 核
memory: 512M # 最多 512MBlogging:防止日志撑爆磁盘
services:
app:
logging:
driver: json-file
options:
max-size: "10m" # 单个日志文件最大 10MB
max-file: "3" # 最多保留 3 个实战案例:Web + MySQL + Redis 完整应用栈
以最常见的「后端 API + MySQL + Redis」为例,一份文件管起整个应用:
services:
api:
build: ./api
ports:
- "8080:8080"
environment:
DB_HOST: mysql # 服务名即 DNS, 自动解析
DB_NAME: myapp
DB_USER: myapp
DB_PASSWORD: ${DB_PASSWORD:?必须设置 DB_PASSWORD}
REDIS_HOST: redis
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
restart: unless-stopped
deploy:
resources:
limits:
memory: 512M
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
mysql:
image: mysql:8.0
environment:
MYSQL_DATABASE: myapp
MYSQL_USER: myapp
MYSQL_PASSWORD: ${DB_PASSWORD:?必须设置 DB_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?必须设置 MYSQL_ROOT_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql # 命名卷持久化
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 10
start_period: 30s
restart: unless-stopped
# 不映射端口到宿主机, 仅容器网络内可访问
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
restart: unless-stopped
volumes:
mysql_data:
redis_data:启动与日常操作:
# 首次启动: 构建 + 后台运行
docker compose up -d --build
# 查看状态和日志
docker compose ps
docker compose logs -f api
# 进容器调试
docker compose exec api bash
# 验证配置是否正确(不启动)
docker compose config
# 停止并清理(容器+网络; -v 会连数据卷一起删, 慎用)
docker compose down
# 更新代码后重建
docker compose up -d --build启动后,api 容器里直接 mysql:3306、redis:6379 就能连上数据库和缓存——这就是 Compose 自动网络的功劳(同一项目内服务名即 DNS,详见之前的 Docker 网络模式详解)。
常用命令速查
| 命令 | 用途 |
|---|---|
docker compose up -d | 后台启动全部服务 |
docker compose up -d --build | 重新构建并启动 |
docker compose down | 停止并删除容器/网络 |
docker compose down -v | 同上并删除数据卷(数据会丢,慎用) |
docker compose ps | 查看服务状态 |
docker compose logs -f [服务名] | 实时查看日志 |
docker compose exec [服务名] bash | 进入容器执行命令 |
docker compose run --rm api pytest | 运行一次性任务 |
docker compose config | 校验并查看合并后的配置 |
docker compose pull | 拉取所有服务的最新镜像 |
docker compose up --scale web=3 | 扩容(指定了 container_name 或固定端口时不可用) |
docker compose watch | 开发模式热重载 |
高级技巧
Profiles:一份文件管多套环境
把可选服务(压测、监控)打上 profile 标签,默认不启动,需要时显式拉起:
services:
app:
image: myapp:latest
grafana:
image: grafana/grafana
profiles: ["monitoring"] # 默认不启动docker compose --profile monitoring up -d # app + grafana
docker compose up -d # 只有 app多文件合并:开发/生产配置分离
# compose.yaml 基础配置
# compose.prod.yaml 生产覆盖(资源限制/日志轮转等)docker compose -f compose.yaml -f compose.prod.yaml up -d开发环境绑定挂载源码(热更新),生产环境用镜像内置代码——兼顾开发效率与运行安全。
Watch 模式:开发热重载(V2.22+)
services:
app:
image: node:20
develop:
watch:
- path: ./src
action: sync # 同步文件不重启(配合框架热加载)
- path: ./package.json
action: rebuild # 依赖变化才重建镜像docker compose watchSecrets:敏感信息更安全的存放方式
官方镜像支持 *_FILE 后缀环境变量从文件读密码(如 MYSQL_ROOT_PASSWORD_FILE),配合 compose 的 secrets 挂载到 /run/secrets/:
services:
mysql:
image: mysql:8.0
secrets:
- mysql_root_password
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/mysql_root_password
secrets:
mysql_root_password:
file: ./secrets/mysql_root_password.txt # 该文件不入 Git最佳实践清单
- 启动顺序用 healthcheck +
condition: service_healthy,别裸用depends_on;start_period要给够(MySQL 建议 30s+) - 敏感信息进
.env或 secrets,.env加.gitignore,提供.env.example模板,用${VAR:?error}快速失败 - 生产环境
restart: unless-stopped(区别于always:手动 stop 后不会自动拉起) - 数据库不映射宿主端口(或只绑 127.0.0.1),容器间走服务名
- 配日志轮转(max-size/max-file),否则日志能撑爆磁盘
- 配资源限制(deploy.resources),防止单个容器吃光主机
- 复杂项目拆网络(frontend/backend 分网),数据库只放后端网络,最小权限原则
- 定期
docker system prune -f清理悬空镜像与构建缓存
常见坑
坑 1:app 连不上数据库,报 connection refused。 十有八九是 depends_on 没配 healthcheck——MySQL 容器起来了但还没初始化完。按上文配 condition: service_healthy 解决。
坑 2:端口冲突起不来。 服务间改用服务名通信(桥接网络),宿主机端口冲突时改映射即可,docker compose ps 能看到具体报错。
坑 3:down -v 把数据库数据删了。 卷是持久化的命根子,删除前确认;备份用命名卷的导出(docker run --rm -v <卷名>:/data -v $(pwd):/backup alpine tar czf /backup/db.tar.gz /data)。
坑 4:.env 改了没生效。 环境变量在 up 时注入,改完 .env 需要重新 docker compose up -d(V2 会重新读取;但已运行容器需 recreate)。
坑 5:日志把磁盘写满。 没配 logging 限制的容器日志无限增长,du -sh /var/lib/docker/containers 能确认,配好 max-size/max-file 一劳永逸。
总结
Compose 的价值在于:把「一组容器怎么跑」从记忆和命令历史变成一份可版本化、可分享的配置文件。对中小项目、开发测试环境和流量不大的生产服务,一份写好的 compose 文件配合 CI/CD 完全够用,不必事事上 Kubernetes。
记住核心原则:开发用绑定挂载、生产用镜像内置;敏感信息走 .env/secrets;restart: unless-stopped + healthcheck + 日志轮转 + 资源限制,这四件套配齐,你的 compose 应用栈就稳了。
评论 (0)
暂无评论,快来抢沙发吧!