处理容器反复退出问题时,你可能会先想到调整重启策略。在 Compose 文件里,最常用的两个值是 always 和 on-failure。下面按我平时排查的顺序,把需要注意的地方记录下来。
在 Compose 文件里改容器重启策略,常见写法是在服务下加一行 restart: always。但在动手前,得先把一个前提说清楚:这个字段处理的是容器进程退出后的动作,不是容器内应用的健康检查。换个说法,就算你写了 restart: always,应用卡死或端口没响应,只要主进程没退出,容器也不会被重启。
先分清要处理的是退出还是失联
重启策略解决的是“进程结束了,要不要再拉起来”的问题。如果你观察到容器处于 Exited 状态,那是重启策略该管的;如果容器还是 Up 状态,但接口超时、报错,那优先排查应用日志和依赖资源,而不是改重启策略。有些排查场景里,容器反复重启会导致日志被覆盖,反而掩盖了原始错误,所以动手改配置之前,先确认现象。可以先用 docker ps -a 看退出码,再用 docker logs 看最后几行日志,判断是退出还是失联。
always 和 on-failure 各自适合什么场景
always 适用面更广,适合 web 服务、API 这类需要持续在线的容器。它会在容器退出后自动重新拉起,但如果有人手动执行 docker stop,容器不会因为 restart 策略被自动启动。这一点容易和“总是会自动恢复”搞混。对于常驻服务来说,手动停止通常是运维操作,Docker 不会违背这个意图。
这里必须补一句:always 只在容器退出时触发,手动执行 docker stop 不会让它重新拉起来。on-failure 则只在主进程返回非零退出码时重启,适合那些因临时资源不足或外部依赖抖动而退出的任务。如果你希望服务在崩溃后自动恢复,但又不希望在手动停掉后反复拉起,on-failure 反而更接近这种期望,不过它要求进程正确设置退出码,否则场景容易判断错。举个例子:一个脚本式主进程,正常结束返回 0,异常时返回 1。在 on-failure 策略下,返回 0 的退出不会触发重启,返回 1 的退出会触发重启。这个逻辑用 docker run 验证很直观,但写进 Compose 时要注意没有次数限制。
Compose 里写 restart 的常见坑
先看一个正常可用的配置片段:
services:
web:
image: nginx:1.25-alpine
restart: always这里的 restart 必须写在服务节点下,和 image、ports 同级。很多新手会把它误放到顶层,导致 Compose 解析报错。Compose 文件里的 restart 值只能写 no、always、on-failure、unless-stopped 这些策略名,不能带数字参数。Compose 文件里的 restart 不支持 on-failure:3 这种带次数上限的写法,docker run 支持。如果要限制重试次数,需要在启动入口脚本里自己控制,或者借助外部调度。这一点经常被误用,需要特别留意。如果你写的是 restart: on-failure:3,Compose 在解析时要么报错,要么静默忽略后面部分,行为就变得不可预期。所以请按照 Compose 的语法要求,只写策略名。
改完后看这几个信号
修改了 Compose 文件后,直接用 docker-compose up -d 往往不会重建已存在的容器,因为 Compose 可能认为配置没有变化。要应用新的 restart 策略,需要重新创建容器,通常执行 docker-compose up -d 时加上 --force-recreate,或者先 docker-compose down 再 up -d。注意 down 会删掉容器和网络,数据卷除非加了 -v 否则保留。更稳妥的做法是:改完配置后,运行 docker-compose up -d,然后检查容器是否被重建。如果只改 restart 字段,Compose 可能会提示 Recreating container,但也可能因为配置比较逻辑而跳过,所以用 --force-recreate 更明确。
改完配置后,不要只看 docker ps,更准确的检查是用 docker inspect 看 RestartPolicy。另外,restart 策略变更不会自动应用到已存在的容器,需要重新执行 docker-compose up -d 让 Compose 重建配置。检查命令如下:
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' <容器名>
docker inspect -f '{{.RestartCount}}' <容器名>第一行会输出当前生效的重启策略名,比如 always 或 on-failure。第二行会显示容器自创建以来的重启次数,注意这个值记录的是被 Docker 守护进程重启的次数,不包括手动 start 的次数。看到这两个字段,再结合容器状态和日志,才能确认重启策略是否按预期工作。另外,如果容器被反复拉起,docker ps 里的 STATUS 会显示如 Up X seconds,留意这个现象可以更快发现重启风暴。
别忘了边界:重启不等于恢复
restart: always 只负责让容器进程重新跑起来,不对成功与否负责。如果容器内的应用每次启动都因缺配置或连不上数据库而退出,restart 策略会让它无限循环,形成“重启风暴”。这时候查看 docker logs 比看 docker ps 更有价值。建议把 restart 策略和 healthcheck 配合使用,让容器在健康检查失败时也能被标记为 unhealthy,方便外部监控介入。要注意的是,healthcheck 只影响状态,不会触发重启。如果你需要依赖顺序,比如等数据库就绪后再启动应用,可以在 Compose 中用 depends_on 配合 condition: service_healthy,但 Compose 版本对语法的支持有差异,需要结合当前环境确认。
回滚也很简单:把 restart 改回 no,再执行一次 docker-compose up -d --force-recreate。改动之前建议先记下当前的配置,特别是有多个服务时,不要一次性全部改掉,先在一台测试环境上跑通再批量应用。重启策略不是万能药,有些容器就是应该退出,人工介入反而能暴露真实问题。