跳到主要内容

Docker 常见问题与故障排除

本章汇总了 Docker 使用过程中最常见的问题及其解决方案,帮助你快速定位和解决问题。

容器启动问题

容器启动后立即退出

现象:运行 docker run 后容器立即退出,状态显示为 Exited。

排查步骤

# 查看容器退出码
docker inspect --format='{{.State.ExitCode}}' <容器名或ID>

# 查看容器日志
docker logs <容器名或ID>

# 查看详细退出信息
docker inspect --format='{{.State.Error}}' <容器名或ID>

常见退出码含义

退出码含义可能原因
0正常退出短期任务执行完成
1应用错误程序抛出未捕获的异常
126命令无法执行权限问题或命令不存在
127命令未找到路径错误或命令不存在
137被 SIGKILL 杀死内存不足(OOM)或手动 kill
139段错误程序访问了非法内存地址
143被 SIGTERM 终止正常停止(docker stop)

解决方案

# 退出码为 0:如果是长期运行的服务,检查是否缺少保持运行的命令
# 错误示例
docker run -d nginx # Nginx 默认会后台运行,导致主进程退出

# 正确示例
docker run -d nginx:alpine nginx -g "daemon off;"

# 退出码为 137:检查是否被 OOM 杀死
docker inspect --format='{{.State.OOMKilled}}' <容器名或ID>

# 如果是 OOM,增加内存限制
docker run -d --memory="1g" nginx

# 退出码为 1:查看应用日志定位错误
docker logs --tail 100 <容器名或ID>

镜像拉取失败

现象:执行 docker pull 时报错。

常见错误及解决方案

1. 网络连接超时

# 错误信息
Error: Get https://registry-1.docker.io/v2/: net/http: TLS handshake timeout

# 解决方案:配置镜像加速器
# Linux: 编辑 /etc/docker/daemon.json
# Windows/macOS: Docker Desktop -> Settings -> Docker Engine

{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.nju.edu.cn"
]
}

# 重启 Docker
sudo systemctl restart docker # Linux

2. 认证失败

# 错误信息
unauthorized: authentication required

# 解决方案:登录 Docker Hub 或私有仓库
docker login
# 或指定仓库
docker login myregistry.example.com

# 登出后重试
docker logout
docker login

3. 镜像不存在或标签错误

# 错误信息
manifest for image:tag not found

# 解决方案:检查镜像名和标签是否正确
docker search nginx
docker pull nginx:stable-alpine

4. 磁盘空间不足

# 错误信息
no space left on device

# 解决方案:清理未使用的资源
docker system prune -a --volumes

# 查看磁盘使用
docker system df

端口冲突

现象:启动容器时提示端口已被占用。

# 错误信息
Error: bind: address already in use

# 查看端口占用(Linux/macOS)
lsof -i :8080
netstat -tuln | grep 8080

# 查看端口占用(Windows)
netstat -ano | findstr :8080

# 解决方案 1:停止占用端口的进程
kill -9 <PID> # Linux/macOS
taskkill /PID <PID> /F # Windows

# 解决方案 2:使用不同端口
docker run -d -p 8081:80 nginx

# 解决方案 3:查找并停止相关容器
docker ps | grep 8080
docker stop <容器ID>

资源问题

内存不足(OOM)

现象:容器被强制终止,退出码为 137。

排查步骤

# 检查是否被 OOM 杀死
docker inspect --format='{{.State.OOMKilled}}' <容器名或ID>

# 查看容器内存使用历史
docker stats --no-stream <容器名或ID>

# 查看系统内存
free -h

解决方案

# 增加容器内存限制
docker run -d --memory="2g" --memory-swap="2g" myapp

# 设置内存软限制
docker run -d --memory="2g" --memory-reservation="1g" myapp

# 禁用 OOM Killer(谨慎使用)
docker run -d --oom-kill-disable --memory="2g" myapp

优化应用内存使用

  • 检查应用是否有内存泄漏
  • 调整 JVM、Node.js 等运行时的内存配置
  • 使用内存分析工具(如 VisualVM、pprof)

CPU 使用率过高

现象:容器占用大量 CPU,影响其他服务。

排查步骤

