本节摘要:核心手艺站——声明文件的完整写法。以"Web 应用加数据库加缓存"的三服务编队为样本,逐字段讲透服务、网络、卷三大块,以及手工命令到声明语法的翻译对照表。
上一节看了两只箱子的迷你编队,本节把样本升级成真实规模:Web 应用、数据库、缓存三只箱子,外加一条专属航道与一个数据仓库。完整的声明文件如下,先通读再拆解:
# compose.yaml:三服务编队的全部事实 services: # 第一块:编队成员 web: build: . # 用当前目录的装箱单现造(而非现成镜像) ports: - "8080:8000" # 泊位映射,与 run 的 -p 同义 environment: # 环境变量,与 run 的 -e 同义 - DB_HOST=db - CACHE_HOST=cache - TZ=Asia/Shanghai volumes: # 绑定挂载:开发热载配置 - ./conf:/app/conf:ro depends_on: # 依赖:等数据库健康再起 db: condition: service_healthy restart: unless-stopped # 值夜策略,与 run 同义 healthcheck: # 应用自身的就绪探测 test: ["CMD", "wget", "-qO-", "http://localhost:8000/health"] interval: 10s timeout: 3s retries: 3 db: image: postgres:16-alpine # 现成镜像直接提货 environment: - POSTGRES_PASSWORD=devpass - POSTGRES_DB=orders volumes: - db-data:/var/lib/postgresql/data # 数据仓库:业务数据落卷 healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 3s retries: 5 cache: image: redis:7-alpine command: redis-server --appendonly yes # 覆盖默认启动命令 volumes: - cache-data:/data volumes: # 第二块:编队仓库(声明式建卷) db-data: cache-data: networks: # 第三块:编队航道(不写则自动建默认航道) default: driver: bridge
服务块的字段几乎能与第 2、4 章的 run 参数一一对应,翻译对照表如下:
| 声明字段 | 等价手工参数 | 说明 |
|---|---|---|
| image 或 build | 镜像名 或 装箱单 | 二选一:提现货或现场造 |
| ports | -p | 泊位映射 |
| environment | -e | 环境变量注入 |
| volumes | -v | 卷或绑定挂载 |
| restart | --restart | 重启策略 |
| command | 镜像默认命令的覆盖 | 追加在入口之后 |
| depends_on | 人工起吊顺序 | 声明式依赖,配合健康检查 |
| healthcheck | 无手工等价物 | 就绪探测,编队的核心增量 |
| container_name | --name | 一般不写,让编队自动命名 |
三个字段值得展开。build 与 image 的分工:本地开发用 build(改代码即重建),联调与部署用 image(提现成的版本货);两者可以并存——build 负责造,image 给造出来的货定名。command 覆盖镜像的默认启动参数,Redis 开持久化这类"官方镜像加自己的参数"的常见需求靠它。container_name 建议不写:编队会生成"项目-服务-序号"的规范化名字,天然支持后续扩容多个副本,手工指定名字反而把扩容路堵死。
网络:什么都不写,编队自动建一条默认航道,所有服务接入、服务名即域名——6.4 节的 DB_HOST=db 直接生效的原因就在这。需要多航道隔离(比如前端区与数据区分开)时才显式声明并给服务指定 networks。
卷:服务块里 db-data:/var/lib/postgresql/data 中的卷名必须在顶层 volumes 块登记——这是声明式风格的一个适应点:所有资源先声明后引用。登记后编队负责建卷、收港时默认保留卷(数据安全默认值,好设计)。对照记忆:绑定挂载(宿主路径写法 ./conf)不需要登记,只有具名卷要。
写完声明文件先别急着 up,两条验证命令帮你把语法与语义错误拦在起航前:
# 语法与规范校验:不执行,只检查文件合法性 docker compose config --quiet # (无输出即合法;有问题会指出具体行与原因) # 展开渲染:把声明渲染成引擎将执行的最终配置(变量已替换) docker compose config | head -30 # name: my-fleet # services: # web: # build: # context: . # ...
config 渲染是排障利器:环境变量引用、默认值填充、依赖条件——一切"文件里写了但不确定最终长什么样"的疑问,渲染一遍全见分晓。
写个反面样本感受校验的价值:
# 错误样本:引用了未在顶层声明的卷 services: db: image: postgres:16-alpine volumes: - ghost-data:/var/lib/postgresql/data # ghost-data 未登记 # (顶层缺 volumes: ghost-data 的声明)
# docker compose config 的报错(输出节选): # service "db" refers to undefined volume ghost-data: # invalid compose project
错误指向精确到字段——声明式工具的好处是错误也能说得明白。
文件会写了,下一节把编队操练起来:起航、收港、扩缩与项目机制。