Docker Compose 服务依赖与健康检查:healthcheck 让容器按正确顺序启动
这篇文章解决什么问题
用 Docker Compose 编排多容器应用时,最常见的坑是「容器起来了,但服务还没就绪」:数据库容器明明处于 running 状态,你的 Web 应用却因为连不上数据库而崩溃退出。本文教你用 depends_on 配合 healthcheck,让容器按照正确的、可运行的顺序启动,读完就能直接套用到自己的 docker-compose.yml 里。
适合谁读:已经会用 Compose 跑单个容器,但还没处理过多服务启动顺序的开发者。
先看问题:depends_on 的局限
很多人以为写了 depends_on 就能解决顺序问题:
services:
web:
build: .
depends_on:
– db
db:
image: postgres:16
这段配置的真实含义是:等 db 容器「启动」后再启动 web 容器。注意这里说的是「容器进程启动」,而不是「PostgreSQL 真正接受连接」。PostgreSQL 从容器启动到 ready for connections,中间还要经历初始化数据目录、加载配置、监听端口等过程,通常需要几秒到十几秒。如果 web 在数据库 ready 之前就发起连接,就会得到 connection refused 或 the database system is starting up 这类错误,很多场景下应用会直接报错退出,随后被 restart 策略反复拉起。
healthcheck:告诉 Docker 什么才算「就绪」
healthcheck 让 Docker 通过一个命令来探测容器是否健康,返回空闲即视为通过。给 PostgreSQL 加一个健康检查:
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: example
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 10
start_period: 10s
逐项解释:
- test:健康检查命令。pg_isready -U postgres 是 PostgreSQL 官方镜像自带的就绪探测命令,退出码 0 表示数据库已可连接。CMD-SHELL 表示通过 shell 执行字符串;如果命令本身带参数想避免 shell,也可以用 ["CMD", "pg_isready", "-U", "postgres"] 这种数组形式。
- interval:每次检查的间隔,默认 30s。
- timeout:单次检查的超时时间,超时视为失败,默认 30s。
- retries:连续失败多少次才把容器标记为 unhealthy,默认 3 次。
- start_period:容器启动后的宽限期,这段时间内的失败不计入 retries,给慢启动的服务留出初始化时间。
加了 healthcheck 后,可以用 docker inspect 查看容器状态:
docker inspect –format='{{.State.Health.Status}}' <容器名>
会依次看到 starting → healthy(或 unhealthy)。
depends_on 的 condition 条件
从 Compose 文件格式 2.x 起,depends_on 支持 condition 子句,让依赖关系精确到「健康状态」:
services:
web:
build: .
depends_on:
db:
condition: service_healthy
ports:
– "8080:8080"
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: example
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 3s
retries: 10
start_period: 10s
现在 Compose 会先启动 db,等待它的 healthcheck 返回 healthy,之后才启动 web。这样 web 启动时数据库已经可以正常接受连接了。
condition 支持三个值:
- service_healthy:等待目标服务进入 healthy 状态(最常用)
- service_started:等待目标服务启动(等价于不写 condition 的默认行为)
- service_completed_successfully:等待目标服务运行完成并成功退出,适合一次性任务(如数据库迁移容器)
一个完整可运行的例子
下面是一个 Web 应用(用 curl 探活)+ Redis + PostgreSQL 的完整编排,web 同时依赖两个后端服务都健康:
services:
web:
image: nginx:1.25
depends_on:
db:
condition: service_healthy
cache:
condition: service_healthy
ports:
– "8080:80"
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: example
POSTGRES_DB: app
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d app"]
interval: 5s
timeout: 3s
retries: 10
start_period: 10s
cache:
image: redis:7
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 10
Redis 官方镜像自带 redis-cli,用 redis-cli ping 探测,返回 PONG 即健康。
把这个文件保存为 docker-compose.yml,在目录下运行:
docker compose up -d
然后用 docker compose ps 观察,你会看到 db 和 cache 先进入 healthy,随后 web 才被创建。
几种常见数据库的健康检查写法
不同镜像自带的探测工具不同,这里列出常用写法,方便直接抄:
MySQL / MariaDB:
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-p${MYSQL_ROOT_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 5
注意 mysqladmin ping 在服务「活着但还没 ready」时可能也返回成功,更严格的做法是再加一个能真正执行查询的探测命令(例如 mysql -e "SELECT 1")。
MongoDB:
healthcheck:
test: ["CMD", "mongosh", "–quiet", "–eval", "db.adminCommand('ping').ok"]
interval: 10s
timeout: 5s
retries: 5
通用 HTTP 服务: 若镜像没有自带探测工具,可以安装 curl/wget 后探测本机端口:
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/healthz || exit 1"]
interval: 10s
timeout: 5s
retries: 5
start_period: 15s
-f 让 curl 在 HTTP 状态码大于等于 400 时返回非零退出码,正确触发失败标记。
几个容易踩的坑
1. 自定义 service 必须在依赖它之前定义 healthcheck。 condition: service_healthy 依赖目标服务声明了 healthcheck,否则 Compose 会直接报错而不是静默跳过。
2. healthcheck 是「进程内反复执行」的命令。 不要在 test 里放有副作用的命令(比如写文件、发消息),它会在 interval 周期内反复运行,日志噪音和副作用都会累积。
3. start_period 要够长。 应用冷启动慢(比如 JVM、模型加载)时,如果 start_period 太短,容器会被过早判为 unhealthy。不过要注意:即使 db 被判为 unhealthy 后,它的进程仍在运行,web 是否能启动取决于 condition 是否被满足——有些场景下与其死等,不如给足 retries 和 start_period。
4. 一个容器只做一件事。 如果一个容器既跑数据库又跑缓存,健康检查只能反映「这个容器」整体是否健康,无法区分内部哪个服务挂了。拆成多个 service 才能分别监测。
小结
- depends_on 默认只等容器「启动」,不保证服务就绪,要用 condition: service_healthy 精确等待。
- healthcheck 用一段探测命令定义「健康」,参数里 start_period、interval、retries 决定判定的快慢和容错度。
- 针对 PostgreSQL、MySQL、Redis、MongoDB 各有现成的探测命令,HTTP 服务用 curl 探 /healthz 端点。
- 把本文的 docker-compose.yml 拷到本地 docker compose up -d 跑一遍,观察 docker compose ps 里各容器状态的变化顺序,是最快的理解方式。
延伸阅读:Docker 官方文档中 healthcheck 参考 与 Compose depends_on 章节。