# 实时查看 CPU 使用
docker stats

# 查看容器内进程
docker top <容器名或ID>

# 进入容器查看详细进程信息
docker exec -it <容器名或ID> top

解决方案

# 限制 CPU 使用
docker run -d --cpus="1.5" myapp # 最多使用 1.5 个 CPU

# 绑定到特定 CPU 核心
docker run -d --cpuset-cpus="0,1" myapp

# 设置 CPU 权重(相对值)
docker run -d --cpu-shares=512 myapp # 默认 1024

磁盘空间不足

现象:Docker 操作失败,提示磁盘空间不足。

排查步骤

# 查看 Docker 磁盘使用
docker system df

# 查看详细信息
docker system df -v

解决方案

# 清理悬空镜像
docker image prune

# 清理所有未使用的镜像
docker image prune -a

# 清理停止的容器
docker container prune

# 清理未使用的卷
docker volume prune

# 清理所有未使用的资源
docker system prune -a --volumes

# 手动删除特定内容
docker rmi $(docker images -f "dangling=true" -q)
docker rm $(docker ps -a -q)

网络问题

容器间无法通信

现象:容器之间无法通过服务名或 IP 互相访问。

排查步骤

# 检查容器网络配置
docker network inspect <网络名>

# 查看容器 IP 地址
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' <容器名或ID>

# 进入容器测试连通性
docker exec -it <容器名或ID> ping <目标容器名或IP>

# 检查 DNS 解析
docker exec -it <容器名或ID> nslookup <服务名>

常见原因及解决方案

1. 使用了默认 bridge 网络

默认 bridge 网络不支持容器名解析:

# 问题:容器无法通过名称访问
docker run --name app1 --network bridge myapp
docker run --name app2 --network bridge myapp
# app2 中 ping app1 失败

# 解决方案:使用自定义网络
docker network create mynet
docker run --name app1 --network mynet myapp
docker run --name app2 --network mynet myapp
# app2 中 ping app1 成功

2. 防火墙阻止

# 检查 iptables 规则
sudo iptables -L -n

# 检查 UFW 状态(Ubuntu)
sudo ufw status

# 如果 UFW 启用,确保 Docker 规则正确
sudo ufw reload

3. 网络驱动问题

# 重新创建网络
docker network rm mynet
docker network create mynet

无法从外部访问容器服务

现象:容器正常运行,但无法通过映射端口从外部访问。

排查步骤

# 确认端口映射
docker port <容器名或ID>

# 确认容器内服务正在运行
docker exec -it <容器名或ID> curl localhost:80

# 检查主机端口监听
netstat -tuln | grep <端口>

# 从主机本地测试
curl localhost:<映射端口>

常见原因及解决方案

1. 服务只监听 localhost

# 检查服务配置,确保监听 0.0.0.0 而非 127.0.0.1
# 错误示例(只监听本地)
docker run -d -p 8080:80 nginx
# 容器内服务监听 127.0.0.1:80

# 正确示例
# 确保 Nginx 配置 listen 80; 而非 listen 127.0.0.1:80;

2. 端口映射配置错误

# 检查端口映射格式
docker inspect --format='{{json .NetworkSettings.Ports}}' <容器名或ID>

# 确保使用正确的端口
docker run -d -p 8080:80 nginx # 主机8080映射到容器80

3. 防火墙阻止外部访问

# 开放防火墙端口
sudo ufw allow 8080/tcp # Ubuntu

# 或使用 firewalld
sudo firewall-cmd --add-port=8080/tcp --permanent
sudo firewall-cmd --reload

DNS 解析失败

现象:容器内无法解析域名。

排查步骤

# 测试 DNS 解析
docker exec -it <容器名或ID> nslookup google.com

# 查看容器 DNS 配置
docker exec -it <容器名或ID> cat /etc/resolv.conf

解决方案

# 指定 DNS 服务器
docker run -d --dns 8.8.8.8 --dns 8.8.4.4 myapp

# 配置 Docker Daemon 默认 DNS
# /etc/docker/daemon.json
{
"dns": ["8.8.8.8", "8.8.4.4"]
}

存储问题

数据卷挂载失败

