凌晨线上容器集体 Exited,我冲上去连敲 docker ps 都没有反应,折腾半小时才发现根因根本不在容器,而在守护进程。
这篇把 Docker 排查收敛成一条决策链路,从报错到根因,照着走就行。
一、docker ps 都没反应:问题在守护进程
容器挂了,你登录主机敲下 docker ps,终端没有列出任何容器,反而甩回这么一句:
latex
Cannot connect to the Docker daemon at unix:///var/run/docker.sock.
Is the docker daemon running?
这句报错和容器本身没有任何关系。Docker 采用客户端/服务器架构,docker 命令只是客户端,真正管理容器、镜像的是后台的守护进程(dockerd),二者通过本地套接字 /var/run/docker.sock 通信。连不上守护进程,只有两种可能:进程没启动,或者当前用户没有权限访问套接字。
| 报错根因 | 典型现象 | 处理方式 |
|---|---|---|
| 守护进程未启动 | 重启机器或服务异常后,所有命令无响应 | 启动并设置开机自启 |
| 当前用户无权限 | 普通用户执行命令报权限相关错误 | 把用户加入 docker 用户组 |
对应的修复命令:
bash
systemctl status docker
systemctl start docker
systemctl enable docker
usermod -aG docker $USER
newgrp docker
如果你执行 systemctl start docker 后服务状态直接是 failed,说明守护进程没起来,常见诱因是 /etc/docker/daemon.json 配置语法错误或存储驱动不匹配。这时不要反复重启,直接看守护进程日志定位:
bash
journalctl -u docker -xe
修复配置后重新 systemctl restart docker,再用 docker info 验证:能正常打印版本、存储驱动、容器数量等信息,就说明守护进程已经恢复。
注意:docker info 是验证守护进程是否真正可用的最快方式,比 systemctl status 更可信。
守护进程通了,docker ps 也有输出了,可你 docker run 启动一个新容器时,却报端口冲突、容器名冲突、OCI 运行时失败,甚至容器瞬间 Exited,这四种情况怎么分?
二、容器起不来问题排查
容器启动失败看似报错五花八门,实则只有四道门。先按报错关键字分流,再用对应命令取证:
| 报错关键字 | 根因 | 第一手取证命令 |
|---|---|---|
| port is already allocated | 宿主机端口被其他进程或容器占用 | ss / lsof |
| name already in use | 同名容器已存在(多为停止状态) | docker ps -a |
| OCI runtime create failed | 权限、SELinux、挂载、cgroup 等系统级问题 | docker inspect / journalctl |
| Exited (1) | 容器内主进程启动失败后退出 | docker logs |
第一道:端口被占用。映射端口前,先查是谁占用了它:
bash
ss -tulnp | grep 8080
lsof -i:8080
找到占用进程后,停掉冲突进程,或把容器映射到其他端口(如 -p 8081:80)。
第二道:容器名冲突。同名容器即使已经停止,名字仍然被占用,删掉旧容器即可:
bash
docker ps -a
docker rm 旧容器名或容器ID
第三道:运行时创建失败。这是系统级问题,重点看挂载路径和安全策略。用 docker inspect 查看容器完整配置,用 journalctl 查看守护进程记录的底层拒绝信息:
bash
docker inspect 容器ID
journalctl -u docker -xe
第四道:启动即退出。docker run 明明返回成功,容器状态却是 Exited (1)。我第一次遇到时只会反复 docker run,每次都瞬间退出,后来才明白停止的容器进不去,唯一的现场就是日志:
bash
docker ps -a
docker logs --tail 200 容器名或容器ID
docker logs -f --tail 200 容器名或容器ID
容器的生命周期与主进程绑定:主进程在,容器在;主进程退出,容器随之停止。 所以 Exited 的根因永远在主进程的启动日志里,而不是 Docker 本身。
处理完后用 docker ps 验证,目标容器状态为 Up 即通过。
容器能起了,可换台机器、或重新拉取镜像时,docker pull 直接卡死或报拒绝,连镜像都没有,容器从何而来?
三、镜像拉不下来:问题集中在认证、网络、架构
镜像(Image)是容器的只读模板,拉取失败集中在认证、网络、架构三类,先按报错关键字对号入座:
| 报错关键字 | 根因 | 处理方式 |
|---|---|---|
| pull access denied / manifest unknown | 私有仓库未登录,或标签(tag)写错 | docker login,核对镜像名与标签 |
| TLS handshake timeout / connection refused | 网络波动、加速器失效、代理异常 | 配置镜像加速器或代理 |
| exec format error | 镜像架构与宿主机不匹配 | 指定平台参数运行 |
认证类问题,先登录私有仓库,再核对镜像名和标签是否真实存在:
bash
docker login registry.example.com
docker pull nginx:1.25
docker logout registry.example.com
网络类问题,在 /etc/docker/daemon.json 中配置镜像加速器,然后重启守护进程生效:
json
{
"registry-mirrors": ["https://你的加速器地址"]
}
bash
systemctl daemon-reload
systemctl restart docker
架构类问题,我在一台 ARM64 主机上跑 AMD64 镜像时栽过,CPU 指令集不兼容,启动即报 exec format error。跨架构运行时用 --platform 显式指定架构:
bash
docker run --platform linux/amd64 nginx:1.25
还有一类不是报错、而是慢:每次 docker build 都全量重新下载依赖,分层缓存完全失效。镜像由多个只读层(layer)堆叠而成,构建时逐层命中缓存。Dockerfile 中任意一层发生变更,其后所有层的缓存全部失效。 因此依赖安装必须放在业务代码拷贝之前,并用 .dockerignore 排除无关文件:
dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "server.js"]
把 package.json 单独提前 COPY,业务代码变更不会再让依赖层缓存失效,这是构建提速最直接的一步。
用 docker images 验证目标镜像与标签存在,即拉取或构建完成。
镜像有了、容器也 Up 了,可 curl 过去要么超时、要么 502,容器之间还互相 ping 不通,这网络怎么查?
四、容器活着但不通:容器间、宿主机、DNS
Docker 网络问题可以收敛为三件事,逐一排查:
| 故障现象 | 根因 | 处理方式 |
|---|---|---|
| 容器间 ping 容器名报 bad address | 默认桥接网络(bridge)隔离,不提供容器名解析 | 改用自定义桥接网络 |
| 宿主机访问不了容器 | 端口映射未生效、服务只监听 127.0.0.1、防火墙拦截 | 核对映射、监听地址、防火墙 |
| 容器内域名解析失败 | 默认 DNS 异常 | 指定 DNS 或全局配置 |
容器之间默认都接入名为 bridge 的默认网络,该网络下容器彼此隔离,也不能通过容器名互访。创建一个自定义桥接网络,把相关容器都接进去:
bash
docker network create mynet
docker run -d --network mynet --name app nginx:1.25
docker run -d --network mynet --name db mysql:8.0
同一自定义网络内,容器可直接通过容器名互相访问,Docker 会自动维护容器名到 IP 的域名解析(DNS)。 之后 app 连接 db 直接用容器名即可,无需写死 IP。
宿主机访问不了容器时,先确认映射和服务监听地址。服务若只监听容器内的 127.0.0.1,宿主机和其他容器都无法访问,必须监听 0.0.0.0:
bash
docker ps
docker exec -it 容器ID netstat -tulnp
容器内无法解析外网域名(Name or service not known),可以在启动时指定公共 DNS 临时验证,再决定是否写入守护进程全局配置:
bash
docker run -d --dns 8.8.8.8 nginx:1.25
验证方式:进入一个容器 ping 同网络的另一个容器名,能通则容器间网络正常。
网络通了,业务却开始报 Permission denied、写文件失败,甚至整个节点磁盘写满,这些"看不见"的存储问题怎么处理?
五、写不进去、磁盘爆满:存储与权限
存储类问题没有统一报错位置,先按现象归为三类:
| 故障现象 | 根因 | 处理方式 |
|---|---|---|
| 挂载目录 Permission denied | 容器内用户 UID/GID 与宿主机目录权限不一致 | chown 或 --user 指定用户 |
| 挂载后容器内文件全部消失 | 空目录绑定挂载(bind mount)遮蔽了容器目录 | 改用命名卷,或先复制文件 |
| no space left on device | 废弃镜像、容器、缓存、日志占满磁盘 | 查看占用并清理 |
权限拒绝,本质是容器内进程的用户身份和宿主机目录归属对不上。把宿主机目录归属改成容器内用户的 UID/GID,或启动时直接指定运行用户:
bash
chown -R 1000:1000 /data
docker run -d --user 1000:1000 -v /data:/container/data nginx:1.25
把宿主机空目录挂载到容器内非空目录时,容器目录里镜像自带的配置文件不会被删除,但会被宿主机目录整体遮蔽,看起来就是"文件全没了"。绑定挂载会用宿主机目录原样遮蔽容器目录,镜像内文件不会自动复制出来。 更稳妥的做法是使用命名卷(named volume),由 Docker 管理数据,首次挂载时还会自动把容器目录内容拷入卷中:
bash
docker volume create myvol
docker run -d -v myvol:/container/data nginx:1.25
磁盘写满,先看清空间被谁占用,再决定清理范围:
bash
docker system df
docker system prune -a
docker volume prune
注意:<font style="color:#c00;">docker system prune -a</font> 会删除所有未被运行容器使用的镜像,生产环境执行前务必确认镜像可重新拉取,避免误删。
容器日志也会持续膨胀,路径为 /var/lib/docker/containers/*/*-json.log,需要纳入定期清理。清理后用 docker system df 验证空间已回收。
零散命令临时记不住,有没有一套固定打法?
六、通用命令速查

下面这九条命令覆盖了绝大多数取证场景,建议直接收藏:
bash
docker ps -a
docker logs -f --tail 200 容器名或容器ID
docker inspect 容器名或容器ID
docker exec -it 容器名或容器ID sh
docker stats
docker events
docker system df
docker info
journalctl -u docker -xe
排查闭环始终是同一条路径:
latex
现象 -> 日志 -> 定位 -> 修复 -> 验证
| | |
报错截图 logs/journalctl 对照本文表格分流
术语速查表:
| 术语 | 英文 | 含义 |
|---|---|---|
| 镜像 | Image | 容器的只读模板,含运行所需文件 |
| 容器 | Container | 镜像运行后的实例,生命周期随主进程 |
| 守护进程 | dockerd | 后台服务,管理镜像、容器、网络、存储 |
| 标签 | tag | 镜像的版本标记,如 nginx:1.25 |
| 分层 | layer | 镜像由多层只读层堆叠,支撑构建缓存 |
| 桥接网络 | bridge | Docker 默认网络驱动,默认网络下容器隔离 |
| 域名解析 | DNS | 将容器名或域名解析为 IP |
| 绑定挂载 | bind mount | 把宿主机目录直接挂载进容器 |
| 命名卷 | named volume | 由 Docker 管理的数据卷 |
| 用户标识 | UID/GID | 进程的用户与用户组编号,决定文件权限 |
结语
排查 Docker 故障,顺序不能乱,越往下越靠近业务内层------先确认命令能不能用(守护进程),再看容器能不能起(四道门),然后是镜像能不能拉(三道关)、网络通不通(三件事)、数据对不对(存储)。绝大多数问题不需要深入调试,按报错关键字分流、用对应命令取证,就能快速定位。
参考链接(均为 Docker 官方文档):
- Troubleshooting the Docker daemon:https://docs.docker.com/engine/daemon/troubleshoot/
- Network drivers:https://docs.docker.com/engine/network/drivers
- Prune unused Docker objects:https://docs.docker.com/engine/manage-resources/pruning/
- View container logs:https://docs.docker.com/engine/logging/
- Docker CLI Cheat Sheet:https://www.docker.com/resources/cli-cheat-sheet/