第 8 章 Docker Compose:一键编排多容器
本章目标:掌握 compose.yaml 的结构与常用指令,理解它与
docker run的对应关系,能一条命令启动/管理多服务应用。
1. 为什么需要 Compose
真实应用很少只有一个容器:Web 服务 + 数据库 + 缓存 + 消息队列……用 docker run 一条条敲,问题立刻显现:
- 命令又长又乱,端口、环境变量、卷、网络反复写;
- 忘记加
--network,容器间就连不上; - 团队协作时每个人手敲的启动方式都不一样。
Compose 把"应用由哪些服务组成"声明式地写进一个 YAML 文件(compose.yaml),docker compose up -d 一条命令启动全部,down 一键清理。Compose 是单机编排的标准方式(多机编排是后面的事,如 Swarm/K8s)。
2. compose.yaml 的结构
yaml
services: # 服务列表:一个服务 ≈ 一个容器
web: # 服务名(同时是网络内的域名)
image: nginx:alpine
ports:
- "8086:80"
cache:
image: redis:7-alpine
networks: # 自定义网络(可选,不写也会自动创建默认网络)
default:
driver: bridge
volumes: # 具名卷(可选)
db-data:要点:
services是唯一必填的顶层键;networks/volumes不写时 Compose 也会自动创建默认网络/匿名卷;- 服务名即域名:同一项目内,服务之间直接用服务名访问(
redis、web),自动获得第 5 章讲的容器名 DNS 解析; - 现代 Compose 不再需要
version:字段(写了对新版本反而是警告)。
3. 服务字段:与 docker run 的对应
Compose 字段基本就是 docker run 选项的声明式翻译:
| docker run | compose.yaml(服务字段) | 说明 |
|---|---|---|
-p 8086:80 | ports: ["8086:80"] | 端口映射(列表) |
-e KEY=val | environment: [KEY=val] 或 map | 环境变量 |
-v 卷:/路径 | volumes: ["卷:/路径"] | 挂载(卷名或相对路径) |
--network x | networks: [x] | 加入的网络 |
--restart x | restart: x | 重启策略 |
--name n | container_name: n(一般不写,让 compose 命名) | 容器名 |
| — | depends_on: | 声明服务依赖与启动顺序 |
| — | healthcheck: | 健康检查(配合 depends_on 条件启动) |
docker build | build: . + image: 名 | 本地构建(第 6 章) |
depends_on 的两种用法:
yaml
services:
web:
depends_on:
- db # 简单版:只保证 db 先启动(不保证就绪)
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secret
healthcheck: # 健康检查:mysqladmin ping 通才算"健康"
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-psecret"]
interval: 5s
timeout: 3s
retries: 10
app:
depends_on:
db:
condition: service_healthy # 进阶版:等 db 健康后再启动 app
depends_on只解决"启动顺序",不保证依赖"就绪";要等就绪必须配合healthcheck+condition: service_healthy。
4. 常用命令
bash
docker compose up -d # 启动全部服务(后台;首次会自动构建/拉取)
docker compose up -d --build # 构建后启动(改了 Dockerfile/代码时用)
docker compose ps # 查看服务状态
docker compose logs -f web # 跟踪某个服务的日志
docker compose exec web sh # 进入某个服务
docker compose config # 校验并展开最终的 compose 配置(排错利器)
docker compose pull # 拉取所有镜像
docker compose down # 停止并删除容器(默认保留卷)
docker compose down -v # 连具名卷一起删除(数据没了,慎用!)关键区别:
up与run:up启动的是"长期服务";docker compose run web cmd是临时跑一次命令;down与stop:down删容器(保留卷),stop只停;down -v会删卷——数据库数据一并清除,只应在确定不要数据时使用。
5. 完整示例:三服务应用(本机可复现)
配套 examples/08-docker-compose/,全部镜像已在本地,秒级启动:
yaml
# examples/08-docker-compose/compose.yaml
services:
web:
image: nginx:alpine
ports:
- "8086:80"
volumes:
- ./html:/usr/share/nginx/html
depends_on:
- cache
- app
cache:
image: redis:7-alpine
app:
image: alpine
command: ["sh", "-c", "sleep 300"]
depends_on:
cache:
condition: service_started启动并验证(服务名即域名):
bash
cd examples/08-docker-compose
docker compose up -d
docker compose ps # 三个服务都 running
curl http://localhost:8086/ # web 可访问
docker compose exec app ping -c 1 cache # app 里按服务名 ping 通 redis
docker compose logs --tail 5 web # 看 web 日志
docker compose down # 一键清理扩展:加数据库只需在
services下追加db服务(image: mysql:8.0+environment+healthcheck),并让依赖它的服务depends_on: db: {condition: service_healthy}——Compose 的编排能力让"加一个服务"变成"加几行 YAML"。
常见误区
- 误区:
depends_on能保证依赖就绪。 正确:它只保证启动顺序;要等就绪必须healthcheck+condition: service_healthy。 - 误区:
down会清掉数据。 正确:down保留具名卷;down -v才会删卷,慎用。 - 误区:compose.yaml 必须写
version: '3'。 正确:现代 Compose 已不需要 version 字段。 - 误区:不同服务之间用 localhost 访问。 正确:服务间通信用服务名(
http://web:80),localhost只指容器自身。 - 误区:
docker compose和docker-compose一样。 正确:docker compose是 Compose v2(集成进 docker CLI);docker-compose是独立的 v1 老工具,新项目用 v2。
小结
- Compose = 用 YAML 声明式描述多服务应用,
up -d一键启动、down一键清理。 - 结构:
services(必填)+networks/volumes(可选);服务名即网络内域名。 - 服务字段与
docker run选项一一对应(ports/environment/volumes/restart/depends_on/healthcheck)。 depends_on管顺序,healthcheck + condition: service_healthy管就绪。down保留卷,down -v删卷(慎用);docker compose config校验配置。
练习
- 在
examples/08-docker-compose/运行docker compose up -d,用docker compose ps观察三个服务的状态。 - 用
docker compose exec app ping -c 1 cache验证服务名 DNS 解析;再试ping -c 1 web,理解 compose 自动网络。 - 在
compose.yaml中给web加healthcheck(如wget -q --spider http://localhost/),用docker compose ps观察健康状态列。 - 分别执行
docker compose down与docker compose down -v,对比docker volume ls的变化,理解-v的破坏性。 - 思考题:为什么说"服务之间用 localhost 互相访问"是常见的错误?(提示:localhost 在容器里指的是容器自己。)
可运行示例见
examples/08-docker-compose/,按其中的 README 步骤执行。