现象:容器启动失败或数据无法持久化。

常见错误及解决方案

1. 路径不存在

# 错误信息
Error: ... no such file or directory

# 解决方案:创建目录或使用绝对路径
mkdir -p /host/path
docker run -v /host/path:/container/path myapp

2. 权限问题

# 错误信息
Error: ... permission denied

# 解决方案 1:修改主机目录权限
chmod -R 777 /host/path

# 解决方案 2:在容器内使用正确的用户
docker run -v /host/path:/container/path --user $(id -u):$(id -g) myapp

# 解决方案 3:在 Dockerfile 中设置正确的权限
COPY --chown=user:group file /path

3. Windows 路径问题

# Windows 路径格式问题
# 错误
docker run -v C:\Users\data:/app/data myapp

# 正确
docker run -v /c/Users/data:/app/data myapp
docker run -v ${PWD}/data:/app/data myapp

数据卷空间不足

现象:写入数据失败,提示空间不足。

排查步骤

# 查看卷信息
docker volume inspect <卷名>

# 查看 Docker 根目录空间
df -h /var/lib/docker

解决方案

# 清理未使用的卷
docker volume prune

# 迁移 Docker 数据目录
# 1. 停止 Docker
sudo systemctl stop docker

# 2. 迁移数据
sudo rsync -aP /var/lib/docker/ /new/path/docker/

# 3. 配置新路径
# /etc/docker/daemon.json
{
"data-root": "/new/path/docker"
}

# 4. 启动 Docker
sudo systemctl start docker

构建问题

构建缓存问题

现象:构建时没有使用缓存或缓存异常。

解决方案

# 强制不使用缓存
docker build --no-cache -t myapp .

# 使用特定镜像作为缓存源
docker build --cache-from myapp:latest -t myapp:new .

# 清理构建缓存
docker builder prune

# 清理所有构建缓存
docker builder prune -a

构建上下文过大

现象:构建时发送上下文时间过长。

原因:构建上下文包含了不必要的文件(如 node_modules、.git)。

解决方案

创建 .dockerignore 文件排除不需要的文件:

# Git 相关
.git
.gitignore

# 依赖目录
node_modules
vendor
venv
__pycache__

# 构建输出
dist
build
target

# 开发工具
.idea
.vscode
*.swp

# 环境配置
.env
.env.local

# 日志和临时文件
*.log
logs/
tmp/

多阶段构建产物复制失败

现象:多阶段构建时无法从其他阶段复制文件。

# 错误信息
COPY --from=builder: file not found

# 检查阶段性产物是否存在
docker build --target builder -t debug-builder .
docker run --rm debug-builder ls -la /app/build

# 确保路径正确
COPY --from=builder /app/build/dist ./dist

Docker Compose 问题

服务依赖启动顺序问题

现象:服务启动顺序不正确,导致依赖服务未就绪。

解决方案

使用 depends_on 配合健康检查:

services:
app:
depends_on:
db:
condition: service_healthy
redis:
condition: service_started

db:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5

Compose 配置文件格式错误

现象docker compose up 失败,提示配置错误。

排查步骤

# 验证配置文件
docker compose config

# 查看解析后的完整配置
docker compose config --services

常见错误

# 错误 1:使用旧版 version 字段(已废弃)
version: "3.8" # 不再需要

# 正确
services:
app:
image: nginx

# 错误 2:YAML 格式问题
services:
app:
image: nginx
ports:
- "8080: 80" # 错误:冒号后有空格

# 正确
services:
app:
image: nginx
ports:
- "8080:80"

权限问题

Docker 命令需要 sudo

现象:执行 docker 命令提示权限不足。

解决方案

# 将当前用户添加到 docker 组
sudo usermod -aG docker $USER

# 使更改生效
newgrp docker

# 或注销后重新登录

容器内文件权限问题

现象:容器内创建的文件在主机上无法访问,或反之。

解决方案

# 方案 1:使用主机用户 ID 运行容器
docker run --user $(id -u):$(id -g) myapp

# 方案 2:在 Dockerfile 中设置用户
FROM node:18-alpine
RUN adduser -D -u 1000 appuser
USER appuser

