Docker 容器一直 Restarting 怎么办?退出码、健康检查与重启策略排查

2026-09-20

直接答案:容器持续 Restarting 时,先查看 docker inspect 的 State、ExitCode、OOMKilled、Error、StartedAt/FinishedAt 和 RestartCount,再读取 docker logs --since。常见根因是入口命令立即退出、配置/秘密缺失、端口或文件权限错误、依赖未就绪、健康检查设计不当、内存被 cgroup OOM 杀死,或 always/unless-stopped 策略把一次失败变成循环。不要先反复 docker restart;它会缩短取证窗口并持续冲击依赖。

本文适合单容器或 Compose 服务反复启动退出。若状态是 Up (unhealthy) 而非 Restarting,Docker 本身不会仅因普通 healthcheck 失败自动重启单个容器,需检查编排器或外部监控策略。

本文按 2026 年 9 月可查的官方文档核对。示例命令以只读取证为主;凡涉及删除、覆盖、重启、权限、网络或数据库参数的动作,都应先确认操作对象、备份、维护窗口和带外恢复入口。不同发行版与软件版本的路径可能不同,必须先查询实际配置再改。

先用证据判断故障在哪一层

现象或检查点 它能说明什么 直接证据 不要误判为
ExitCode非零且日志有应用异常 应用/配置主动退出 最后日志、入口命令与配置 Docker守护进程故障
OOMKilled=true或退出137 内存限制或宿主OOM可能参与 inspect、内核日志与cgroup指标 一定是程序崩溃
退出0仍重启 主进程正常结束但策略要求常驻 Entrypoint/Cmd与restart policy 容器健康
挂载后才失败 文件、权限、架构或配置被覆盖 Mounts与容器内实际路径 镜像损坏
依赖稍后可用仍循环 启动探针、等待或重试策略不足 时间线、DNS与依赖日志 只需增加depends_on

这张表的用法不是“看到一个关键词就改一个参数”,而是把客户端、DNS或边缘、Web 代理、应用、数据层依次分开。至少保留故障时间、完整 URL、状态码、关键响应头、同一时刻的服务日志和一份正常样本。只有时间能够对齐,才能判断两个现象是否属于同一次故障。

六步排查与修复顺序

1. 保存状态并降低循环冲击

记录容器 ID、镜像 digest、创建时间、重启次数和当前策略。若循环正在压垮数据库或日志,可在维护判断后停止单个容器;先保存 inspect 和最近日志,不要删除容器。

2. 读取退出码与上一轮日志

ExitCode、OOMKilled 和 Error 提供第一层方向;日志要包含上一轮短暂运行窗口。若应用把日志写文件而非 stdout/stderr,检查挂载目录和权限,但不要进入循环容器里手工修改后丢失可复现性。

3. 核对镜像入口与运行参数

查看 Entrypoint、Cmd、WorkingDir、User、Env、Mounts、端口和架构,比较 Compose 解析后的配置。空的环境变量、被 bind mount 覆盖的目录、CRLF 脚本或无执行权限都可能让 PID 1 立即退出。

4. 区分内存、健康和依赖

退出 137 不总是 OOM,结合 inspect 与内核日志;依赖启动顺序不等于可用,应用应重试。healthcheck 要检查真实服务但不能过重,start_period 应覆盖正常初始化时间。

5. 修复根因再设置重启策略

在临时无自动重启的隔离容器中复现入口命令,修配置、权限或依赖,并固定镜像版本。选择 on-failure、always 或 unless-stopped 时明确预期;不要用 host systemd 和 Docker policy 双重管理同一容器。

6. 验证启动、运行与重启

新容器先保持稳定超过 Docker 监控窗口,检查健康、日志和资源,再主动重启 Docker/主机验证策略。应用还要经过一次真实读写和依赖短暂不可用测试,确认没有重连风暴或数据损坏。

可复制的只读取证命令

以下命令中的域名、服务名、路径和时间范围要替换为自己的真实值。先执行查询命令并保存原始输出;不要把“命令能运行”误当成“业务已恢复”。

