如何排查一个状态显示为 Exited (1) 且无法正常启动的容器?
当 Docker 容器的状态显示为 Exited (1) 时,意味着容器内的主进程(PID 1)异常退出,并且返回了错误码 1(通常表示通用错误)。
要排查并解决这个问题,可以按照以下标准排查步骤进行,从最简单直接的查看日志,到深入的调试模式:
第一步:查看容器日志(最直接有效)
容器退出时,通常会在标准输出或标准错误中留下线索。
bash
docker logs <container_id_or_name>
- 常用参数:
docker logs --tail 100 <container_id>:只看最后 100 行。docker logs -t <container_id>:带上时间戳,方便比对报错时间。
常见日志报错及原因:
Can't find file...或No such file or directory:找不到配置文件或启动脚本。Permission denied:权限不足(可能需要非 root 用户运行,但目录权限没设对)。Address already in use或port is already allocated:端口冲突。- 数据库连接超时、凭证错误等。
第二步:查看容器的详细元数据(Inspect)
如果日志里什么都没有,可以通过 docker inspect 查看容器退出的具体时间和配置。
bash
docker inspect <container_id_or_name>
重点关注以下几项:
State模块:ExitCode: 应该显示为1。Error: 是否有 Docker 引擎级别的报错信息。FinishedAt: 确认容器退出的精确时间。
Path和Args:- 看看容器启动时实际执行了什么命令,是否与你预期的一致。
Mounts:- 检查挂载的宿主机目录是否存在,权限是否正确。
第三步:尝试在前台运行(交互模式)
有时候容器一启动就挂了,导致日志没来得及写入。你可以尝试用交互模式手动运行该镜像,看看在命令行中直接启动会报什么错。
通过 docker ps -a 找到该容器使用的镜像名称(IMAGE),然后运行:
bash
docker run -it --rm <image_name> /bin/sh
# 或者如果支持 bash
docker run -it --rm <image_name> /bin/bash
--rm:退出后自动删除这个临时测试容器。-it:开启交互式终端。
在容器内手动测试:
进入容器后,手动执行容器原本的启动命令(可以通过第二步 inspect 里的 Path 和 Args 找到,或者查看 Dockerfile 中的 ENTRYPOINT 或 CMD)。
例如:
bash
# 进入容器后,手动运行你的程序
python app.py
# 或者
npm start
此时,任何报错都会直接打印在你的终端上。
第四步:覆盖 Entrypoint 强行进入容器
如果容器镜像设置了 ENTRYPOINT,导致你无法通过上面的命令进入 shell,你可以强制覆盖它:
bash
docker run -it --rm --entrypoint /bin/sh <image_name>
# 或者
docker run -it --rm --entrypoint /bin/bash <image_name>
进去之后,检查:
- 依赖包是否完整。
- 环境变量是否正确(输入
env查看)。 - 配置文件路径是否正确。
第五步:排查常见诱因
如果以上方法仍未定位到问题,请检查以下几点:
- 配置文件/挂载问题(最常见):
- 你是否挂载了宿主机的配置文件?如果宿主机上的文件不存在,Docker 可能会把该路径当成一个目录创建,导致容器内的程序读取失败。
- 检查挂载目录的权限:
chmod -R 755 /path/to/host/dir。
- 端口冲突:
- 容器要绑定的宿主机端口是否已被其他程序占用?
- 用
netstat -tuln | grep <端口号>检查宿主机端口。
- 内存不足(OOM - Out of Memory):
- 虽然 OOM 的退出码通常是
137,但有时也会表现为1。 - 检查宿主机内存:
free -m。 - 检查系统日志:
dmesg -T | grep -i oom或tail -n 100 /var/log/messages。
- 虽然 OOM 的退出码通常是
- 环境变量缺失:
- 某些镜像(如 MySQL, Postgres)如果启动时不提供必要的环境变量(如
MYSQL_ROOT_PASSWORD),会自动退出。
- 某些镜像(如 MySQL, Postgres)如果启动时不提供必要的环境变量(如
总结排查口诀:
先看日志 (
logs),再看配置 (inspect);
前台交互 (run -it),覆盖入口 (entrypoint);
检查挂载与权限,端口内存扫一遍。