# 方案 3:挂载时指定用户
docker run -v ./data:/app/data --user $(id -u):$(id -g) myapp

兼容性问题

Docker Engine v29 API 版本兼容性

重要变化:Docker Engine v29 将最低 API 版本提升到了 v1.44(对应 Docker v25.0+)。这是一个破坏性变更,会导致使用旧版 SDK 或 API 客户端的工具无法连接。

现象:升级 Docker Engine 后,某些工具报错:

Error: client version is too old. Minimum supported API version is 1.44
Error response from daemon: client version 1.43 is too old

常见受影响的工具

工具问题描述解决方案
Traefik 旧版本无法连接 Docker API升级到 Traefik 3.x
Portainer 旧版本无法管理容器升级到最新版本
旧版 Docker SDKAPI 调用失败更新 SDK 版本
自定义脚本API 版本不兼容更新 API 版本号

排查步骤

# 查看 Docker API 版本信息
docker version --format '{{.Server.APIVersion}}'

# 查看客户端支持的 API 版本
docker version --format '{{.Client.APIVersion}}'

# 检查 API 版本范围
docker version
# Server Version: 29.x.x
# API Version: 1.52
# Min API Version: 1.44 ← 最低支持的版本

解决方案

方案 1:升级客户端工具

这是推荐的解决方案,确保所有工具都使用支持新 API 版本的版本。

方案 2:设置 API 版本环境变量

临时解决方案,强制客户端使用特定 API 版本:

# 设置客户端使用的 API 版本
export DOCKER_API_VERSION=1.44

# 在命令中临时指定
DOCKER_API_VERSION=1.44 docker ps

方案 3:回退 Docker Engine 版本

如果无法立即升级客户端工具,可以临时回退 Docker Engine 版本:

# 查看可用版本
apt list -a docker-ce

# 安装特定版本(例如 v28.x)
sudo apt install docker-ce=5:28.x.x-1~ubuntu.24.04~noble

注意:回退版本不是长期解决方案,应该尽快升级客户端工具。

containerd 镜像存储迁移问题

现象:升级到 Docker Engine v29 后,现有镜像和容器"消失"。

原因:Docker Engine v29 对新安装默认使用 containerd 镜像存储。切换存储后端会隐藏之前存储的内容,数据实际仍在磁盘上。

解决方案

# 方案 1:切换回 overlay2 存储驱动
# 编辑 /etc/docker/daemon.json
{
"storage-driver": "overlay2"
}
# 重启 Docker
sudo systemctl restart docker

# 方案 2:重新拉取镜像
# 在使用 containerd 存储后,重新拉取需要的镜像
docker pull nginx:latest

# 方案 3:使用 docker save/load 迁移
# 在切换前导出镜像
docker save -o backup.tar nginx:latest
# 切换后导入
docker load -i backup.tar

cgroup v1 弃用警告

现象:Docker 启动时出现 cgroup v1 弃用警告。

DEPRECATION: cgroup v1 is deprecated and will be removed in a future release

解决方案

迁移到 cgroup v2:

# 检查当前 cgroup 版本
mount | grep cgroup

# 编辑 GRUB 配置启用 cgroup v2
sudo vi /etc/default/grub
# 添加内核参数
GRUB_CMDLINE_LINUX="... systemd.unified_cgroup_hierarchy=1"

# 更新 GRUB
sudo update-grub # Debian/Ubuntu
sudo grub2-mkconfig -o /boot/grub2/grub.cfg # RHEL/CentOS

# 重启系统
sudo reboot

# 验证 cgroup v2 已启用
cat /proc/cgroups
# 如果看到 "cgroup2" 则表示成功

性能问题

文件描述符限制变化(Docker Engine v29)

重要变化:从 Docker Engine v29 开始,容器中打开文件描述符的限制(ulimit -n)默认值从 1048576 改为 1024。这是为了与 systemd 的默认行为保持一致,防止程序因过高的 ulimit 设置而消耗过多内存。

现象:某些应用程序可能因文件描述符不足而出现错误:

Too many open files
Unable to accept new connection: accept4: too many open files

排查步骤

# 查看容器当前的 ulimit 设置
docker exec <容器名> sh -c "ulimit -n"

