在日常运维中,容器显示为 Up,并不代表其中的应用一定能够正常提供服务。进程可能仍在运行,但应用可能处于初始化、线程阻塞、依赖不可用或接口持续报错等状态。Docker 健康检查通过周期性执行探测命令,为容器补充 starting、healthy、unhealthy 等健康状态,帮助开发和运维人员更准确地判断服务是否可用。🔍
一、Docker 健康检查的工作机制
健康检查通常由镜像中的 HEALTHCHECK 指令,或 Compose 文件中的 healthcheck 配置定义。Docker 会在容器内部执行指定命令,并根据退出码判断结果:退出码为 0 表示检查成功,退出码为 1 表示检查失败,退出码 2 为保留值,不建议使用。详细语法可参考 Dockerfile 官方文档。
容器启动后,健康状态通常先显示为 starting。探测成功后变为 healthy;连续失败达到重试次数后,则变为 unhealthy。需要注意的是,健康状态异常通常不会自动停止或重启容器,它主要用于状态展示、服务依赖控制以及外部监控判断。
二、在 Dockerfile 中配置 HEALTHCHECK
对于需要随镜像统一分发的检查规则,可以直接写入 Dockerfile。下面以 HTTP 服务为例:
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD curl -fsS 来源链接 || exit 1
- --interval:两次常规检查之间的时间间隔。
- --timeout:单次检查允许执行的最长时间,超时按失败处理。
- --start-period:容器启动后的宽限期,适用于初始化较慢的应用。
- --retries:连续失败多少次后,将容器标记为 unhealthy。
如果基础镜像已经包含健康检查,但当前镜像不希望继承,可以使用 HEALTHCHECK NONE 禁用。一个 Dockerfile 中即使出现多条 HEALTHCHECK 指令,最终通常也只有最后一条生效,因此应避免重复定义。
三、在 Docker Compose 中配置健康检查
使用 Compose 部署时,可以针对不同环境覆盖镜像内的检查规则。常见配置如下:
services:
api:
image: example-api:latest
healthcheck:
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8080/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 20s
CMD 数组形式会直接执行命令,不经过 Shell;如果检查逻辑包含管道、变量展开或 || 等操作符,应改用 CMD-SHELL。Compose 的完整字段说明可查看 healthcheck 配置说明。⚙️
多容器应用还可以结合 depends_on 与 condition: service_healthy,让应用服务在数据库等依赖通过健康检查后再启动。不过,这只能控制启动阶段的依赖顺序,不能替代应用自身的重试、断线重连和故障恢复机制。
四、如何查看容器健康状态
排查时首先执行 docker ps,状态列可能显示 Up 2 minutes (healthy) 或 Up 2 minutes (unhealthy)。若需要查看具体检查记录,可执行:
docker inspect --format='{{json .State.Health}}' 容器名称
重点关注检查命令的退出码、输出内容、开始时间和结束时间。也可以直接进入容器,手动执行完全相同的探测命令:
docker exec -it 容器名称 sh
curl -fsS 来源链接
手动执行能够快速确认问题来自应用本身、探测工具、运行用户权限,还是健康检查参数设置。
五、unhealthy 状态的常见原因
1. 镜像中缺少探测工具
精简镜像可能没有安装 curl、wget、bash 或数据库客户端。此时应用本身正常,但健康检查会因为“命令不存在”而失败。应在构建阶段安装必要工具,或编写体积更小的专用探测程序。对于无 Shell 的镜像,应采用数组形式直接执行可用程序。
2. 检查地址或端口填写错误
健康检查在容器内部执行,因此 127.0.0.1 指向当前容器,而不是宿主机或其他服务。检查当前容器内的应用时,应使用容器内部监听端口,而不是宿主机映射端口;检查其他 Compose 服务时,则应使用对应的服务名和容器端口。
3. 应用只监听特定网络地址
如果应用仅监听某个容器 IP,使用 127.0.0.1 可能无法访问;反之,如果服务只绑定本地回环地址,其他容器也无法连接。可以通过 ss -lntp 或应用日志确认实际监听地址,并检查配置是否符合访问场景。
4. 启动宽限期设置太短
数据库迁移、缓存预热或配置加载可能需要较长时间。若 start_period 太短,容器会在初始化完成前被标记为异常。建议结合真实启动日志设置宽限期,不要单纯通过增加 retries 掩盖启动问题。⏳
5. 健康接口设计过重
健康接口如果同时查询多个外部系统,任何一个依赖短暂波动都可能导致容器被判定为 unhealthy。基础存活检查应尽量轻量,只验证进程和核心服务是否可响应;依赖检查可以放在更完整的就绪接口或监控系统中,并设置明确超时。
六、推荐的异常排查顺序
- 使用 docker ps 确认异常容器及当前状态。
- 使用 docker inspect 查看最近一次健康检查的退出码与输出。
- 通过 docker logs 检查应用启动、端口监听和依赖连接日志。
- 进入容器手动运行探测命令,确认工具、路径、权限和返回结果。
- 核对容器内部端口、服务监听地址、DNS 解析及网络连通性。
- 检查 interval、timeout、start_period 和 retries 是否符合应用启动特征。
- 修改配置后重新构建或创建容器,再持续观察多个检查周期。
七、配置健康检查的实用建议
- 为 Web 服务提供独立且轻量的 /health 或 /healthz 接口。
- 为检查命令设置短超时,避免探测进程长时间占用资源。
- 不要在探测脚本中执行数据写入、状态修改或高开销查询。
- 不要把密钥、密码等敏感信息直接输出到健康检查结果中。
- 将 unhealthy 状态接入日志告警和监控系统,而不是只依赖人工查看。
- 健康检查应验证实际服务能力,不能仅检查某个进程是否存在。
总结
Docker 健康检查弥补了“容器正在运行”与“应用真正可用”之间的判断空白。合理设置探测命令、启动宽限期、超时时间和失败重试次数,可以更早发现服务无响应、初始化失败以及依赖异常等问题。遇到 unhealthy 状态时,应从检查记录、应用日志、容器内手动测试、端口监听和网络配置逐层定位,而不是立即重启容器。只有让检查逻辑保持轻量、准确且可观测,健康状态才能真正成为稳定运行的有效依据。✅