docker-compose up 时一个经典坑:app 容器比 mysql 容器起得还快,app 启动时报 “connection refused”。depends_on 只控制"启动顺序",不等待依赖服务真的可用——你需要更稳的解法。
这一篇把"启动依赖等待"的所有主流方案收齐:wait-for-it.sh、docker-compose-wait、depends_on 的局限、healthcheck 主动检查。
阅读对象:用 docker-compose 编排多服务、被"启动顺序"问题坑过的开发者 覆盖范围:wait-for-it + docker-compose-wait + healthcheck + depends_on 局限 + 最佳实践
一、问题的本质:依赖启动的"就绪"问题
| |
depends_on: db 的真实行为:
- ✅ 控制启动顺序——
db一定先于app启动 - ❌ 不等待
db真的可用——db容器起来后,MySQL 进程可能还要 5-10 秒才能接受连接
结果:app 启动时 mysql:3306 可能还没监听,连接失败。
二、解法 1:wait-for-it.sh
wait-for-it 是社区里最常用的"端口可达性探测"小脚本。
2.1 基础用法
| |
参数:
<host>:<port>——要等待的地址-t <seconds>——超时时间,默认 15s,设为 0 表示无限等--后跟"等到了执行的命令"
输出示例:
| |
2.2 在 docker-compose 中使用
| |
机制:
wait-for-it.sh轮询my_nacos:8848是否 TCP 可达- 可达后执行后面的
java -jar命令- 30 秒超时(默认 15s)后放弃,启动失败
2.3 host 网络模式下的特例
如果 network_mode: "host",容器名解析失效——my_nacos:8848 不通。
| |
Why:host 网络下容器共享宿主机网络栈,容器内看到的 localhost 就是宿主机的 localhost——
my_nacos容器名解析不到,得用localhost。
三、解法 2:docker-compose-wait
docker-compose-wait 是用 Python 写的"等待一组服务"工具,比 wait-for-it 强大:
- ✅ 支持 多个 host:port
- ✅ 支持 环境变量配置(不用改 compose 文件)
- ✅ 支持 健康检查命令(不仅是端口)
- ✅ 支持 自定义等待策略
3.1 安装
在 Dockerfile 里装:
| |
3.2 compose 文件
| |
优势:
- 多端口一行配置
- 超时、间隔都走环境变量
- 支持 healthcheck 字符串——
WAIT_HOSTS: "tcp://db:3306, http://api/health"
四、解法 3:depends_on 长语法(Docker Compose v2)
Docker Compose v2(2020-08 GA)扩展了 depends_on:
| |
What:
condition: service_healthy——等 db 的 healthcheck 通过再启动 appcondition: service_started——等 db 容器启动(默认行为)- 不需要 wait-for-it 这种外部脚本
但:
- 必须配 healthcheck——不带 healthcheck,service_healthy 永远不通过
- Docker Compose v1 不支持——必须升 v2(
docker compose插件)
五、解法 4:healthcheck + restart
最朴素但有效的方案:给容器加 healthcheck,启动失败自动重试。
| |
机制:
- 容器启动后 30s 内(
start_period)不计入健康检查失败次数- 之后每 10s 查一次,连续 5 次失败标 unhealthy
- Docker Compose v2 的
service_healthy会等这个状态
配合 depends_on 长语法:
| |
这是 Docker Compose v2 时代的最佳实践——不需要外部脚本,配置即文档。
六、解法 5:应用层重试
最不优雅但最 robust 的方案:应用启动时自带重试。
| |
或用 Spring Boot 的 datasource retry:
| |
优势:不依赖任何外部编排——裸跑 docker run 也能 work 劣势:耦合进应用代码、不是纯基础设施问题
七、对比表
| 方案 | 配置复杂度 | 适用场景 | 推荐度 |
|---|---|---|---|
| wait-for-it.sh | 低 | 单端口、简单场景、Compose v1 | ⭐⭐⭐ |
| docker-compose-wait | 中 | 多端口、需要可配置 | ⭐⭐⭐ |
| depends_on + healthcheck (v2) | 中 | Compose v2 + 多服务依赖 | ⭐⭐⭐⭐⭐ |
| 应用层重试 | 高 | 框架级稳健性 | ⭐⭐⭐⭐ |
2020 年后推荐:
- Compose v2 +
depends_on: condition: service_healthy+ 每个服务配 healthcheck- 裸 docker run + 应用层重试
- wait-for-it / docker-compose-wait 仅在 Compose v1 或老项目使用
八、最佳实践总结
- 每个长跑服务都加
healthcheck——不只是依赖方 - depends_on 用长语法——
condition: service_healthy(Compose v2) - 避免 wait 外部脚本——能配置的不用脚本
- 超时一定要设——
start_period: 30s给慢启动服务缓冲 - 应用层有兜底——基础设施层等不到时,应用自己重试
- 用
restart: on-failure——启动失败时容器自动重试(不是always,避免无谓重启)
九、要点回顾
depends_on不等待可用——只控制启动顺序- wait-for-it 适合单端口、Compose v1 项目
- docker-compose-wait 适合多端口
- healthcheck + service_healthy 是 Compose v2 最佳实践
- 应用层重试是最 robust 的兜底
- host 网络下容器名解析失效,用
localhost
十、小结
“启动依赖等待"是 docker-compose 里最经典的"看起来能 work 但实际跑起来就崩"的问题。把 healthcheck + depends_on: service_healthy 配成肌肉记忆,比写一堆 wait 脚本清爽。
下一步:理解了启动顺序,下一步是 K8s 的 Init Container + Readiness Probe——把"启动依赖等待"从编排层下沉到容器层,配合 Pod lifecycle hook(
postStart/preStop)做更精细的启动 / 关停控制。K8s 1.20+ 的startupProbe专门解决"慢启动服务"的 readiness 判断问题。
