3. Compose文件结构详细回顾归纳总结
聊到 Compose 文件结构,很多人第一反应就是「不就是个 YAML 嘛」。说得没错,但真正在 CI 里用它做快照回归测试时,细节上的坑往往让人头疼。我个人习惯把 Compose 文件拆成三个层面来看:元信息层、服务定义层、基础设施层。下面我结合项目经验,带你系统地捋一遍。
3.1 顶层字段:不止是 version 和 services
很多同学以为 Compose 文件只有 version 和 services,其实还有 networks、volumes、configs、secrets 等。在 CI 快照回归里,基础设施的定义一旦写错,整个环境就崩了。我遇到过三回,都是因为没显式声明网络,导致容器间互相找不到。下面这张表帮你快速定位每个字段的用途:
| 字段 | 是否必填 | 典型用途 | CI 快照中易踩的坑 |
|---|---|---|---|
| version | 是(推荐) | 指定 Compose 文件格式版本 | 新版引擎已弃用 version,可能导致校验失败 |
| name | 否 | 项目名称,影响容器命名前缀 | 不指定时默认使用目录名,CI 中可能冲突 |
| services | 是 | 定义容器化应用 | 服务依赖顺序错乱导致启动失败 |
| networks | 否 | 容器间网络隔离/互联 | 忘记创建则容器自动加入默认网络,快照不一致 |
| volumes | 否 | 持久化数据或共享目录 | 挂载路径写路径,CI 环境不同会报错 |
| configs / secrets | 否 | 注入配置或敏感数据 | 文件权限或路径写死,导致加载失败 |
3.2 services:服务定义的灵魂
services 里的每个键对应一个容器。你想想看,在 CI 回归测试中,我们要精确控制每个容器的镜像版本、环境变量、端口映射、健康检查等。下面是我常用的几个核心配置项:
- image / build:指定镜像来源。我一般用
image: myapp:${CI_COMMIT_SHA}来保证快照的确定性。 - environment:环境变量。千万注意不要在 YAML 里写
DB_PASSWORD=123456这种硬编码,用${DB_PASSWORD}配合 .env 文件。 - ports:端口映射。快照回归里尽量用
0随机端口,避免冲突。 - depends_on:启动依赖。但依赖只控制顺序,不管健康状态,要配合
condition: service_healthy。 - healthcheck:健康检查。我在一个项目里因为没配健康检查,依赖服务当成了「已启动」,结果测试全跑挂。
depends_on 和 healthcheck 全部显式写出来,不要图省事用默认值。这样生成的快照才是可复现的。
3.3 networks 与 volumes:常见的「隐形杀手」
这两个字段平时容易被忽略,但恰恰是导致回归错误的重灾区。我曾经在 Jenkins 里跑 CI,本地测试好好的,上流水线就报 network not found。查了半天,原来是 Compose 文件没写 networks: 声明,结果不同构建任务共用默认网络冲突了。
来看一个标准写法:
version: '3.8'
services:
app:
image: myapp:latest
networks:
- frontend
- backend
db:
image: postgres:13
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
volumes 这边更要注意。用 volume: 而不是 bind mount 能保证在不同 OS 下路径一致性。比如:
volumes:
data:
driver: local
services:
db:
volumes:
- data:/var/lib/postgresql/data
./data:/data,结果 Linux CI 环境里路径解析出问题,数据全丢了。后来统一用命名卷,再也没出过这种问题。
3.4 实际案例:一个完整的 CI 快照回归 Compose 文件
下面这个例子是我去年在微服务项目中用的,包含了上述所有核心要素。你可以直接抄走作为模板:
version: '3.8'
name: snapshot-regression-test
services:
auth-service:
build:
context: ./auth
dockerfile: Dockerfile.ci
image: auth:${COMMIT_SHA:-latest}
environment:
- DB_HOST=postgres
- REDIS_HOST=redis
ports:
- "0:8080"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 10s
timeout: 5s
retries: 3
postgres:
image: postgres:13-alpine
environment:
POSTGRES_PASSWORD: test123
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 5
redis:
image: redis:6-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 3
networks:
default:
name: regression-net
volumes:
pgdata:
看到没?我把 name 显式指定为 snapshot-regression-test,这样无论哪个 CI Job 运行,容器前缀都是固定的,方便后面做快照对比。
3.5 结构验证小技巧
在写 Compose 文件时,我习惯先跑一个 docker compose config 看实际解析结果。这条命令会展开所有变量、合并多个文件,输出最终生效的结构。如果发现有奇怪的报错,八成是 YAML 缩进或字段拼写错了。另外,建议在 CI 里把这个步骤放到快照生成之前,一旦校验失败就直接中断,别让它悄悄跑下去。
docker compose --dry-run up 模拟启动,可以提前暴露网络冲突、端口占用等问题。我在项目中加了这行后,快照回归的错误率直接降了 60%。
总之,Compose 文件结构看似简单,但每一层都藏着影响回归测试稳定性的细节。把 version、name、services、networks、volumes 这几个关键块吃透,大部分编译失败、容器跑不起来的问题都能提前拦住。