Docker 疑难杂症¶
本文汇总 Docker 使用中常见的故障现象、原因分析与解决命令。
🟢存储与空间¶
du · df · thin_check · overlay2 · inode。磁盘、inode、device-mapper 与挂载相关故障。
flowchart TD
A[容器报空间不足] --> B{df -Th 磁盘满?}
B -->|是| C[清理日志 / 迁移数据目录]
B -->|否| D{df -i inode 满?}
D -->|是| E[inode64 / large_dir 重新挂载]
D -->|否| F[检查容器默认大小 / overlay2 占用]
1. 迁移 Docker 存储目录¶
默认情况下 Docker 将容器存放在 /var/lib/docker,该目录过大会导致容器无法启动(ERROR: cannot create temporary directory!)。
注意事项
迁移时用 mv 直接移动,或用 cp 复制时须保留权限和属性,否则可能因普通用户运行容器而出现权限问题(如无法使用 /tmp)。尽量不用软链接,部分编排系统(如 k8s)不支持。
2. Docker 设备空间不足¶
现象一:物理磁盘真的满了¶
解决:清理无效数据(清除日志),或将 Docker 数据迁移到大分区。
# 显示哪些容器目录具有最大的日志文件
du -d1 -h /var/lib/docker/containers | sort -h
# 清除指定容器日志文件内容
cat /dev/null > /var/lib/docker/containers/container_id/container_log_name
现象二:容器默认大小限制(CentOS7 默认 10G)¶
日志中提示 No space left on device:
或修改 systemd 启动文件:
sudo systemctl stop docker
sudo rm -rf /var/lib/docker
sudo vim /usr/lib/systemd/system/docker.service
# 将 ExecStart=/usr/bin/dockerd 改为:
ExecStart=/usr/bin/dockerd --storage-opt dm.basesize=20G
sudo systemctl start docker
sudo systemctl daemon-reload
现象三:inode 节点数满¶
ext3 使用 inode table 存储 inode 信息,xfs 使用 B+ tree;默认 B+ tree 只用前 1TB 空间,写满后无法写入 inode。
# 查看 inode 使用情况
sudo df -i
# 重新挂载,指定 inode64 扩展到整个文件系统
sudo mount -o remount -o noatime,nodiratime,inode64,nobarrier /dev/vda1
现象四:ext4 目录文件数过多¶
ext4 基于 BTree 索引,默认层级为 2,约限制目录下文件数在 2k–3kw 以内;未开启 large_dir 时会报 No space left on device。
# 排查思路
df -ih # 检查磁盘与 inode 是否足够
fallocate -l 10G /cache/test # 排除 mount-bind 缺陷
fdisk -l # 确认文件系统类型
dmesg # 查看内核日志,定位 htree level 报错
tune2fs -l # 确认是否开启 large_dir
tune2fs -O large_dir /dev/sdd # 开启 large_dir
4. Docker 容器文件损坏¶
容器无法关闭、重启、删除,报错:
sudo systemctl stop docker
sudo rm -rf /var/lib/docker/containers
sudo thin_check /var/lib/docker/devicemapper/devicemapper/metadata
sudo thin_check --clear-needs-check-flag /var/lib/docker/devicemapper/devicemapper/metadata
sudo systemctl start docker
10. Docker NFS 挂载报错¶
Python 程序对 NFS 挂载文件加锁时,报 OSError: [Errno 9] Bad file descriptor。
with open(self.mount(path), 'rb') as fileobj:
fcntl.flock(fileobj, fcntl.LOCK_EX)
data = fileobj.read()
原因:flock() 在旧内核中对 NFS 支持受限。Linux 2.6.12 起 NFS 客户端才支持 flock()(以整文件字节范围锁模拟)。该问题也曾是 RedHat 内核错误,在 kernel-3.10.0-693.18.1.el7 中修复。对 NFSv3/NFSv4 需升级内核版本。
26. Docker 容器磁盘不足¶
/var/lib/docker/overlay2 占用过大。该目录以 md5 命名,是 Docker 使用的文件存储驱动目录。
层目录含义:
diff:差分信息,容器的可读可写层,初始为空。merged:容器运行时的挂载点。upperdir:容器层。lowerdir:初始镜像层。
# 查看哪个容器层占用大量磁盘
sudo du -sh /var/lib/docker/overlay2/* | grep G | sort -rn
# 查找对应层所属容器
docker ps -q | \
xargs docker inspect --format '{{.State.Pid}}, {{.Id}}, {{.Name}}, {{.GraphDriver.Data.WorkDir}}' | \
grep xxx
# 查看容器的层数信息(从低到高)
docker inspect xxx | grep -E "LowerDir|UpperDir|MergedDir|WorkDir"
27. Docker 挂载无法删除¶
原因是服务启动时把文件硬挂载进容器:
cat /proc/mounts | grep "config"
# /dev/sda /opt/app/config/config-docker-app.yml ext4 rw,relatime,data=ordered 0 0
🟢网络与端口¶
docker0 · default-address-pools · firewalld · IPv6。容器互通、网段冲突与端口限制。
8. Docker 容器网络互通¶
Nginx 容器代理本机服务时一直 502,原因是容器中的 localhost 指容器自身而非宿主机。
解决:改为宿主机 IP(如 docker0 的 172.17.0.1):
或让容器与宿主共用网络,此时容器的 localhost 就是宿主机的 localhost:
11. Docker 使用默认网段¶
多个服务有时能互通、有时不能,原因是内部私有地址网段不一致(如 172.17-172.31 与 192.169.0-192.168.224)。手动指定 Docker 启动网段即可。
{
"registry-mirrors": ["https://vec0xydj.mirror.aliyuncs.com"],
"default-address-pools": [{"base": "172.17.0.0/12", "size": 24}]
}
完整配置见下文第 5 条「Docker 容器优雅重启」。
18. Docker 绑定到 IPv6 上¶
系统禁用 IPv6 后启动 docker-compose 报错:
Error starting userland proxy: listen tcp6 [::]:80: socket: address family not supported by protocol.
20. Docker 端口网络限制¶
部署架构 nginx(80) -> web1(8080) -> web2(8081) 时,Nginx 报:
原因:防火墙过滤。开放端口或关闭防火墙。
# 检查开放端口
sudo firewall-cmd --permanent --zone=public --list-ports
# 开启需要路由的端口
sudo firewall-cmd --permanent --zone=public --add-port=8080/tcp
sudo firewall-cmd --permanent --zone=public --add-port=8081/tcp
firewall-cmd --reload
23. Docker 不使用默认网段¶
内部规划网段与 dockerd 默认网段冲突导致端口不通(Connection refused)。手动指定网段并重启服务:
sudo cat /etc/docker/daemon.json
# { "default-address-pools": [{"base": "192.168.100.0/20", "size": 24}] }
sudo systemctl restart docker
# 验证
ip a
docker network inspect app | grep Subnet
若报 could not find an available, non-overlapping IPv4 address pool,需合理划分子网:
# 给定 10.210.200.0/24,每个子网 16 个 IP,则 size=28
sudo cat /etc/docker/daemon.json
# { "default-address-pools": [{"base": "10.210.200.0/24", "size": 28}] }
base指定划分子网的起始网段,size指定每个子网可用的 IP 数量。
33. 网桥转发被 Docker 默认策略阻断¶
用 ip netns + bridge 做网络实验时,各命名空间已接入 br0 却仍 ping 不通。原因是系统为 bridge 开启了 iptables 功能,所有经过 br0 的数据包都受 iptables 规则限制;而 Docker 出于安全考虑把 filter 表 FORWARD 链默认策略设成了 DROP,不符合 Docker 规则的包无法被转发。
# 方法一:关闭系统 bridge 的 iptables 功能
echo 0 > /proc/sys/net/bridge/bridge-nf-call-iptables
# 方法二(推荐,不影响 Docker):为 br0 放行 FORWARD
iptables -A FORWARD -i br0 -j ACCEPT
🟢镜像与仓库¶
16. Docker 删除镜像报错¶
docker rmi 3ccxxxx2e862
# Error response from daemon: conflict: unable to delete 3ccxxxx2e862 (cannot be forced) - image has dependent child images
原因是存在其他镜像(TAG)引用了该镜像。先查询依赖关系,再根据 TAG 删除:
# 查询依赖
docker image inspect --format='{{.RepoTags}} {{.Id}} {{.Parent}}' \
$(docker image ls -q --filter since=<image_id>)
# 根据 TAG 删除
docker rmi -f c565xxxxc87f
# 删除悬空镜像
docker rmi $(docker images --filter "dangling=true" -q --no-trunc)
21. Docker 无法获取镜像¶
原因:镜像 TAG 写错(应为 0.0.10)。出现该报错通常代表镜像不存在。
24. Docker 添加私有仓库¶
29. Docker 容器导入异常¶
原因是导入命令组合错误,注意配对使用:
🟢compose 与命令¶
-p 项目名 · -it · COMPOSE_HTTP_TIMEOUT · YAML 引号。串台、TTY、超时与变量解析。
12. Docker 服务启动串台¶
在不同目录用 docker-compose 启动两组服务时会串台,导致无法同时启动。原因是 compose 会给容器加 label,其中 com.docker.compose.project 取启动配置文件所在目录的最底层子目录名:
解决:调整目录结构,或用 -p 手动指定项目名。
13. Docker 命令调用报错¶
CI 中执行脚本报错,提示设备不是 tty:
| 参数 | 解释 |
|---|---|
-i / --interactive |
即使没有附加也保持 STDIN 打开;执行命令需要开启 |
-t / --tty |
分配一个伪终端 |
原因:-t 分配伪 TTY,而 CI 执行 job 时并非在 TTY 终端中,故报错。去掉 -t 即可。
14. Docker 定时任务异常¶
Crontab 中通过 Docker 备份 MySQL,备份结果为空:
0 */6 * * * \
docker exec -it <container_name> sh -c \
'exec mysqldump --all-databases -uroot -ppassword ......'
原因:Crontab 执行不是交互式的,-i 多余。需要回显用 -t,需要交互式会话用 -i。
15. Docker 变量使用引号¶
compose 中添加环境变量时引号处理容易踩坑:
原因:Compose 解析 YAML 时把引号也作为值的一部分,TEST_VAR="test" 实际被解析为 'TEST_VAR="test"'。结论:能不用引号就不用引号。若值形如日期(2022-01-01),使用 '/" 引起来可避免被 yaml.load 当作 date 类型。
19. Docker 容器启动超时¶
ERROR: for xxx UnixHTTPConnectionPool(host='localhost', port=None): Read timed out. (read timeout=70)
ERROR: An HTTP request took too long to complete. Retry with --verbose to obtain debug information.
按提示加大超时环境变量:
另需排查 I/O 瓶颈,例如 vscode 远程搜索目录产生的 rg 进程占满 I/O:
28. Docker 数组环境变量¶
compose 中通过环境变量传数组,程序读取到的是字符串。
from app.config import get_config
get_config('APP_CONFIG_EMAIL_LIST') # ['a', 'b']
type(get_config('APP_CONFIG_EMAIL_LIST')) # <class 'list'>
🟢权限与运行¶
3. Docker 缺共享链接库¶
docker-compose --version 报错:
error while loading shared libraries: libz.so.1: failed to map segment from shared object: Operation not permitted
原因是 Docker 命令需要对 /tmp 目录有访问权限,重新挂载即可:
6. Docker 容器无法删除¶
docker rm -f 报 Conflict, cannot remove the default name of the container,且 ps -ef 查不到对应进程。原因通常是容器启动后主机重启且未优雅终止容器,残留文件让系统认为旧容器仍存在。
# 删除对应容器文件夹
sudo rm -rf /var/lib/docker/containers/f8e8c3...65720
# 重启服务
sudo systemctl restart docker.service
9. Docker 容器总线错误¶
容器内运行程序报 Bus error (core dumped),原因是 shm 分区太小导致共享内存不足。未设置 --shm-size 时默认分配 64M,跑 PyTorch 多进程任务容易不足。
# 查看确认
df -TH
# 启动时指定 shm 大小(单位 b,k,m,g)
docker run -it --rm --shm-size=200m pytorch/pytorch:latest
# docker-compose 中配置
# shm_size: '2gb'
Note
容器内磁盘空间不足同样会导致 bus error,需清理文件或分配更大的磁盘空间。
17. Docker 普通用户切换¶
用普通用户启动 Nginx 时报权限错误:
nginx: [alert] could not open error log file: open() "/var/log/nginx/error.log" failed (13: Permission denied)
[emerg] 23#23: mkdir() "/var/cache/nginx/client_temp" failed (13: Permission denied)
解决:将 Nginx 需要的文件/目录配置到无权限问题的路径。
user www-data;
worker_processes 1;
error_log /data/logs/master_error.log warn;
pid /dev/shm/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
gzip on;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
client_body_temp_path /tmp/client_body;
fastcgi_temp_path /tmp/fastcgi_temp;
proxy_temp_path /tmp/proxy_temp;
scgi_temp_path /tmp/scgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
include /etc/nginx/conf.d/*.conf;
}
22. Docker 使容器不退出¶
服务启动异常且 restart: on-failure 时容器无限重启,不便排查。可让容器 hang 住:
30. Docker 日志输出授权¶
supervisor 启动 nginx 报 EACCES:
INFO spawnerr: unknown error making dispatchers for 'nginx': EACCES
INFO gave up: nginx entered FATAL state, too many start retries too quickly
原因:容器中服务使用普通用户,而日志文件属主是 root。另在类 K8S 平台需输出到终端时,/dev/fd/1、/dev/stdout 可能对普通用户不可写,需提前授权:
31. Docker 文件权限问题¶
# Rust 程序报错
OS can't spawn a new worker thread
# Tomcat 服务报错
Cannot find /usr/local/tomcat/bin/setclasspath.sh
原因是权限异常,解决方式为更新 runc 或给容器加特权开关:
32. Docker 提示权限受限¶
容器内以 root 运行仍报 Operation not permitted:
OpenBLAS blas thread init: pthread create failed for thread 1 of 16: Operation not permitted
ls: cannot access '/etc/docker-entrypoint-initdb.d/': Operation not permitted
原因:宿主机内核版本过低,新版本镜像使用的特性不受支持。
🟢守护进程与系统配置¶
live-restore · systemctl reload|restart dockerd · localtime。优雅重启与时间同步。
5. Docker 容器优雅重启¶
默认守护进程终止时会关闭正在运行的容器。自 Docker-ce 1.12 起,可在配置中添加 live-restore 使容器在守护进程不可用时保持运行(Windows 平台暂不支持)。
# 在守护进程停机期间保持容器存活
sudo dockerd --live-restore
# 重载(相当于发送 SIGHUP 给 dockerd)
sudo systemctl reload docker
# 网络设置需要 restart 才能生效
sudo systemctl restart docker
完整示例:
{
"registry-mirrors": ["https://vec0xydj.mirror.aliyuncs.com"],
"experimental": true,
"default-runtime": "nvidia",
"live-restore": true,
"runtimes": {
"nvidia": {
"path": "/usr/bin/nvidia-container-runtime",
"runtimeArgs": []
}
},
"default-address-pools": [
{
"scope": "local",
"base": "172.17.0.0/12",
"size": 24
}
]
}
25. Docker 解决时间同步¶
容器内时间为 UTC,宿主机为 CST,相差 8 小时。
🟢语言与编码¶
7. Docker 容器中文异常¶
MySQL 容器查询中文字段异常,locale -a 只显示 C、C.UTF-8、POSIX。原因是使用 POSIX 字符集,不支持中文。
# 永久解决
docker run --name some-mysql \
-e MYSQL_ROOT_PASSWORD=my-secret-pw \
-d mysql:tag --character-set-server=utf8mb4 \
--collation-server=utf8mb4_unicode_ci
Tip
K8S 进入 pod 不能输入中文时也可用同样方法解决。