快速修复
# 给依赖服务加 healthcheck,app 用长写法 depends_on:
services:
db:
image: your-db-image
healthcheck:
test: ["CMD", "your-healthcheck-cmd"] # 必须在镜像里存在!
interval: 5s
timeout: 3s
retries: 5
start_period: 10s # 启动期失败不计入 retries(服务启动慢必配)
app:
image: your-app-image
depends_on:
db:
condition: service_healthy # 等 db 健康才启动 app
docker compose up -d
docker compose up --wait --wait-timeout 60 # 验收:全部 healthy 才算成功
你会遇到什么
docker compose up 后,依赖方(app)报 Connection refused(连数据库/上游服务失败)、dependency failed to start: container xxx is unhealthy,或者 docker compose ps 里服务一直显示 unhealthy。
网上教程都写了 depends_on,你也写了,还是不行——普通 depends_on 只保证启动顺序,不保证依赖就绪:db 容器起来了 ≠ 端口能连。
本文路径:快速修复(一段 compose 片段)→ 一步步排查(可跳读)→ 原理与变体(症状/原因/证据)。赶时间直接跳到「快速修复」。
为什么会这样
compose 的 depends_on 有两种语义:启动顺序(短写法 depends_on: [db]:先创建/先启动 db,不等就绪)和就绪条件(长写法 depends_on: db: condition: service_healthy:等 db 的健康检查通过才启动 app)。
数据库/中间件从「进程启动」到「端口可连」通常要几秒(实测:本机 python http.server 模拟的 db 要 sleep 4 秒),这期间连接必然被拒。
就绪条件依赖健康检查(healthcheck):compose 按 interval 周期执行 test 命令,连续失败 retries 次判 unhealthy,成功即 healthy。up --wait 会等所有服务 healthy(可配 --wait-timeout 防止无限等)。
健康检查有三个高频坑,全部实测复现:① test 用了镜像里没有的二进制(如 alpine 没有 busybox httpd applet)→ 容器直接 exit 127,compose 报 dependency failed to start ... exited (127);
② 服务启动慢但没设 start_period → 启动期连续失败被误判 unhealthy(实测:服务 4 秒才就绪,interval 1s × retries 3 在 ~3 秒时已判死);
③ 检查命令本身写错/永远失败 → ps 显示 Up x seconds (unhealthy)、up --wait 超时报错。
确定性推论:看到 unhealthy 先看 docker inspect <容器> --format "{{json .State.Health.Log}}" 的最后一条输出——它直接告诉你检查命令为什么失败;看到 exited (127) 则先确认 test 命令在镜像里存在。
一步步来
1. 先分清报错类型 — 必做
Connection refused:app 启动太早,依赖还没就绪 → 上健康检查 +condition: service_healthy。dependency failed to start ... is unhealthy:健康检查失败 → 看第 2 步。dependency failed to start ... exited (127):健康检查命令在镜像里不存在 → 换命令或换镜像。
2. 查健康检查为什么失败 — 必做
docker compose ps # 看哪个服务 unhealthy
docker inspect <容器名> --format "{{json .State.Health.Log}}" | tail -c 800
最后一条 log 的 Output 就是检查命令的真实报错(实测能看到 urllib 的 ConnectionRefused traceback)。
3. 检查 test 命令在镜像里存在 — 必做
docker run --rm <镜像> sh -c "which <命令>" # 不存在 → 换命令
实测反例:alpine 里 busybox httpd 不存在(httpd: applet not found,exit 127)。
4. 启动慢的服务补 start_period — 按诊断
服务要几秒才就绪(拉配置、等端口)时,start_period 必须大于就绪时间,否则启动期失败会计入 retries 被误判 unhealthy(实测:无 start_period 时 4 秒就绪的服务在 ~3 秒被判死)。
5. 用 up --wait 验收 — 必做
docker compose up -d
docker compose up --wait --wait-timeout 60 # 超时即失败,不无限等
怎么确认修好了
docker compose up --wait --wait-timeout 60 退出码 0;docker compose ps 全部 Up x seconds (healthy);app 日志不再有 Connection refused,业务请求成功(实测:db healthy 后 app 立即 APP_OK)。
其它环境的表现
| 写法 | 行为 |
|---|---|
depends_on: [db] | 只保证启动顺序;app 可能先于 db 就绪启动 → Connection refused |
depends_on: db: condition: service_healthy | 等 db 健康检查通过才启动 app(推荐) |
| 健康检查命令不存在 | 容器 exit 127 → dependency failed to start ... exited (127) |
| 启动慢 + 无 start_period | 启动期失败计入 retries → 误判 unhealthy |
| 健康检查永远失败 | ps 显示 unhealthy;up --wait 超时报 dependency failed to start ... is unhealthy |
症状
app-1 | wget: can't connect to remote host (172.18.0.3): Connection refused
app-1 | APP_CONN_REFUSED
db-1 | httpd: applet not found
db-1 exited with code 127
dependency failed to start: container depstest-db-1 exited (127)
Container depstest-db-1 Waiting
Container depstest-db-1 Error
dependency failed to start: container depstest-db-1 is unhealthy
特征:compose 里写了 depends_on 仍然连接被拒;或 ps 显示 unhealthy;或容器 exit 127。
原因
普通 depends_on 只保证启动顺序不保证就绪;就绪需 healthcheck + condition: service_healthy。健康检查失败三来源:命令在镜像中不存在(exit 127)、启动慢没配 start_period(误判 unhealthy)、检查命令自身错误(持续 unhealthy)。
解法
- 依赖服务加
healthcheck(test 命令必须存在于镜像;启动慢加start_period); - app 改用
depends_on: db: condition: service_healthy; docker compose up -d后docker compose up --wait --wait-timeout 60验收;- unhealthy 时
docker inspect <容器> --format "{{json .State.Health.Log}}"看检查命令真实报错。
验证证据
- 本机实测复现(2026-08-31,WSL 定制预构建版,docker 29.1.3 + compose 2.40.3):
- 相位1:普通 depends_on → app 立即启动 →
APP_CONN_REFUSED(db sleep 4s); - 相位2:healthcheck +
condition: service_healthy+ start_period 6s → db healthy 后 app 启动 →APP_OK; - 变体A:无 start_period → 启动期 3 次失败 →
dependency failed to start ... is unhealthy(服务 4s 后才就绪); - 变体B:db 用
busybox httpd(alpine 无此 applet)→httpd: applet not found、exit 127; - 相位3:healthcheck
["CMD","false"]→ ps 显示Up 7 seconds (unhealthy)、up --wait报 unhealthy 错误。 - 实测复现日志(2026-08-31)
相关 canonical
按「前置 → 同根 / 同域 / 相关」读:
- 前置(先排除):
- docker compose / docker compose command not found(同域:compose 启动链路的前置问题)
- 相关(报错相近,根因不同):
- Docker daemon 连不上(起栈前 daemon 必须活着,连接错误先于 compose 语义)
适用工具 (1)
本问题方案已挂到下列工具的方案页——从工具页也能回到这里,双向可达。
docker · 外部工具来源与证据
← 编码代理与终端 问题域 | 问题货架 | 遇到其它问题?去 问题空间 提交求助卡