# 查看主机级别的限制
cat /proc/sys/fs/file-max

解决方案

# 方式 1:运行时指定 ulimit
docker run -d --ulimit nofile=65535:65535 myapp

# 方式 2:在 Docker Compose 中配置
services:
app:
image: myapp
ulimits:
nofile:
soft: 65535
hard: 65535
# 方式 3:在 daemon.json 中配置默认值
{
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Soft": 65535,
"Hard": 65535
}
}
}

影响场景

应用类型可能受影响建议
Web 服务器(高并发)增加文件描述符限制
数据库可能根据连接数调整
缓存服务(Redis)可能根据最大连接数调整
后台任务通常不影响保持默认

容器运行缓慢

排查步骤

# 查看资源使用
docker stats

# 查看容器进程
docker top <容器名或ID>

# 检查 IO 等待
docker exec -it <容器名或ID> top

常见原因及优化

  1. 资源限制过严:适当增加 CPU 和内存限制
  2. 磁盘 IO 瓶颈:使用数据卷而非绑定挂载,或使用 tmpfs
  3. 网络延迟:检查网络配置,避免不必要的网络跳转
  4. 镜像过大:优化镜像大小,减少层数

Docker Desktop 性能问题(macOS/Windows)

现象:Docker Desktop 占用大量资源。

解决方案

  1. 调整资源配置:Settings -> Resources
  2. 减少分配给 Docker 的 CPU 和内存
  3. 定期清理未使用的资源
  4. 考虑使用 WSL 2 后端(Windows)

日志问题

日志占用过多磁盘空间

解决方案

# 查看日志文件大小
du -sh /var/lib/docker/containers/*/*-json.log

# 配置日志大小限制
# /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}

# 单个容器设置
docker run -d \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
nginx

无法查看容器日志

现象docker logs 无输出或报错。

排查步骤

# 检查日志驱动
docker inspect --format='{{.HostConfig.LogConfig.Type}}' <容器名或ID>

# 如果使用 journald/syslog,检查对应服务
# 如果使用 none,日志被禁用

调试技巧

使用调试容器

当目标容器缺少调试工具时,可以使用专用调试容器:

# 网络调试
docker run --rm -it --network container:<目标容器> nicolaka/netshoot

# 文件系统调试
docker run --rm -it --pid=container:<目标容器> --cap-add SYS_PTRACE alpine

# 进入已停止容器的文件系统
docker export <容器ID> | tar -C /tmp/extract -xvf -

查看容器详细状态

# 查看完整配置
docker inspect <容器名或ID>

# 查看运行状态
docker inspect --format='{{json .State}}' <容器名或ID> | jq

# 查看网络配置
docker inspect --format='{{json .NetworkSettings}}' <容器名或ID> | jq

# 查看挂载点
docker inspect --format='{{json .Mounts}}' <容器名或ID> | jq

实时监控

# 实时资源使用
docker stats

# 实时事件
docker events

# 容器内进程
watch -n 1 'docker top <容器名或ID>'

常用故障排除清单

容器无法启动

# 1. 查看容器状态
docker ps -a
docker inspect <容器名或ID>

# 2. 查看日志
docker logs --tail 100 <容器名或ID>

# 3. 检查退出码
docker inspect --format='{{.State.ExitCode}}' <容器名或ID>

# 4. 尝试交互运行
docker run -it --entrypoint sh <镜像>

# 5. 检查资源限制
docker stats --no-stream

网络问题

# 1. 检查网络配置
docker network ls
docker network inspect <网络名>

# 2. 检查容器 IP
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' <容器名或ID>

# 3. 测试连通性
docker exec -it <容器名或ID> ping <目标>

# 4. 检查端口
docker port <容器名或ID>
netstat -tuln | grep <端口>

# 5. 检查 DNS
docker exec -it <容器名或ID> nslookup <域名>

存储问题

# 1. 检查磁盘空间
docker system df
df -h

# 2. 检查卷信息
docker volume ls
docker volume inspect <卷名>

# 3. 检查挂载点
docker inspect --format='{{json .Mounts}}' <容器名或ID>

# 4. 清理未使用资源
docker system prune -a --volumes

参考资源