docker-compose.yml 的 services 必须是映射而非列表,build context 路径相对于 yml 文件位置,depends_on 不保证服务就绪,env_file 加载遵循后覆盖前且被 environment 覆盖。

docker-compose.yml 里 services 字段必须是映射,不是列表
很多人把 services 写成数组格式,比如加了中括号,结果 docker-compose up 直接报错:yaml: unmarshal errors: line X: cannot unmarshal !!seq into config.Config。这是因为 Compose 规范强制要求 services 是一个键值对映射(map),每个服务名作为 key,配置作为 value。
实操建议:
- 检查缩进:服务名顶格写,后面跟冒号,下一级配置缩进 2 空格(不是 tab)
- 避免在服务名后加
-或[],那是 YAML 列表语法 - 用
docker-compose config验证结构,它会输出解析后的配置,出错时立刻暴露层级问题
build context 路径必须相对于 docker-compose.yml 所在目录
你在 docker-compose.yml 里写 build: ./src,但实际执行命令的路径不在 yml 文件所在目录,docker build 就会找不到上下文,报错:ERROR: build path ./src either does not exist, is not accessible, or is not a valid URL。
实操建议:
-
context路径永远以docker-compose.yml文件位置为基准,不是当前 shell 工作目录 - 如果项目结构复杂(比如 yml 在
deploy/下,Dockerfile 在./),就写context: ..,再配dockerfile: ./Dockerfile - 不要依赖
cd切换目录来“绕过”路径问题——CI/CD 和远程部署时不可靠
depends_on 不等于等待服务就绪
depends_on 只控制容器启动顺序,不检查端口是否监听、数据库是否可连。你看到 db 容器先启,app 还是连不上 Connection refused,就是这个坑。
实操建议:
- 用
healthcheck配合condition: service_healthy替代裸depends_on - 在应用代码里实现重试逻辑(比如 Python 的
tenacity,Go 的backoff),别指望编排层兜底 - 临时调试可用
docker-compose exec app sh -c "apk add --no-cache curl && curl -f http://db:5432"手动验证连通性
env_file 加载顺序和覆盖规则容易误判
多个 env_file 同时存在时,后写的文件会覆盖前面同名变量;但 environment 字段里的变量又会覆盖所有 env_file —— 这个优先级链不明确,常导致本地测试正常、上线后环境变量失效。
实操建议:
- 用
docker-compose config查看最终生效的environment值,确认是否被意外覆盖 - 避免混用
.env(顶层环境变量)、env_file(服务级)、environment(硬编码)三层机制 - 敏感变量(如密码)别放
env_file,改用secrets或运行时注入
最麻烦的从来不是写对 yml,而是搞清哪一层变量在哪个阶段生效、哪个 healthcheck 没触发、哪个 context 路径在 CI 里变了。多跑一次 docker-compose config,比重启十次容器更省时间。










