docker ps 里容器状态一直显示 Restarting (1) xx seconds ago,或者刚 up 没多久又变成 Exited——这是用 Docker 时最常碰到的故障之一。容器反复重启,本质就一句话:容器主进程退出了,而重启策略又把它拉起来,于是循环。排查的思路也很直接,就是去找”主进程到底为什么退出”。下面按步骤给出能直接复制的命令。
第一步:看容器日志,绝大部分问题在这一步就能看出来
docker logs --tail 100 容器名
docker logs -f 容器名 # 实时跟踪重启瞬间的输出
常见的日志线索有这些:配置文件路径写错、数据库连不上、端口被占用、缺环境变量、应用自己抛异常。日志能看懂的,直接回去修应用问题就行;日志为空或者看不出名堂的,继续下一步。
第二步:查退出码,判断退出类型
docker inspect 容器名 --format '{{.State.ExitCode}} {{.State.OOMKilled}} {{.State.Error}}'
退出码对照如下:
- 0:进程正常结束——通常是容器里没有前台常驻进程(见第四步);
- 1:应用报错退出——回去细看 docker logs;
- 137:被 SIGKILL 杀掉,若 OOMKilled 为 true 就是内存超限;
- 126/127:启动命令不可执行/找不到——检查 entrypoint 和 command 的拼写;
- 139:段错误,多半是程序或依赖库自己的缺陷。
第三步:内存不足(退出码 137)的处理
先确认宿主机整体内存和容器的限制:
free -h
docker stats --no-stream
如果容器设了 -m 内存上限,而且经常打满,要么调大限制,要么优化应用(比如给 Java 应用设 -Xmx 小于容器限制,给 MySQL 容器调低 innodb_buffer_pool_size)。宿主机整体内存不够时,可以临时加 swap 应急:
fallocate -l 2G /swapfile && chmod 600 /swapfile
mkswap /swapfile && swapon /swapfile
第四步:退出码 0 却一直重启?主进程没有常驻
容器要求 PID 1 是一个前台常驻的进程。下面这些写法都会”启动即退出”:入口命令是一条执行完就结束的脚本、nginx 没加 daemon off、用了 service xxx start 这类后台化命令。修复示例:
# 错误:CMD service nginx start
# 正确:
CMD ["nginx", "-g", "daemon off;"]
调试时可以先用 sleep 把容器保住,再进去手动跑命令看报错:
docker run -it --entrypoint /bin/sh 镜像名
# 容器内手动执行原启动命令,直接观察报错
第五步:检查重启策略与依赖顺序
docker inspect 容器名 --format '{{.HostConfig.RestartPolicy.Name}}'
如果应用依赖数据库,而数据库容器还没就绪应用就启动失败,就会形成”启动-失败-重启”的循环。docker-compose 里应该用 healthcheck + depends_on 的 condition: service_healthy,而不是裸的 depends_on。另外磁盘写满(用 df -h 检查)也会让容器反复起不来。
常见问题
如何暂时阻止容器无限重启,方便排查?
先执行 docker update --restart=no 容器名 再 docker stop,排查修好后再改回 always 或 unless-stopped。
docker logs 什么都没有输出怎么办?
说明主进程在打印任何内容之前就退出了,多半是入口命令本身有误。用上面那个 –entrypoint /bin/sh 的方式进容器,手动执行启动命令来定位。
容器在宿主机重启后全部变成 Exited 正常吗?
如果重启策略是 no 就属于正常现象。希望开机自启的容器,应该设成 –restart unless-stopped。
如果你的容器问题按上面的步骤还没解决,欢迎到 fenij.com 评论区贴出 docker logs 和退出码,我们帮你一起看。