text docker ps -a --filter 'status=restarting' --no-trunc docker inspect --format '{{json .State}}' your-container docker inspect --format '{{json .HostConfig.RestartPolicy}}' your-container docker logs --timestamps --since 20m your-container journalctl -k --since '30 minutes ago' | grep -Ei 'oom|killed process|memory cgroup'

  • docker ps -a --filter 'status=restarting' --no-trunc:列出重启中的容器及完整命令摘要
  • docker inspect --format '{{json .State}}' your-container:查看退出码、OOMKilled、错误和时间
  • docker inspect --format '{{json .HostConfig.RestartPolicy}}' your-container:查看自动重启策略
  • docker logs --timestamps --since 20m your-container:保存最近多轮stdout/stderr日志
  • journalctl -k --since '30 minutes ago' | grep -Ei 'oom|killed process|memory cgroup':查宿主内核是否记录OOM或cgroup杀进程

如命令输出包含公网 IP、用户名、Cookie、令牌、数据库连接串或客户数据,分享工单前先脱敏。对比时保留时间戳、时区和命令本身,否则后来无法复现同一观察条件。

五个最容易扩大故障的误区

  • 不断 docker restart:只重复失败并覆盖日志,不会修复入口或依赖。
  • 立刻 docker rm:会丢失 inspect、挂载和退出状态证据。
  • 看到137就断言OOM:137表示被SIGKILL,需结合 OOMKilled 和内核日志。
  • depends_on当成就绪检查:它不能保证数据库已可接受业务查询,应用仍需退避重试。
  • 同时设置Docker和systemd重启:两个管理器会制造竞态、重复实例和不可预测停止行为。

变更、回滚与数据边界

保留旧容器、镜像 digest、Compose 配置和 volume 快照。新镜像或配置失败时,停止新容器并以原 digest/配置启动旧版本;数据库模式若已不可逆迁移,不能只切回镜像。修改 restart policy 后若产生循环,先恢复 no 或上一策略止损,再处理根因;不要删除 volumes 作为回滚步骤。

如果只能先恢复服务、还不能确认根因,应把措施标记为“临时缓解”,例如限流、摘除单节点、切回上一配置或临时绕过一层代理。恢复后仍要在受控环境复现原因,并验证同类故障不会在下一次重启、发布或高峰重新出现。

统一验收清单

  • [ ] State、ExitCode、OOMKilled和RestartCount已保存
  • [ ] 上一轮失败日志完整
  • [ ] Entrypoint、Cmd、User、Env和Mounts已核对
  • [ ] 依赖就绪与启动顺序已区分
  • [ ] 自动重启策略与业务目标一致
  • [ ] 容器持续稳定且健康检查通过
  • [ ] Docker/主机重启后按预期恢复
  • [ ] 真实读写、日志和资源无异常

验收必须同时覆盖机器内部和真实外部路径。首页返回 200 不能证明上传、长连接、登录、数据库写入或移动端都正常;同样,控制台显示“运行中”也不能证明应用已能处理请求。至少选一个正常请求、一个边界请求和一个失败请求,记录预期与实际结果。

相关内容与隔离测试入口

建议先阅读Docker Compose生产部署、云服务器内存不足、服务器日志保留,把本问题放回完整请求链路。若生产环境不适合直接试验,可查看美国弹性云服务器的当前规格与库存,搭建与生产隔离的复现环境;测试机不能替代生产备份,价格、库存和可选配置以实时产品页为准。

常见问题

为什么日志是空的?

进程可能在日志系统初始化前退出、写入文件、权限失败或入口未执行。查看 State.Error、入口和挂载。

退出码0为什么仍一直重启?

主进程正常结束,但 restart policy 要求容器常驻。检查命令是否应前台运行,以及任务型容器是否用了不合适策略。

healthcheck失败会自动重启吗?

普通Docker容器只会标记 unhealthy,不一定自动重启;Swarm/Kubernetes或外部监控可能另有行为。

使用 --restart always 能提高可用性吗?

只能在进程退出后再次启动,不能修坏配置、数据或依赖,甚至可能形成重启风暴。

容器重建会丢数据吗?

写在可写层的数据会随容器删除而丢失;明确的 volume/bind mount 仍需备份和恢复验证。

官方参考资料

最近更新