Administrator
Administrator
发布于 2026-10-28 / 0 阅读
0
0

Docker 故障排查(09):容器反复 Restarting:重启策略掩盖了应用配置错误

适用:使用 systemd 的 Linux 服务器、常规 rootful Docker Engine;Compose 示例使用 docker compose V2。本文为教学故障场景,错误文本和结果为示意,具体输出以现场为准。

一、遭遇情况:刚启动就重启,日志不断重复

发布后 docker ps 显示 Restarting。偶尔能进入容器,几秒后连接又断开。日志里重复出现同一条启动错误,看上去像“Docker 一直不稳定”。

本篇案例是 Nginx 配置文件里写入一个不存在的指令,进程启动即失败;容器又配置了失败重启策略,所以同一错误不断发生。重启策略在执行既定动作,真正失败的是应用初始化。

二、原因分析:重启是结果,不是根因

应用异常退出、OOM、依赖未就绪、配置错误,都可能触发重启策略。仅看到 Restarting,还不能判断是哪一种。需要把 Docker 状态与应用日志放到同一条时间线上。

另外,Compose 或外部部署平台也可能重建容器。容器 ID 一直变化与同一容器的 RestartCount 增加不同,排查时先确认是哪种行为。

重启循环的定位与恢复步骤

三、排查思路:收集最小且有用的信息

假设容器叫 web:

docker inspect web --format 'Status={{.State.Status}} Exit={{.State.ExitCode}} OOM={{.State.OOMKilled}} RestartCount={{.RestartCount}}'
docker inspect web --format '{{json .HostConfig.RestartPolicy}}'
docker inspect web --format 'Started={{.State.StartedAt}} Finished={{.State.FinishedAt}}'
docker logs --tail 150 --timestamps web

日志不要一开始就无限 -f,快速重启可能刷掉你的观察重点。先读取有边界的最近日志,找到首次失败相关的具体信息,例如配置解析错误、缺少环境变量或依赖连接失败。

unhealthy 与“主进程退出”不是一回事。普通 Docker Engine 不会仅因为健康检查变成 unhealthy,就必然按重启策略重启容器;如果发生这种情况,还要查外部自动化控制。

四、实践:用有限重试复现配置错误

在同一个 Bash 会话中执行,下列临时目录只保存实验 Nginx 配置:

lab_dir=$(mktemp -d /tmp/dt09.XXXXXX)
printf '%s\n' 'events {}' 'http { broken_directive on; }' > "$lab_dir/nginx.conf"
docker run -d --name dt09-loop --restart=on-failure:3 --mount "type=bind,src=$lab_dir/nginx.conf,dst=/etc/nginx/nginx.conf,readonly" nginx:stable-alpine
docker logs --tail 60 --timestamps dt09-loop
docker inspect dt09-loop --format 'Status={{.State.Status}} Restarts={{.RestartCount}} Exit={{.State.ExitCode}}'

日志预期指向未知指令 broken_directive。因为设置了最多 3 次失败重试,观察时可能是 Restarting,也可能已经达到限制后 Exited;不应要求读者恰好截到某一瞬间。

实验不发布任何宿主机端口,不会接管线上 Web 入口。

五、修复实践:先预检,再重新启用服务

对这个明确的实验容器,关闭重启策略并停止,减少诊断噪声:

docker update --restart=no dt09-loop
docker stop dt09-loop

真实业务操作前先记录原策略,并确认变更范围。若由 Compose 管理,最终修复还要写回 Compose 文件,否则后续部署可能恢复旧配置。

将实验配置修正为一个最小可用服务。本例直接写入原文件,保持文件挂载目标清楚:

cat > "$lab_dir/nginx.conf" <<'EOF'
events {}
http {
    server {
        listen 80;
        location / { return 200 "ok\n"; }
    }
}
EOF

docker run --rm --mount "type=bind,src=$lab_dir/nginx.conf,dst=/etc/nginx/nginx.conf,readonly" nginx:stable-alpine nginx -t

配置预检通过后,再启动原实验容器:

docker start dt09-loop
docker logs --tail 30 dt09-loop
docker exec dt09-loop wget -qO- http://127.0.0.1/
docker inspect dt09-loop --format 'Status={{.State.Status}} Restarts={{.RestartCount}}'

预期得到 ok,容器保持运行。这里的检查证明应用实际监听并能响应,而不只是“配置语法通过”。

确认稳定后,可将实验的失败重试策略恢复为 docker update --restart=on-failure:3 dt09-loop。生产容器应恢复原先经过评估的策略,而不是统一套用本示例。

六、验证与总结:重启次数要停止增长

隔一段时间再次检查状态和重启次数,确认没有继续增长;核对应用请求、相关依赖和最近日志。不要把一次 docker start 返回容器名视为修复完成。

如果改完仍反复失败,回到新的错误信息继续处理,而不是不断提高重试次数。配置发布应增加应用自身的预检步骤,能在上线前发现的语法错误,就不要留给重启策略反复放大。

本篇的排查闭环是:确认谁在重启 → 捕获具体启动错误 → 暂停噪声 → 验证配置 → 启动并请求服务 → 观察稳定性。重启策略负责恢复尝试,不负责修复配置。

参考资料


上一篇 · 系列目录 · 下一篇


评论