容器起不来?Docker 排错手册

容器 Exited、Restarting 无限循环、端口不通、权限报错、磁盘爆满……小够整理 Docker 最常见的 10 类故障和排查顺序:先看状态,再看日志,最后看配置,八成问题半小时内定位。

我是小够,只推够用的 VPS。

Docker 出问题时别慌,大多数故障都逃不过固定套路。今天给一份排错手册,按顺序查,八成问题半小时内定位。建议收藏,真出事了翻出来一步一步走。

通用排查顺序

记住这个顺序,别跳步:状态 → 日志 → 配置 → 资源 → 系统。新手最常见的错误是反着来:先怀疑系统、再怀疑 Docker,最后发现是 compose 文件少了个空格。

故障一:容器反复 Restarting

docker ps -a   # 看退出码
docker logs 容器名 --tail 50

退出码速查:0 是正常退出(比如一次性任务跑完了,别当故障);1 是应用报错,看日志;2 是参数错误,检查 CMD/ENTRYPOINT;125 是 Docker 守护进程的错,比如镜像不存在;126 是命令不可执行,权限问题;127 是命令找不到,路径或拼写错;137 是被 OOM kill(内存爆了),去加内存限制那篇找对策;139 是段错误,多半是镜像和 CPU 架构不匹配(ARM 机器拉了 AMD64 镜像);143 是被 SIGTERM 终止,可能是有人停了它,也可能是健康检查失败被干掉。

故障二:端口映射了但访问不通

三步查:

docker ps  # PORTS 列确认映射存在,比如 0.0.0.0:8080->80/tcp
curl localhost:8080  # 本机先试,通了就是防火墙/安全组问题
sudo ufw status  # 看主机防火墙放行了没

本机通、外面不通:99% 是云商家安全组或 UFW 没放行。两个都要查:商家控制台的安全组 + 机器里的 UFW,缺一个都不通。容器里应用只监听了 127.0.0.1 也有可能,检查应用的绑定地址——Nginx 这类默认监听 0.0.0.0 没事,自己写的服务要注意。

还有个隐蔽的:ports: ["127.0.0.1:8080:80"] 这种写法只监听本机,外面当然访问不到。看 PORTS 列,如果是 127.0.0.1:8080->80 而不是 0.0.0.0:8080->80,就是这个问题。

故障三:compose up 报 network 相关错误

docker network ls
docker compose down
docker compose up -d

网络名冲突多见于:手动建过同名网络、项目目录改名导致旧网络残留。down 清掉重建一般就好。顽固的用 docker network prune 清理(会删所有没在用的网络,确认没重要东西再执行)。

故障四:数据卷权限问题(Permission denied)

容器里写文件报权限不足,经典原因是:主机目录是 root 建的,容器里跑的是非 root 用户。三种解法:

  1. 主机上 chown 目录给容器用户用的 UID:sudo chown -R 1000:1000 ./html。
  2. compose 里加 user: "0:0" 临时用 root 跑(不推荐长期)。
  3. 绑定挂载改用具名数据卷,让 Docker 自己管权限。

先 docker exec 容器名 id 看容器里是哪个用户,再对症下药,别上来就 chmod 777——那是把门拆了,不是开门。777 一时爽,被黑火葬场。

故障五:镜像拉不下来

看本月镜像加速那篇。先确认:docker pull 卡在哪个阶段、镜像多大、tag 对不对。还有个常见坑:磁盘满了,df -h 看一眼,镜像下到一半没地方放了也会报莫名其妙的错,让你以为是网络问题。

故障六:磁盘满了

df -h
docker system df   # 看 Docker 占了多少:镜像、容器、数据卷、构建缓存
docker system prune -a --volumes  # 大扫除,删所有没用的(数据卷也会删,确认备份了再执行)

