Skip to content

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 8080
text
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

两种情况:

  1. 旧容器占着:docker stop old-app。
  2. 宿主机进程占着(比如本机的 Tomcat):换端口 -p 18080:8080。
bash
# 确认是不是 docker-proxy 占的
ps -fp 29411
text
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 8080

docker-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}}' app
text
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 -lntp
text
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:alpine
text
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.json
text
Registry Mirrors:
 https://xxxx.mirror.aliyuncs.com/

四种典型错误:

错误原因处置
Client.Timeout exceeded连不上 Docker Hub配镜像加速
dial tcp: lookup registry-1.docker.io ... no such hostDNS 解析失败改 /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 -5

inode 用尽:

bash
df -i /data

案例 6:容器内存超限被 OOMKilled ​

现象:服务每隔几小时重启一次,日志没有异常。

bash
docker inspect -f '{{.State.OOMKilled}} {{.State.ExitCode}} {{.HostConfig.Memory}} {{.RestartCount}}' app
text
true 137 2147483648 3

OOMKilled=true、limit 2GB、已重启 3 次。

bash
dmesg -T | grep -i 'memory cgroup out of memory' | tail -3
text
[Tue Oct 27 21:31:02 2026] Memory cgroup out of memory: Killed process 31882 (java)
total-vm:3214342kB, anon-rss:2089412kB

anon-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_info
text
 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 id
text
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.0

SELinux

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 sh
text
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/null
text
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/localtime

Alpine 镜像默认没有 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}}' app
text
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 + idUID 不匹配、SELinux
进不去容器docker inspect Entrypointdistroless 无 shell
时间差 8 小时docker exec date缺 tzdata、没设 TZ
重启后容器消失RestartPolicy没开 live-restore、data-root 改了