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 SDK | API 调用失败 | 更新 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
常见原因及优化:
- 资源限制过严:适当增加 CPU 和内存限制
- 磁盘 IO 瓶颈:使用数据卷而非绑定挂载,或使用 tmpfs
- 网络延迟:检查网络配置,避免不必要的网络跳转
- 镜像过大:优化镜像大小,减少层数
Docker Desktop 性能问题(macOS/Windows)
现象:Docker Desktop 占用大量资源。
解决方案:
- 调整资源配置:Settings -> Resources
- 减少分配给 Docker 的 CPU 和内存
- 定期清理未使用的资源
- 考虑使用 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