--volumes 会删数据卷,没备份千万别加。日常用不带 --volumes 的 docker system prune 就够。构建缓存(build cache)经常是隐形大户,docker builder prune 单独清它。日志文件也是大户,du -sh /var/lib/docker/containers/*/*-json.log 看一眼。

故障七:compose 文件 YAML 报错

docker compose config  # 校验并打印解析后的配置,排版错了一目了然

YAML 对缩进敏感,90% 的报错是空格问题。用两个空格缩进,别用 Tab。docker compose config 是免费校验器,改完先跑它。冒号后面没加空格也是经典错误:image:nginx 是错的,image: nginx 才对。还有 Tab 和空格混用,编辑器看着对齐了,解析器不认——把编辑器设成"Tab 转空格"一劳永逸。

故障八:容器之间连不通

服务名当主机名用是 Compose 的默认行为,跨项目不行。检查清单:

  1. 两个服务在同一个 compose 文件(同一个网络)吗?
  2. depends_on 写对了吗?服务名拼错最常见。
  3. 目标服务真的在监听吗?docker exec -it 容器A ping 服务B 实测,别靠猜。ping 通但端口不通,用 nc -zv 服务B 3306 测端口。
  4. 防火墙是不是把容器网段禁了?自定义 iptables 规则有时会误伤 docker0 网段(默认 172.17.0.0/16)。

故障九:容器时间不对

容器默认用 UTC,日志时间戳和你的北京时间差 8 小时,排错时对不上。解法:挂载主机时区文件:

volumes:
  - /etc/localtime:/etc/localtime:ro

或者环境变量 TZ=Asia/Shanghai(看镜像支不支持,MySQL、Nginx 都支持)。小问题,但能省很多"我明明刚操作的日志去哪了"的困惑。数据库的时间不对更麻烦,定时任务、订单时间全乱,建站的一开始就配好。

故障十:宿主机层面

Docker 都没问题但机器卡?往下看一层:

dmesg | tail -30        # 内核日志,看 OOM、磁盘错误
journalctl -u docker --since "1 hour ago"  # Docker 守护进程的日志
free -h && df -h        # 内存和磁盘一起看
uptime                  # 看 load average,1 核机器 load 超过 2 就是过载

宿主机内存耗尽时,Docker 守护进程自己都可能被 OOM,这时候 docker ps 都没响应——只能去商家后台看监控或重启。资源限制那篇配好了能大幅降低这种概率。磁盘 IO wait 高(top 看 wa 列)说明磁盘是瓶颈,小机器的通病,加内存比换磁盘管用。

最后的手段

docker inspect 容器名   # 看完整配置,挂载、网络、环境变量全在这
docker events           # 实时看 Docker 在干什么,适合抓"谁重启了我的容器"

实在搞不定,把 docker logs 的报错原文拿去搜——你遇到的坑,别人基本都踩过。报错信息贴全,别只贴"我觉得关键的那两行",新手最容易误判哪行关键。提问时带上:系统版本、Docker 版本、compose 文件(脱敏)、完整报错,这四样齐了,别人才能帮你。

案例复盘:三个真实故事

案例一:升级 WordPress 后白屏。 症状:up -d 后网站白屏,容器状态 Up。排查:docker logs 看到 PHP Fatal error,插件不兼容新版。解决:compose 文件里 image tag 改回旧版,up -d 回滚,禁掉那个插件再升级。教训:升级前备份 + 先看插件兼容性,应用回滚靠 tag。

案例二:磁盘满了导致全站挂。 症状:所有容器陆续异常,docker ps 都卡。排查:df -h 显示 100%,du 定位到某个容器的 json.log 几个 GB。解决:truncate 清空日志,daemon.json 配轮转,重建容器。教训:日志轮转是部署时就要配的,不是出事了才配。

案例三:compose 改完起不来。 症状:up 报 YAML 错误。排查:docker compose config 直接指出第 23 行缩进问题——混用了 Tab。解决:改成空格。教训:改完先 config,三秒钟省半小时。

预防清单:不出事的秘诀

  1. 部署时配好:restart 策略、日志轮转、资源限制、healthcheck。
  2. 改配置前备份 compose 文件:cp docker-compose.yml docker-compose.yml.bak。
  3. 改完先 docker compose config 校验。
  4. 升级前备份数据卷,大版本先看 release note。
  5. 每月看一次 docker system df 和 df -h。
  6. 重要操作挑低峰期做,留好回滚路径。

排错能力 = 预防 + 套路。这篇手册是套路,清单是预防,两个都有了,Docker 就真成你的工具了。