「Docker避坑指南」系列第③篇,聚焦存储驱动和权限管理的高频问题。
有天早上刚到公司,告警就响了——某台生产服务器磁盘使用率瞬间飙到100%,导致服务挂了一片。经过一番排查,发现罪魁祸首是Docker,它把磁盘空间塞满了,其中光是容器日志文件就占了将近200GB。
处理完这类故障后,你会发现Docker的存储和权限问题,其复杂程度和带来的麻烦一点也不比网络问题少。今天,我就把自己在实践中踩过的4个典型坑整理出来,每个都是真实的案例,希望帮你提前避雷。对于这类基础设施稳定性问题,也可以在云栈社区的运维板块找到更多讨论。
坑①:Docker把磁盘吃光了,怎么快速排查和清理
排查第一步:搞清楚Docker占了多少
要摸清家底,第一道命令就是:
docker system df
它的输出会清晰告诉你镜像、容器、数据卷各自占用了多少空间,以及其中有多少是可以回收的:
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 50 10 25GB 20GB (80%)
Containers 100 5 50GB 45GB (90%)
Local Volumes 30 5 200GB 180GB (90%)
看到这个输出,问题出在哪里基本心里就有数了。
排查第二步:找出最大的日志文件
容器日志是名副其实的磁盘空间“杀手”。一些日志量巨大的服务,跑上几天就能轻松产生几十GB的日志文件。该怎么快速定位它们呢?
sudo du -sh /var/lib/docker/containers/*/*-json.log | sort -rh | head -20
如果输出中某个文件的尺寸显示为几十GB,那么它很可能就是导致磁盘爆满的元凶。
清理方案:
清理的命令分几个层级,从局部到全局:
# 清理停止的容器
docker container prune -f
# 清理未使用的镜像(包括中间层)
docker image prune -a -f
# 清理未使用的数据卷
docker volume prune -f
# 核武器:一键清理所有(确认没有需要保留的数据再用)
docker system prune -a --volumes -f
治本措施:在 daemon.json 里限制日志大小
清理只是权宜之计,要从根本上解决问题,必须从源头限制日志的体积。修改Docker守护进程的配置文件是最有效的方法:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
这段配置意味着:每个容器最多只保留3个日志文件,每个文件最大100MB,总共不超过300MB。超出限制后,旧的日志文件会被自动滚动覆盖。
配置完成后需要重启Docker服务才能生效。但请注意,此配置只对新建的容器生效,对于已有的容器,你需要重建它们才能应用新的日志策略。
坑②:存储驱动用的devicemapper,容器启动慢、IO高
这个坑主要出现在老版的CentOS 7系统上。在这些系统上,Docker默认使用 devicemapper 作为存储驱动,其性能表现往往不尽如人意:容器启动可能需要5-10秒,磁盘IO很高,有时甚至会莫名其妙地卡死。
切换到overlay2是当前的正确选择,性能提升会非常明显:
| 存储驱动 |
容器启动时间 |
IO性能 |
推荐度 |
| overlay2 |
1-2秒 |
非常好 |
★★★★★ |
| devicemapper |
5-10秒 |
差 |
★★ |
| aufs |
2-3秒 |
好 |
★★★ |
切换步骤:
切换存储驱动需要谨慎操作,因为它会清空现有的Docker数据(镜像、容器等)。
# 1. 停止Docker服务
sudo systemctl stop docker
# 2. 备份数据(非常重要,切换会清空现有数据)
sudo cp -r /var/lib/docker /var/lib/docker.bak
# 3. 修改Docker守护进程配置
sudo nano /etc/docker/daemon.json
# 添加或修改为以下内容:
# {
# "storage-driver": "overlay2"
# }
# 4. 清空旧的存储数据
sudo rm -rf /var/lib/docker/*
# 5. 启动Docker服务
sudo systemctl start docker
# 6. 验证切换是否成功
docker info | grep “Storage Driver”
# 预期输出:Storage Driver: overlay2
注意事项:
- 数据丢失风险:切换存储驱动会丢失所有本地存储的镜像和容器。切换前务必使用
docker save 命令导出你需要保留的重要镜像。
- 内核要求:overlay2需要内核版本在3.10以上,CentOS 7.3及以上版本都支持。
- 后续操作:切换成功后,你需要重新拉取(pull)或加载(load)所需的镜像。完成之后,容器启动速度和IO性能的提升会非常直观。
坑③:普通用户运行docker命令必须加sudo
这是每个Docker新手几乎都会碰到的权限问题。每次执行Docker命令都要输入sudo密码,不仅繁琐,还打断了工作流。
原因分析:
问题的根源在于Docker守护进程(daemon)是通过一个Unix套接字文件 /var/run/docker.sock 来接收命令的。默认情况下,这个文件的访问权限只授予了root用户和docker用户组的成员。
解决方法:
将你的普通用户添加到docker用户组中即可。
# 把当前用户加入docker用户组
sudo usermod -aG docker $USER
# 刷新当前会话的组权限信息(无需重启系统)
newgrp docker
# 验证权限
docker ps # 现在应该不需要sudo了
注意一个常见的错误:
很多人在执行完 usermod 命令后直接测试,发现依然报 permission denied 错误。这是因为你当前的shell会话还没有加载新的用户组信息。你需要执行 newgrp docker 来刷新当前会话,或者最简单的方法是退出当前登录会话并重新连接。
# 如果newgrp命令无效,直接退出并重新SSH登录即可
exit
# 重新连接服务器后,权限就会生效
安全提示:
请务必注意,加入docker用户组等同于授予了该用户root权限。因为通过Docker可以对系统进行很多底层操作。因此,不要随意将用户添加到docker组。在生产服务器上,出于安全审计和权限控制的考虑,建议保持使用sudo来执行Docker命令,这样所有操作都会有记录,便于问题追溯和权责划分。
坑④:容器内创建的文件,宿主机上是root权限,普通用户改不了
问题描述:
这是一个非常典型的文件权限问题。当你将宿主机的一个目录挂载到容器中,容器内部(默认以root用户运行)在该目录下创建了文件。之后在宿主机上查看这个文件时,你会发现它的所有者是root,导致宿主机上的普通用户无法修改甚至删除它。
docker run -v /home/user/data:/data ubuntu touch /data/test.txt
ls -l /home/user/data/test.txt
# 输出:-rw-r--r-- 1 root root 0 Feb 22 test.txt (属主是root)
原因分析:
容器内部默认以root用户(UID 0)的身份运行进程。因此,它在挂载卷内创建的文件,其所有者自然是root。当这个文件通过卷映射展现在宿主机上时,其UID 0对应的是宿主机的root用户。
三种解决方案:
方案一:启动容器时指定用户(开发环境最方便)
在运行容器时,通过 -u 参数直接指定以当前宿主机用户的UID和GID来运行容器内的进程。
# 用当前用户的UID和GID运行容器
docker run -u $(id -u):$(id -g) -v /home/user/data:/data ubuntu touch /data/test.txt
# 现在宿主机上查看,文件的属主就是当前用户了
这种方式简单快捷,非常适合本地开发调试。
方案二:在Dockerfile里创建和宿主机UID相同的用户(生产环境推荐)
这是一种更规范、更适合构建生产镜像的方法。在Dockerfile中动态创建一个与宿主机目标用户UID/GID相同的用户,并切换至此用户运行。
FROM ubuntu:20.04
ARG USER_ID=1000
ARG GROUP_ID=1000
RUN groupadd -g ${GROUP_ID} appuser && \
useradd -u ${USER_ID} -g appuser -m appuser
USER appuser
构建镜像时,传入宿主机用户的UID和GID作为构建参数:
docker build --build-arg USER_ID=$(id -u) --build-arg GROUP_ID=$(id -g) -t myapp .
方案三:容器跑完之后改权限(临时处理用)
如果文件已经生成,权限不对,一个快速的补救方法是在宿主机上直接修改目录或文件的所有者。
sudo chown -R $USER:$USER /home/user/data/
如何选择?日常开发用方案一最省事;构建生产环境镜像推荐用方案二,最为规范和安全;遇到已有文件权限问题时,用方案三做临时处理。
本篇小结
表面上看这是四个独立的问题,但它们都指向同一个深层次原因:对Docker底层的工作机制不够了解。
- 磁盘爆满 → 不了解日志文件的无限增长机制和资源回收方法。
- devicemapper性能差 → 不了解不同存储驱动的特性与适用场景。
- sudo权限问题 → 不了解Docker守护进程套接字(socket)的权限控制机制。
- 文件权限混乱 → 不了解容器内用户UID与宿主机用户的映射关系。
一旦理解了背后的原理,解决起来其实都有清晰的路径。希望这些从真实运维中总结的经验,能帮助你更平稳地使用Docker。如果你在实践容器化应用时遇到其他棘手问题,也欢迎到云栈社区交流讨论。
本文是「Docker避坑指南」系列第③篇。第④篇将讲解生产环境中的资源限制与日志管理,并附上一份完整的生产级 daemon.json 配置清单,建议收藏备用。