健康检查失败但网页能打开,说明问题出在配置或执行环境:curl可能缺失、端口不匹配、--start-period过短、健康接口返回非200码未被正确捕获、网络隔离干扰或日志静默导致误判。

健康检查失败但网页能打开,说明容器进程和网络层基本正常,问题出在健康检查自身的配置或执行环境上——不是服务挂了,而是 Docker 没“看懂”它还活着。
检查 curl 是否在容器内可用且路径正确
Docker 健康检查命令运行在容器的命名空间里,不继承宿主机环境。很多镜像(比如 alpine、scratch)默认不带 curl,或者只装了 wget。
- 先确认容器内是否存在
curl:docker exec -it <container> which curl</container> - 如果返回空,改用
wget:把CMD curl -f http://localhost:8080/health || exit 1换成CMD wget --spider -q http://localhost:8080/health || exit 1 - 注意端口是否匹配:应用实际监听的是
:8080,但健康检查写成了:80,或反过来;检查docker exec -it <container> netstat -tuln | grep LISTEN</container>
--start-period 太短导致启动未完成就被判失败
健康检查在容器启动后立刻开始计时,但你的应用可能要花 10–20 秒才真正 bind 端口并响应请求。此时第一次检查必然失败,若 --start-period 小于这个时间,失败会被计入重试计数,直接触发 unhealthy。
- 查看当前配置:
docker inspect --format='{{.Healthcheck.StartPeriod}}' <container></container> - 对 Spring Boot、Node.js 等慢启动服务,建议设为
--start-period=60s或更长 - 这个参数只能在
HEALTHCHECK指令中定义,运行时通过docker run --health-start-period覆盖
健康接口返回非 200 状态码但没被 curl -f 捕获
curl -f 只对 400+ 状态码报错退出,但有些框架(如 Express、Flask)健康端点在依赖未就绪时返回 503 或自定义 20x(比如 204),而 curl -f 不认为这是错误。
- 手动测试健康接口真实行为:
docker exec -it <container> curl -v http://localhost:8080/health</container> - 如果返回 503,
curl -f会退出 22(不是 1),Docker 视为“不确定”,不更新状态;需显式处理:CMD curl -f http://localhost:8080/health || [ $? = 22 ] && exit 1 || exit 1 - 更稳妥做法:用简单 HTTP 状态判断脚本替代单条
curl,避免依赖退出码语义模糊
Docker Desktop 的 Hyper-V / WSL2 网络隔离干扰本地请求
在 Windows 上用 Docker Desktop(尤其 WSL2 后端)时,localhost 在健康检查命令中指向的是容器自己的 loopback,不是宿主机或 WSL2 的网络栈。但你的应用如果只监听 127.0.0.1,而健康检查又在容器内发请求,这本身没问题;可一旦你误配了监听地址(比如只监听 0.0.0.0 却没开权限),或用了 Windows 主机网络模式,就容易混淆。
- 验证监听地址:
docker exec -it <container> ss -tlnp | grep :8080</container>,确认是*:8080或:::8080,而非127.0.0.1:8080 - 避免在健康检查中使用
host.docker.internal—— 它在某些 Docker Desktop 版本下解析不稳定,优先用localhost - 如果必须跨网络访问(比如调用同 compose 网络的其他服务),确保目标服务已就绪,否则健康检查失败是设计使然,不是 bug
最常被忽略的一点:健康检查命令不输出任何日志到容器 stdout/stderr,失败时只能靠 docker inspect 查 .State.Health.Log。很多人只看 docker logs 就以为检查根本没跑——其实它跑了,只是静默失败了。











