1. 概述

Docker Compose 是一个用于部署、运行和维护多容器应用的工具。通过 compose 文件,我们可以定义多个协同工作的容器来构建完整的应用系统。Docker Compose 的一个实用功能是支持容器间的依赖关系定义,从而控制容器的启动顺序。

在本教程中,我们将学习 Docker Compose 中的服务依赖机制,并演示如何在包含 MySQL 容器的环境中使用它

2. 容器的健康检查与就绪状态

健康检查(Health Check)是一种通过执行特定命令并检查响应结果,来判断系统是否正常运行的机制。具体来说,外部系统(称为 prober)会执行命令并根据返回结果判断系统是否健康。例如,对一个网站的健康检查可能包括发送一个 GET 请求,并期望返回 200 状态码。

在 Docker 中,定义健康检查可以为容器的生命周期增加一个新的维度。除了常见的 Created、Started、Stopped 状态之外,Docker 还能识别容器是否处于 HealthyUnhealthy 状态。这种状态可用于更精细的控制,比如只在容器状态为 Healthy 时才路由流量到该容器。这对于启动时间较长的容器(如数据库)尤其有用。

2.1 在 Dockerfile 中定义健康检查

我们可以在构建镜像时通过 Dockerfile 的 HEALTHCHECK 指令来定义健康检查:

$ cat Dockerfile
FROM mysql:5.7

ENV MYSQL_ROOT_PASSWORD=example

HEALTHCHECK --interval=10s --timeout=5s --retries=5 CMD mysqladmin ping -h localhost || exit 1

2.2 在 docker-compose.yaml 中定义健康检查

也可以在 docker-compose.yaml 文件中通过 healthcheck 字段来指定健康检查命令:

services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: example
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

上述配置表示每 10 秒执行一次健康检查命令,每次执行等待 5 秒,连续失败 5 次后标记为不健康。


3. Docker Compose 服务依赖关系

对于多容器应用,容器之间可能存在依赖关系,要求它们按照特定顺序启动。例如,Web 应用容器可能在启动时执行数据库迁移脚本,这就要求数据库容器必须先就绪。

Docker Compose 提供了 depends_on 字段来定义服务之间的依赖关系。例如,如果 serviceA 需要在 serviceB 启动后才启动,可以在 serviceA 的配置中添加:

services:
  serviceA:
    image: alpine:latest
    depends_on:
      - serviceB

  serviceB:
    image: alpine:latest

启动时,Docker 会先创建并运行 serviceB,等其状态变为 Running 后再启动 serviceA。

⚠️ 注意:depends_on 控制的是容器的启动顺序,而不是就绪状态

3.1 service_started

默认情况下,depends_on 的条件是 service_started,即只要目标服务容器状态变为 Started,就认为依赖满足。

3.2 service_ready

如果使用 service_ready 条件,则 Docker 会等待目标服务不仅 Started,还要达到 Healthy 状态。这要求目标服务必须定义了 healthcheck,否则 Docker 无法判断其是否就绪。

depends_on:
  db:
    condition: service_healthy

3.3 service_completed_successfully

service_completed_successfully 表示目标服务必须正常执行完毕(退出码为 0)。这通常用于前置初始化容器(如数据迁移容器),这类容器通常是短暂运行的。


4. MySQL 容器的就绪判断

MySQL 是一个广泛使用的开源关系型数据库管理系统。我们通常认为 MySQL 就绪的标准是它开始接受外部连接请求并能执行 SQL 命令。

✅ **判断 MySQL 是否就绪的一个简单方式是执行一个无副作用的 SQL 命令,如 SHOW DATABASES;**:

$ mysql -u root -pexample --execute 'SHOW DATABASES;'
+--------------------+
| Database           |
...

如果执行失败,通常表示数据库尚未就绪,此时健康检查失败;否则认为容器已就绪。

4.1 示例:容器启动依赖配置

我们来配置一个包含 web 和 db 两个服务的环境,其中 web 依赖 db 的就绪状态:

services:
  web:
    image: alpine:latest
    depends_on:
      db:
        condition: service_healthy
  db:
    image: mysql:5.7
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: root
    healthcheck:
      test: ["CMD", "mysql", "-u", "root", "-proot", "--execute", "SHOW DATABASES;"]
      interval: 3s
      retries: 5
      timeout: 5s

关键点:

  • web 服务通过 depends_on 依赖 db,并指定 service_healthy 条件。
  • db 定义了健康检查命令,确保只有当 MySQL 真正就绪后,web 才会启动。

4.2 查看健康检查效果

运行以下命令启动服务:

$ docker compose up -d

输出如下:

[+] Running 1/3
 ✔ Network mysql-complete_default  Created                                             0.0s
 ✔ Container mysql-complete-db-1   Created                                             0.1s
 ✔ Container mysql-complete-web-1  Created                                             0.1s

随后你会看到 db 容器先进入 Running 状态,然后进入 Waiting,等待健康检查通过:

[+] Running 2/3
 ✔ Network mysql-complete_default  Created                                             0.0s
 ⠼ Container mysql-complete-db-1   Waiting                                             2.5s
 ✔ Container mysql-complete-web-1  Created                                             0.1s

一旦 db 容器通过健康检查,状态变为 Healthy,web 容器才会启动:

[+] Running 3/3
 ✔ Network mysql-complete_default  Created                                             0.0s
 ✔ Container mysql-complete-db-1   Healthy                                            10.0s
 ✔ Container mysql-complete-web-1  Started                                            10.3s

5. 总结

本教程中我们学习了:

  • Docker 容器健康检查的作用及配置方式
  • Docker Compose 中服务依赖的使用方法
  • 如何通过 depends_on + healthcheck 实现容器启动顺序控制
  • MySQL 容器就绪判断的最佳实践(如使用 SHOW DATABASES;

使用健康检查和依赖条件组合,可以有效避免容器因依赖服务未就绪而导致的启动失败问题。这是构建健壮容器化应用的重要一步。


原始标题:Wait for MySQL Connection Readiness in Docker Compose