外观
Docker 排错手册
按现象查。每个 case 给:先看什么、输出怎么读、根因在哪。
案例 1:容器起来就退出
现象:docker ps 里看不到,docker ps -a 显示 Exited。
bash
docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Image}}'
docker logs --tail 100 <容器名>
docker inspect -f '{{.State.ExitCode}} {{.State.OOMKilled}} {{.Config.Entrypoint}} {{.Config.Cmd}}' <容器名>text
NAMES STATUS IMAGE
worker Exited (0) 5 seconds ago worker:1.0.0
app Exited (1) 8 seconds ago myapp:1.0.0
# inspect 输出
1 false [/bin/sh -c] [java -jar app.jar]按退出码分:
| 退出码 | 结论 | 下一步 |
|---|---|---|
| 0 | 主进程执行完就结束了 | 容器需要一个前台常驻进程 |
| 1 | 应用自己报错 | 看 docker logs 的第一段堆栈 |
| 126 | 命令没执行权限 | ls -l 看入口脚本 |
| 127 | 命令不存在 | docker run --rm -it image sh 进去找 |
| 137 | 被 SIGKILL | 查 OOMKilled 与 dmesg |
| 143 | 收到 SIGTERM | 谁在 stop 它(编排/健康检查/人工) |
ExitCode 0 最常见的原因是镜像的 CMD 是 service nginx start(后台启动后立即返回):
dockerfile
# 错:service 启动后命令返回,容器退出
CMD service nginx start
# 对:前台运行
CMD ["nginx", "-g", "daemon off;"]排查终极手段:覆盖入口跑一个 shell。
bash
docker run --rm -it --entrypoint sh myapp:1.0.0进去手动执行原入口,看它报什么。
有些镜像连 sh 都没有
--entrypoint sh 报 executable file not found 时,说明是 distroless/scratch。用 docker create + docker export 把文件系统导出来看:
bash
docker create --name tmp myapp:1.0.0
docker export tmp | tar -tv | head -30
docker rm tmp案例 2:端口被占用
现象:docker run -p 8080:8080 报 Bind for 0.0.0.0:8080 failed: port is already allocated。
bash
ss -lntp | grep 8080
docker ps --format 'table {{.Names}}\t{{.Ports}}' | grep 8080text
LISTEN 0 4096 0.0.0.0:8080 0.0.0.0:* users:(("docker-proxy",pid=29411,fd=4))
old-app 0.0.0.0:8080->8080/tcp两种情况:
- 旧容器占着:
docker stop old-app。 - 宿主机进程占着(比如本机的 Tomcat):换端口
-p 18080:8080。
bash
# 确认是不是 docker-proxy 占的
ps -fp 29411text
UID PID PPID C STIME TTY STAT TIME CMD
root 29411 1 0 Oct26 ? Sl 0:02 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 8080docker-proxy 是 Docker 的用户态转发进程,说明有容器映射了这个端口。
案例 3:宿主机或外部访问不了容器端口
现象:容器 Up,docker exec 进去 curl localhost:8080 正常,宿主机 curl localhost:8080 失败。
bash
docker port app
ss -lntp | grep 8080
docker inspect -f '{{json .NetworkSettings.Ports}}' apptext
8080/tcp -> 127.0.0.1:8080
LISTEN 0 4096 127.0.0.1:8080 0.0.0.0:* users:(("docker-proxy",pid=29411,fd=4))
{"8080/tcp":[{"HostIp":"127.0.0.1","HostPort":"8080"}]}HostIp 是 127.0.0.1 → 只有宿主机本机能访问,外部不行。
三种原因:
| 现象 | 原因 | 处置 |
|---|---|---|
HostIp: 127.0.0.1 | 启动时写了 -p 127.0.0.1:8080:8080 | 改成 -p 8080:8080 |
| 宿主机能通、外部不通 | 云安全组 / firewalld | 开安全组,firewall-cmd --add-port |
| 容器内只监听 127.0.0.1 | 应用 bind 127.0.0.1 | 改应用监听 0.0.0.0 |
第三种特别隐蔽:-p 映射到了容器端口,但容器内的应用只监听 loopback,DNAT 过去的流量进不去。
bash
docker exec app ss -lntptext
LISTEN 0 128 127.0.0.1:8080 0.0.0.0:*看到 127.0.0.1:8080 就要改应用配置(Spring 的 server.address=0.0.0.0)。
案例 4:拉镜像失败
现象:docker pull 卡住、超时、或 401。
bash
docker pull nginx:alpinetext
Error response from daemon: Get "https://registry-1.docker.io/v2/":
net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)按顺序查:
bash
# 1. DNS
cat /etc/resolv.conf
getent hosts registry-1.docker.io
# 2. 网络
curl -I --max-time 10 https://registry-1.docker.io/v2/
# 3. 加速器配置是否生效
docker info | grep -A3 'Registry Mirrors'
# 4. 私有仓库认证
cat ~/.docker/config.jsontext
Registry Mirrors:
https://xxxx.mirror.aliyuncs.com/四种典型错误:
| 错误 | 原因 | 处置 |
|---|---|---|
Client.Timeout exceeded | 连不上 Docker Hub | 配镜像加速 |
dial tcp: lookup registry-1.docker.io ... no such host | DNS 解析失败 | 改 /etc/resolv.conf |
unauthorized: authentication required | 私有仓库没登录 | docker login |
denied: requested access to the resource is denied | 账号无权限 / 镜像名错 | 检查命名空间 |
no space left on device | 磁盘满 | 清镜像/日志 |
案例 5:磁盘满了
现象:docker run 报 no space left on device,或者镜像拉不下来。
bash
df -h /data
docker system df -v
du -sh /data/docker/*text
Filesystem Size Used Avail Use% Mounted on
/dev/vdb 500G 500G 0 100% /data
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 48 10 62.4GB 41.2GB (66%)
Containers 18 10 2.1GB 0B (0%)
Local Volumes 12 4 28.4GB 12.1GB (42%)
Build Cache 412 0 18.6GB 18.6GB三步清:
bash
# 1. 构建缓存(最安全,且通常最大)
docker builder prune -f
# 2. 悬空镜像
docker image prune -f
# 3. 已停止容器
docker container prune -f还不够再动镜像:
bash
docker image prune -a -f # 所有没被容器使用的镜像,下一次要重新拉别在这台机器上跑 docker system prune --volumes
如果这台机器上有数据库容器,--volumes 会删掉未挂载的命名卷。先 docker volume ls 和 docker volume inspect 逐个确认。
日志也要查:
bash
du -sh /data/docker/containers/*/*-json.log | sort -h | tail -5inode 用尽:
bash
df -i /data案例 6:容器内存超限被 OOMKilled
现象:服务每隔几小时重启一次,日志没有异常。
bash
docker inspect -f '{{.State.OOMKilled}} {{.State.ExitCode}} {{.HostConfig.Memory}} {{.RestartCount}}' apptext
true 137 2147483648 3OOMKilled=true、limit 2GB、已重启 3 次。
bash
dmesg -T | grep -i 'memory cgroup out of memory' | tail -3text
[Tue Oct 27 21:31:02 2026] Memory cgroup out of memory: Killed process 31882 (java)
total-vm:3214342kB, anon-rss:2089412kBanon-rss 2.09GB ≈ limit 2GB → 确实是堆外+堆一起撑爆。
看容器内的实际构成:
bash
docker exec app sh -c 'cat /sys/fs/cgroup/memory.current; cat /sys/fs/cgroup/memory.max'text
2089410560
2147483648或者看 JVM:
bash
docker exec app jcmd 1 GC.heap_infotext
PSYoungGen total 524288K, used 89124K
eden space 458752K, 19% used
from space 65536K, 0% used
to space 65536K, 0% used
ParOldGen total 1048576K, used 1048576K
Metaspace used 148231K, committed 165888K堆 1.5GB(默认 -Xmx 用了宿主机内存)+ 元空间 148MB + 直接内存 → 超过 2GB。
修:
bash
# 1. 让 JVM 按容器 limit 算堆
java -XX:MaxRAMPercentage=70 -jar app.jar
# 2. 加大 limit
docker update --memory 3g app # 仅对 running 生效,注意 --memory-swap 也要调案例 7:容器写挂载目录报权限错误
现象:Permission denied,应用起不来。
bash
docker logs app | tail -20
ls -ld /data/app/data
docker run --rm myapp:1.0.0 idtext
java.io.FileNotFoundException: /app/data/upload (Permission denied)
drwxr-xr-x 2 root root 4096 Oct 27 21:35 /data/app/data
uid=10001(app) gid=10001(app) groups=10001(app)uid 10001 对 root 拥有的 755 目录没有写权限。
bash
chown -R 10001:10001 /data/app/data或者用命名卷(Docker 会自动按镜像的 VOLUME 设定属主):
bash
docker run -d -v app-data:/app/data myapp:1.0.0SELinux
CentOS/Rocky 开了 SELinux 时,还要加 :z 或 :Z:
bash
-v /data/app/data:/app/data:z不加的话即使 chown 对了,也会报权限错误。ausearch -m avc -ts recent 能看到拒绝记录。
案例 8:exec 进不去容器
现象:docker exec -it app sh 报 executable file not found in $PATH。
bash
docker exec -it app shtext
OCI runtime exec failed: exec failed: unable to start container process:
exec: "sh": executable file not found in $PATH: unknown镜像是 distroless / scratch,故意不带 shell。可选方案:
bash
# 1. 看入口是什么
docker inspect -f '{{.Config.Entrypoint}} {{.Config.Cmd}}' app
# 2. 用同样镜像跑一次性命令(镜像里的二进制还在)
docker run --rm --entrypoint /app/healthcheck myapp:1.0.0
# 3. 借一个带工具的容器,共享进程和网络命名空间
docker run --rm -it \
--pid=container:app --network=container:app \
nicolaka/netshoot sh
# 4. Docker Desktop 24+ / 企业版
docker debug app第三招最通用:临时容器能 ps、top、tcpdump、curl,看到的就是目标容器的现场。
案例 9:容器时间不对
现象:日志时间差 8 小时,或者定时任务在错误的时间触发。
bash
date
docker exec app date
docker exec app cat /etc/timezone 2>/dev/nulltext
Tue Oct 27 21:40:12 CST 2026
Tue Oct 27 13:40:12 UTC 2026容器内是 UTC。修法(按优先级):
bash
# 1. 传环境变量,JVM 会识别
docker run -d -e TZ=Asia/Shanghai myapp
# 2. 挂载宿主机的时区文件
docker run -d -v /etc/localtime:/etc/localtime:ro myapp
# 3. 镜像里装 tzdata 并设置(最稳)
# Dockerfile:
# RUN apk add --no-cache tzdata && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtimeAlpine 镜像默认没有 tzdata,只设 TZ 环境变量时 date 仍显示 UTC(glibc 有内置,musl 需要时区文件)。所以第 3 种最可靠。
案例 10:重启 Docker 后容器全没了
现象:systemctl restart docker 之后 docker ps 是空的。
bash
docker ps -a --format 'table {{.Names}}\t{{.Status}}'text
NAMES STATUS
app Exited (255) 2 minutes ago原因通常是:
| 原因 | 说明 |
|---|---|
live-restore 没开 | daemon 重启会停掉所有容器 |
| 容器没配 restart 策略 | 起来了但不会再被拉起 |
data-root 改了 | 新目录是空的,看起来"全没了" |
bash
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' apptext
no修:
json
// /etc/docker/daemon.json
{ "live-restore": true }bash
docker update --restart unless-stopped app改 data-root 后"容器消失"
它们还在旧目录里。此时不要 docker system prune,先确认是不是路径问题:
bash
du -sh /var/lib/docker /data/docker把旧的 rsync 到新目录再重启,见 安装与加速。
快速对照表
| 现象 | 第一步 | 常见根因 |
|---|---|---|
| 容器立即退出 | docker logs + ExitCode | 主进程非前台、命令不存在、应用异常 |
| 端口冲突 | ss -lntp | 旧容器、宿主进程 |
| 外部访问不通 | docker port + ss | 绑 127.0.0.1、安全组、容器内监听地址 |
| 拉镜像失败 | docker info | grep Mirror | 加速器、DNS、认证 |
| 磁盘满 | docker system df -v | 构建缓存、日志、旧镜像 |
| 反复重启 | OOMKilled | 内存限制、JVM 堆外 |
| 权限拒绝 | ls -ld + id | UID 不匹配、SELinux |
| 进不去容器 | docker inspect Entrypoint | distroless 无 shell |
| 时间差 8 小时 | docker exec date | 缺 tzdata、没设 TZ |
| 重启后容器消失 | RestartPolicy | 没开 live-restore、data-root 改了 |