DK-05 Compose编排
05 · Compose 编排(Docker Compose)
📅 预计 60 分钟 | ⭐ = 重要知识点 | 📌 中英术语见文末
🛠 本讲命令基于 Docker Compose v2,可直接运行
5.0 先给直觉:从"点菜"到"下套餐"
一个真实的应用很少只有一个容器:前端、后端、数据库、缓存……动不动三四个。一个个 docker run 手动敲,又长又容易漏,还记不住谁依赖谁。
Compose 是 Docker 官方的多容器编排工具:把整组容器写进一份 YAML 文件("套餐菜单"),一条命令 docker compose up 全部拉起,一条命令 docker compose down 全部收起。
💡 记忆口诀:Compose = 多容器的"套餐清单";写 YAML 定义服务,
up/down一键起落。
5.1 认识 Compose
一份"服务清单"
Compose 用一份名为 compose.yaml(或 docker-compose.yaml)的 YAML 文件描述整个应用。文件里每个服务(service)通常对应一个容器(或一组相同副本)。
docker run 参数和 Compose 字段的对应关系,是理解它的捷径:
docker run 参数 |
Compose 字段 |
|---|---|
--name / 镜像 |
services.服务名 + image |
-p 8080:80 |
ports: ["8080:80"] |
-v ./html:/usr/share/nginx/html |
volumes: ["./html:/usr/share/nginx/html"] |
-e KEY=value |
environment: KEY=value |
--restart=always |
restart: always |
第一个 Compose 文件 ⭐
services:
web:
image: nginx:1.25
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html
启动与停止:
docker compose up -d # 后台启动整组服务
docker compose ps # 查看这组服务的状态
docker compose down # 停止并删除这组容器
访问 http://localhost:8080,看到的就是 ./html 目录下的静态页面——本地改文件,刷新即生效。
⚠️ 常见错误
- YAML 缩进用 Tab:YAML 只能用空格,Tab 会直接解析报错。编辑器务必开启"显示空白字符"。
- 版本号滥用:Compose v2 不需要(也不建议写)
version:字段,那是旧版遗留,写了可能告警。 - 把
docker run思维硬搬:Compose 里端口、卷都用字符串("8080:80"),YAML 会把8080:80当字符串处理,不加引号可能被解析成别的类型。
5.2 服务定义常用字段
一个典型的多服务示例:Web + 数据库 + Redis,这是最常见的"套餐"。
services:
web:
build: .
image: myapp:1.0
ports:
- "8080:80"
environment:
DB_HOST: db
DB_PASSWORD: secret123
depends_on:
- db
restart: unless-stopped
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: appdb
volumes:
- dbdata:/var/lib/mysql
redis:
image: redis:7-alpine
volumes:
dbdata:
逐个字段解释 ⭐:
image:直接用现成镜像。build:用本地 Dockerfile 构建(可与image一起用,给产物命名)。ports:端口映射,["宿主:容器"]。volumes:卷或绑定挂载;命名卷要在文件底部的顶层volumes:里声明。environment:注入环境变量(对应-e)。env_file:从文件批量读取环境变量(对应--env-file)。depends_on:声明依赖关系,控制启动顺序。restart:no/always/unless-stopped/on-failure。networks:指定加入哪个网络(不写则用默认项目网络)。
一个完整的 Web + 数据库实战 ⭐
用"静态页 + MySQL"组合走一遍完整流程,理解前面所有字段怎么配合。目录结构:
project/
├── compose.yaml
├── html/
│ └── index.html
└── init/
└── init.sql # 首次启动时执行的建表语句
compose.yaml:
services:
web:
image: nginx:1.25
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html
depends_on:
db:
condition: service_healthy
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: appdb
volumes:
- dbdata:/var/lib/mysql
- ./init:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 3s
retries: 5
volumes:
dbdata:
启动:
docker compose up -d
docker compose ps # 两个服务都是 healthy/running
要点:
./init挂到/docker-entrypoint-initdb.d,MySQL 镜像首次建库时会自动执行里面的.sql脚本(只执行一次,卷已存在则跳过)。depends_on+service_healthy保证 web 在 db 真正可连后才启动。- 数据都落进
dbdata命名卷,down不删,重建容器数据还在。
这就是"一个真实的容器化应用"的最小骨架:一个对外服务、一个数据库、一个持久卷、一份健康检查。
变量替换与校验
Compose 文件里可以用 ${变量名} 引用环境变量,适合"同一份文件,不同环境填不同值":
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-root123} # 默认值语法 :-root123
${DB_PASSWORD:-root123} 表示:环境变量存在就用它的值,不存在就用 root123。Compose 在解析文件时会读取宿主机环境变量或同目录 .env 文件。
写完文件先检查再启动,是好习惯:
docker compose config # 打印解析后的最终配置,校验语法
它会把变量替换、默认值、缩进错误全部暴露出来,docker compose up 之前先跑一遍能省很多报错。
⚠️ 常见错误
- 命名卷忘记在顶层声明:
dbdata:要在文件最底部的顶层volumes:出现,否则报错或匿名化。 - 以为
depends_on保证"数据库就绪":它只保证启动顺序(先起 db 再起 web),不保证 db 内部就绪。真正的就绪等待要靠健康检查(condition: service_healthy,第 6 讲)。 - 服务名和容器名混淆:Compose 里容器名会自动带项目名前缀(如
项目名-db-1);服务内通信用服务名(db),不是容器名。 - 改
.env后忘了重新解析:Compose 在up时读取变量,改了.env要重新up -d生效,光改文件不重启没用。
5.3 常用命令
| 命令 | 作用 |
|---|---|
docker compose up -d |
后台启动/重建整组服务 |
docker compose up -d --build |
先重新构建镜像再启动 |
docker compose ps |
查看服务状态 |
docker compose logs -f |
实时看所有服务日志 |
docker compose logs web |
只看 web 的日志 |
docker compose exec web bash |
进入某个服务容器 |
docker compose stop |
停止(不删除容器) |
docker compose start |
启动已停止的服务 |
docker compose restart |
重启 |
docker compose build |
只构建镜像,不启动 |
docker compose down |
停止并删除容器、网络 |
docker compose down -v |
连命名卷一起删除(数据也没了) |
注意新老命令的区别 ⭐:
- 新版:
docker compose(子命令,推荐)。 - 旧版:
docker-compose(独立二进制,已被取代)。
看到文档里两种写法,优先用docker compose。
⚠️ 常见错误
down不等于stop:stop只是停,容器还在;down会删除容器(-v还会删卷)。- 用
docker exec而不是docker compose exec:Compose 容器名带了项目前缀,直接docker exec db bash常找不到容器,用docker compose exec db bash更稳妥。 - 改了 YAML 不重新
up:只改文件不up -d,运行中的服务还是旧配置。up -d会自动检测配置变化并重建。
5.4 项目与网络:Compose 怎么组织
项目名
Compose 把整组服务归为一个项目(project),默认用当前目录名作项目名,也可以指定:
docker compose -p myproject up -d # 指定项目名
项目名直接影响容器名、网络名的前缀,如 myproject-db-1、网络 myproject_default。一套目录一套项目,互不干扰。
默认网络
Compose 会为项目自动创建一个自定义网络(默认模式就是自定义 bridge),项目内所有服务都接入它。这正是第 3 讲说的"自定义网络 + 容器名互访"——所以 web 能直接用 db 这个名字连数据库(见示例里的 DB_HOST: db)。
连接外部网络
默认网络只有本项目的服务能用。需要和项目之外的容器互通时,把网络声明为外部网络并接入:
networks:
shared:
external: true # 外部网络,由 docker network create shared 预先创建
services:
app:
image: myapp:1.0
networks:
- shared
external: true 告诉 Compose "这个网络已经存在,不要创建"。共享网络常用于让多个独立项目访问同一个服务(比如共用一套 Redis、Nginx 网关)。
服务配置的多种写法
同一个服务既能 image 直接拉,也能 build 现构建:
services:
api:
build: ./backend # 用本地 Dockerfile 构建
image: myapi:1.0 # 构建产物命名(可选)
build 指向含 Dockerfile 的目录,image 给构建产物起名,方便之后直接分发。只写 image 不写 build,则纯用仓库镜像,不本地构建。
⚠️ 常见错误
- 跨项目访问想用服务名:不同 Compose 项目默认网络不同,直接
curl http://db连不通;要么放同一网络,要么用外部网络(external: true)。 - 以为
down会删卷:down只删容器和网络,命名卷默认保留(数据安全),只有显式-v才删。 - 多个服务占同一宿主端口:
ports里写"8080:80"和"8080:81"会冲突,起第二个时报端口被占用。
📌 双语术语表(本讲)
| 中文 | English | 记忆点 |
|---|---|---|
| 编排 | orchestration | 管理多个容器 |
| 服务 | service | Compose 里的一个容器定义 |
| 项目 | project | 一组服务的集合 |
| 依赖关系 | depends_on | 控制启动顺序 |
| 环境变量文件 | env_file | 从文件注入配置 |
| 重启策略 | restart | 崩溃后自动拉起 |
| 命名卷 | named volume | 顶层声明后复用 |
| 外部网络 | external network | 跨项目共享 |
| 变量替换 | variable substitution | ${VAR:-default} |
| 配置校验 | compose config | 检查最终配置 |
| 构建服务 | build | 本地 Dockerfile 构建 |
⭐ 本讲考点清单
- Compose 用 YAML 描述多容器应用,一条
up拉起整组 services.服务名+image/build定义服务- 常用字段:
ports/volumes/environment/env_file/depends_on/restart depends_on只保证启动顺序,不保证服务就绪- 命名卷需在顶层
volumes:声明 docker compose up -d/down/ps/logs -f/execdown默认不删命名卷,down -v才删- 新版
docker composevs 旧版docker-compose - 项目默认一个自定义网络,服务间用服务名互访
- YAML 缩进用空格,端口/卷写成字符串
${VAR:-默认值}变量替换;docker compose config校验配置build指定 Dockerfile 目录,image给产物命名或直接用仓库镜像external: true接入外部预建网络,实现跨项目互通- MySQL 镜像的
/docker-entrypoint-initdb.d挂载目录:首次建库自动执行 .sql - 完整应用骨架 = 对外服务 + 数据库 + 持久卷 + 健康检查