跳到主要内容

技术教程

Docker 显示 running,为什么仍是 unhealthy?拆开进程与探针证据

验证与风险信息

适用环境
Docker 24.0.7 / Compose 2.40.0 / Nginx 1.28.3 Alpine
验证耗时
约 10 分钟
风险与回滚
docker inspect 和容器内只读请求为低风险;探针修改需保留旧镜像/Compose,并避免昂贵或有写副作用的检查
最后复核
2026-08-06
验证证据
同一 PID 下 Nginx 页面可访问但缺少就绪标记,Docker 为 running=true/health=unhealthy;创建预期标记后不重启即转为 healthy。

Docker 显示容器是 Up,并不代表应用已经准备好接收流量;显示 unhealthy 也不等于 PID 1 已退出。运行状态描述主进程,健康状态来自镜像或 Compose 中定义的独立检查命令。把两者混为一谈,常见结果是误杀仍在提供部分能力的进程,或把尚未就绪的实例加入负载均衡。

适用范围与核心结论

本文适用于 Docker 或 Compose 服务显示 running/unhealthy、编排平台不分发流量或依赖服务启动失败。验证环境使用 Docker 24.0.7、Compose 2.40.0 与 Nginx 1.28.3 Alpine。

第一步必须分别证明:PID 1 是否运行、健康检查执行了什么、最近一次检查为何失败、应用端点是否仍响应。不要先删除健康检查或无限增加重试次数。

第一步:同时读取运行状态与健康状态

docker inspect --format '
status={{.State.Status}}
running={{.State.Running}}
exit={{.State.ExitCode}}
health={{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}
pid={{.State.Pid}}' app

running=true 说明主进程仍在;health=unhealthy 说明健康命令连续失败达到重试阈值。若没有 State.Health,容器没有配置 Docker HEALTHCHECK,不能把 Up 当作健康证明。

第二步:读取实际健康命令和失败输出

docker inspect --format '{{json .Config.Healthcheck}}' app
docker inspect --format '{{json .State.Health.Log}}' app

日志中的 ExitCodeOutput、开始和结束时间比面板上的红色状态更重要。常见失败包括检查命令不存在、URL 或端口写错、TLS/认证要求变化、依赖尚未就绪、只读文件系统阻止标记创建,以及检查超时低于正常冷启动时间。

第三步:在同一容器内重放检查

使用镜像里真实存在的工具执行健康命令,保留退出码:

docker exec app sh -c 'wget -qO- http://127.0.0.1/health; echo exit=$?'
docker exec app sh -c 'test -f /run/app/ready; echo exit=$?'

从宿主机访问映射端口只能证明外部路径;健康检查通常在容器网络命名空间内运行。反过来,容器内检查成功也不能替代负载均衡器的端到端检查,因此两条路径都要记录。

判断是错误探针还是应用未就绪

如果业务端点正常而健康命令引用了不存在的二进制或旧路径,应修复镜像/探针。如果进程存活但关键依赖、迁移或配置尚未完成,unhealthy 可能是正确保护信号;此时应修复就绪条件,而不是关闭探针。

健康端点应快速、有界,检查实例提供请求所必需的状态。把所有外部依赖都串行探测可能导致级联不健康;完全不检查关键依赖又会过早接流。需要根据流量切换和故障隔离目标定义条件。

最小修复

确认原因后只修改对应项:补齐检查工具、修正端口/路径、让应用在初始化完成后设置明确的就绪状态,或根据实测冷启动时间调整 start_period。修改 Dockerfile 或 Compose 后先在隔离环境验证:

docker compose config
docker compose up -d --wait
docker compose ps

不要只把 retries 改得很大;这会延迟真实故障被发现。也不要让健康检查执行昂贵查询或产生写入副作用。

复核与回滚

复核时确认同一个容器从 starting 转为 healthy,最近健康日志退出码为 0,外部请求正常,并观察超过一个检查周期。若修改后仍失败,恢复原镜像或 Compose 文件,再按变更前命令重建;保留失败容器日志和 inspect 输出用于复盘。

V2CE 复现实验记录

隔离实验启动 Nginx,主进程和默认 HTTP 页面正常,但健康检查额外要求 /tmp/ready。标记不存在时,Docker 记录 status=running running=true health=unhealthy,同一容器内 HTTP 页面仍返回 Nginx 欢迎页。创建预期的就绪标记后,没有重启进程,PID 保持不变,状态转为 health=healthy

这证明运行状态、端点响应和探针就绪条件是三个不同证据。原始 inspect、健康日志与 Compose 状态保存在 labs/docker-healthcheck-unhealthy/evidence

参考资料