CentOS服务器磁盘爆满排查:Docker日志清理与存储管理实战

📅 2026/8/26 7:37:52
CentOS服务器磁盘爆满排查:Docker日志清理与存储管理实战
1. 项目概述当磁盘爆满成为运维的日常“磁盘空间不足”这个报错对于任何一个在服务器上跑过生产环境的人来说都太熟悉了。它不像内存溢出那样瞬间致命却像慢性病一样悄无声息地侵蚀着系统的健康直到某一天你的应用无法写入日志数据库无法创建临时表甚至系统连SSH都登录不上你才惊觉/dev/vda1这个根分区已经亮起了刺眼的100%红灯。尤其是在我们广泛使用Docker容器化部署的今天这个问题变得尤为高频和隐蔽。很多人第一反应是“删点日志”或者“清理下缓存”但往往治标不治本过几天问题又卷土重来。今天我就结合一次真实的线上故障处理从头到尾拆解一下CentOS系统下特别是由Docker引发的磁盘空间占满问题的系统性排查与根治思路。这不仅仅是一次清理操作更是一次对服务器存储管理的深度体检。我们面对的场景很典型一台跑着多个Docker容器的CentOS 7.9服务器df -h命令显示/dev/vda1挂载的根目录使用率100%可用空间为0。业务开始出现写入失败的错误。盲目删除文件是危险的因为你可能删掉关键数据或正在被进程打开的文件导致服务异常。我们的目标是像外科手术一样精准定位“病因”哪些文件/目录在疯狂增长评估“病情”是否可安全清理并实施“治疗方案”清理、转移或扩容同时建立“预防机制”避免问题复发。这个过程适用于任何Linux发行版但我们会聚焦在CentOS和Docker这个经典组合上。2. 核心思路从宏观到微观的排查路径处理磁盘满的问题最忌讳的就是无头苍蝇似的乱删。一个清晰的排查路径至关重要。我的思路通常是自上而下从文件系统整体视角逐步深入到具体的罪魁祸首文件。2.1 第一步确认整体磁盘使用情况首先我们需要一个全局视野。使用df -h命令。这个命令大家都会用但关键要看懂输出里的细节。df -h你会看到类似下面的输出文件系统 容量 已用 可用 已用% 挂载点 /dev/vda1 50G 50G 0B 100% / devtmpfs 3.9G 0 3.9G 0% /dev tmpfs 3.9G 0 3.9G 0% /dev/shm tmpfs 3.9G 1.1M 3.9G 1% /run tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup这里明确告诉我们问题是出在根分区/dev/vda1上。可用为0B已用100%。-h参数让数据以人类可读的G、M单位显示更直观。注意有时候df显示的空间和实际文件大小之和对不上这可能是因为文件系统保留了约5%的空间给root用户使用tune2fs -l /dev/vda1 | grep ‘Reserved block count’查看或者是存在已经被删除但仍有进程在占用的文件这些文件不会释放空间直到进程结束。这是我们后续需要排查的一个点。2.2 第二步定位占用空间的大目录知道了是根分区满了接下来就要找是哪个“坏小子”目录吃掉了大部分空间。这里祭出神器dudisk usage命令。但直接du -sh /*可能会因为权限问题有些目录报错而且输出杂乱。我常用的组合命令是cd / sudo du -sh * 2/dev/null | sort -rh | head -20这个命令分解一下cd /切换到根目录。sudo因为有些目录需要root权限才能查看。du -sh *以人类可读的格式-h汇总-s计算根目录下每个一级目录和文件的大小。2/dev/null将权限错误等标准错误输出重定向到“黑洞”让屏幕只显示有效结果界面更清爽。sort -rh逆序-r按人类可读的数字大小-h排序。-h参数能正确排序 10K, 1M, 2G 这样的单位。head -20只显示最大的前20个。运行后你可能会看到像/var(30G)/usr(10G)/home(5G) 这样的结果。通常在Docker环境中/var目录是头号嫌疑犯因为Docker默认将镜像、容器、卷数据等都存放在/var/lib/docker。2.3 第三步深入嫌疑目录揪出具体文件假设我们发现/var最大就进入/var目录重复上面的操作cd /var sudo du -sh * 2/dev/null | sort -rh | head -20很可能你会看到lib目录独占鳌头。再进入/var/lib最终定位到/var/lib/docker异常庞大。至此我们初步锁定了“案发现场”。但Docker目录下子目录很多我们需要知道具体是overlay2存储驱动目录、containers容器元数据、volumes数据卷还是images镜像层占用了空间。继续使用du深入cd /var/lib/docker sudo du -sh * 2/dev/null | sort -rh | head -102.4 第四步针对Docker的专项检查除了用du查看磁盘用量Docker本身也提供了一些命令来查看其资源占用情况这能给我们另一个维度的信息。查看镜像、容器、数据卷的磁盘占用docker system df -v这个命令非常有用。-v参数会显示详细信息包括每个镜像、每个容器、每个卷的具体大小和可回收空间。它能清晰告诉你哪些镜像是悬空的dangling哪些容器虽然停止了但还占着文件系统哪些数据卷已经不再使用。查看容器日志大小 Docker容器的标准输出STDOUT/STDERR日志默认会由json-file或journald驱动收集存放在宿主机的特定位置。长时间运行的、日志输出频繁的容器其日志文件可能巨大无比。日志位置通常在/var/lib/docker/containers/容器ID/容器ID-json.log。我们可以用命令快速找出日志最大的容器find /var/lib/docker/containers/ -name “*.log” -exec ls -lh {} \; | awk ‘{ print $9 “: ” $5 }’ | sort -hr -k2 | head -10通过以上四步我们从“磁盘满了”这个现象精准地定位到了可能是“Docker的某个特定容器日志”或“某个悬空镜像层”导致了问题。这个排查思路是通用的即使不是Docker是MySQL的binlog、是Nginx的access_log、是某个应用生成的临时文件你都能用这套方法找到它。3. 实战清理安全高效地释放空间定位到问题后就要开始清理了。但清理不是简单的rm -rf必须有策略分优先级确保业务不受影响。3.1 清理优先级与安全操作我的清理优先级通常是日志文件 无用Docker资源 系统包缓存 其他临时文件。第一优先级容器日志清理这是最安全、见效最快的操作。如果发现某个容器的日志文件巨大比如超过1G处理方式如下动态清理治标直接清空日志文件。注意不能直接删除文件因为容器进程可能正打开它。正确做法是使用truncate命令或重定向清空。# 找到大日志文件比如 /var/lib/docker/containers/abcd1234/abcd1234-json.log # 使用 truncate 命令将其大小截断为0但保留文件句柄 sudo truncate -s 0 /var/lib/docker/containers/abcd1234/abcd1234-json.log或者如果知道是哪个容器可以进入容器内部重启日志服务如果支持但更通用的方法是配置Docker日志驱动限制日志大小和数量这是治本之策我们后面讲。查看容器日志诊断用清理前如果想看最后一部分日志内容可以用docker logs命令但不要用-ffollow或--tail所有内容这可能会输出海量数据。建议docker logs --tail 100 容器名或ID # 只看最后100行第二优先级清理Docker无用资源使用Docker自带的修剪命令这是最安全的方式。删除所有悬空镜像悬空镜像是那些没有标签且没有被任何容器引用的中间层镜像是磁盘空间的隐形杀手。docker image prune执行后会提示确认输入y。如果想强制删除不提示加-f参数。删除所有未被使用的资源这个命令更彻底会删除悬空镜像、停止的容器、未被任何容器引用的网络和构建缓存。docker system prune注意这个命令会删除所有停止的容器请确保这些容器确实不再需要。如果想保留卷加--volumes参数但通常不推荐因为未使用的卷也可能很大。更安全的做法是先docker system df -v查看确认。针对性删除镜像如果某个特定的大镜像不再需要直接删除。docker rmi 镜像ID或镜像名:标签如果镜像被容器引用需要先删除容器。第三优先级清理系统包管理缓存CentOS的YUM/DNF会在/var/cache/yum或/var/cache/dnf目录下保留下载的软件包头文件headers和软件包文件packages。这些缓存可以安全清理。# 清理缓存的软件包文件 sudo yum clean packages # 或者使用 dnf (CentOS 8) sudo dnf clean packages # 更彻底的清理包括元数据、插件缓存等 sudo yum clean all清理缓存不会删除已安装的软件非常安全。这通常能释放出几百MB到几GB的空间。第四优先级查找并清理其他大文件/目录使用find命令寻找特定大小或特定时间前的文件。# 查找根目录下大于100M的文件 sudo find / -type f -size 100M 2/dev/null | xargs ls -lh | sort -k5hr | head -20 # 查找 /var/log 目录下7天前的日志文件.log结尾 sudo find /var/log -name “*.log” -type f -mtime 7 -exec ls -lh {} \; # 确认无误后可以删除谨慎 # sudo find /var/log -name “*.log” -type f -mtime 7 -exec rm -f {} \;重要警告在根目录/下执行find并删除文件是极其危险的务必先ls -lh列出文件确认其路径和用途特别是/proc,/sys,/dev下的文件绝对不能动再决定是否删除。对于日志文件更好的做法是使用logrotate服务进行轮转和压缩管理而不是手动删除。3.2 一个完整的实战清理案例假设我们通过排查发现问题是1一个Nginx容器日志达到5GB2积累了数十个悬空镜像3YUM缓存有2GB。我的操作序列会是备份意识虽然清理操作相对安全但如果有极其重要的自定义镜像或数据卷在操作前心里要有数。对于日志可以先压缩备份再清理。# 例如备份大日志文件可选 sudo gzip -c /var/lib/docker/containers/nginx-container-id/nginx-container-id-json.log /tmp/nginx-log-backup-$(date %Y%m%d).gz清理容器日志# 找到Nginx容器的ID docker ps --filter “namenginx” --format “{{.ID}}” # 假设ID是 abcd1234 sudo truncate -s 0 /var/lib/docker/containers/abcd1234/abcd1234-json.log清理Docker资源# 先查看做到心中有数 docker system df -v # 删除悬空镜像 docker image prune # 删除停止的容器和未使用的网络确认这些容器已不需要 docker system prune -f清理系统缓存sudo yum clean all验证效果df -h /此时你应该能看到/dev/vda1的可用空间增加了。记录下释放的空间量并与之前的估算对比。4. 根治与预防构建可持续的磁盘管理策略一次清理解决不了根本问题。我们需要建立机制防止磁盘再次被“吃光”。这包括配置调整、监控告警和良好的使用习惯。4.1 配置Docker日志驱动限制日志大小这是预防容器日志膨胀的最有效手段。Docker支持多种日志驱动默认的json-file没有大小限制。我们可以为Docker守护进程全局配置或者为单个容器配置日志选项。方法一全局配置修改/etc/docker/daemon.json{ “log-driver”: “json-file”, “log-opts”: { “max-size”: “10m”, “max-file”: “3” } }max-size: 单个日志文件的最大大小到达后会自动轮转。这里设置为10MB。max-file: 保留的旧日志文件数量。这里设置为3意味着最多会有当前日志文件3个历史日志文件如-json.log,-json.log.1,-json.log.2,-json.log.3。更旧的文件会被自动删除。 修改后需要重启Docker服务生效sudo systemctl daemon-reload sudo systemctl restart docker注意重启Docker服务会短暂影响所有正在运行的容器。请在业务低峰期操作。方法二单个容器配置运行容器时指定docker run -d \ --name my-nginx \ --log-driver json-file \ --log-opt max-size10m \ --log-opt max-file3 \ nginx:latest对于已经运行的容器需要先删除再重新用新参数创建。所以最佳实践是在编写docker-compose.yml或 Kubernetes Pod Spec 时就定义好日志策略。4.2 使用日志轮转工具 logrotate对于系统服务如Nginx、MySQL、Syslog和Docker容器日志如果未用上述方法限制可以使用系统自带的logrotate服务进行管理。我们可以为Docker容器日志创建专用的轮转配置。创建文件/etc/logrotate.d/docker-containers/var/lib/docker/containers/*/*.log { rotate 7 daily compress delaycompress missingok copytruncate }rotate 7: 保留7个备份文件。daily: 每天轮转一次。compress: 轮转后使用gzip压缩旧日志。delaycompress: 延迟压缩下一次轮转时才压缩上一次的日志文件方便排查最近一天的问题。missingok: 如果日志文件不存在也不报错。copytruncate: 这是关键先复制日志文件然后清空原文件。这避免了需要重启Docker或容器进程来释放文件句柄。对于正在写入的日志文件这种方式最安全。 配置好后logrotate服务会每天自动执行。你也可以手动测试配置sudo logrotate -vf /etc/logrotate.d/docker-containers。4.3 建立磁盘空间监控与告警“治未病”比“治已病”更重要。我们需要在磁盘空间使用率达到一个危险阈值比如80%时就收到告警而不是等到100%。简单脚本监控可以写一个Shell脚本结合df命令和邮件或钉钉/企业微信机器人API定时通过cron检查磁盘使用率。#!/bin/bash THRESHOLD80 USAGE$(df / | tail -1 | awk ‘{print $5}’ | sed ‘s/%//’) if [ $USAGE -gt $THRESHOLD ]; then echo “警告根分区磁盘使用率 ${USAGE}% 超过阈值 ${THRESHOLD}%” | mail -s “磁盘空间告警” adminexample.com # 或者调用webhook发送到告警平台 fi将脚本加入crontab每小时执行一次0 * * * * /path/to/check_disk.sh使用专业监控系统在生产环境中更推荐使用Prometheus Grafana Alertmanager或者ZabbixNagios等成熟的监控方案。它们可以更直观地展示历史趋势并配置多级、多通道的告警规则。4.4 优化存储架构与使用习惯为Docker配置独立的数据分区或目录在初始化服务器时最好将/var/lib/docker挂载到一块独立的大容量数据盘上而不是和根分区挤在一起。这样即使Docker数据增长也不会影响系统根分区。可以通过修改Docker的>sudo lsof L1或者更精确地查找已删除的大文件sudo lsof | grep deleted | awk ‘{print $2, $4, $7, $9}’ | sort -n -k3 -r | head -20这个命令会列出所有状态为“deleted”的已打开文件并显示其进程ID(PID)、文件描述符(FD)、文件大小和路径。你会看到类似(deleted)的路径名。解决方法 找到对应的进程后你有两个选择重启该进程这是最干净的方法。通知业务方或选择合适的时间窗口重启持有该文件句柄的进程比如某个Java应用、MySQL、或者一个Docker容器。进程重启后内核会回收所有其占用的资源包括这些已删除文件的空间。清空文件内容如果进程非常重要不能重启你可以通过文件描述符来清空文件内容前提是你知道这个文件可以清空比如日志文件。首先找到进程PID和文件描述符FD比如1234和3ww表示写模式。然后# 方法将空内容写入 /proc/[PID]/fd/[FD] # 警告此操作不可逆且可能影响进程行为请务必确认文件类型 sudo echo “” /proc/1234/fd/3 # 或者使用 truncate 命令更安全 sudo : /proc/1234/fd/3极度谨慎确保你清空的是日志文件之类的可丢弃数据而不是数据库文件或其他关键数据文件。5.2 文件系统inode耗尽df命令看的是数据块block的使用情况。文件系统还有另一个限制inode数量。每个文件包括目录、软硬链接都会消耗一个inode。如果磁盘上存在海量小文件比如Docker镜像的无数个小层或者邮件系统的无数小邮件即使数据块空间还有剩余inode用完了系统也无法创建新文件同样会报“No space left on device”。检查inode使用情况df -i或者df -ih /查看/dev/vda1的IUse%列。如果接近或达到100%就是inode耗尽了。排查inode被谁用了# 查找根目录下哪个目录包含的文件数量最多消耗inode最多 sudo find / -xdev -type f | cut -d “/” -f 2 | sort | uniq -c | sort -rn | head -20-xdev参数防止find命令跨越到其他挂载点比如/proc,/sys。解决方法 找到产生海量小文件的源头通常是某个失控的程序或配置错误的日志记录。清理该目录下的文件。对于Docker可能是某个容器内的临时目录或缓存目录产生了大量小文件需要进入容器内部或通过数据卷进行清理。预防措施同样是监控inode使用率并在docker run时注意挂载卷的权限和容器内进程的文件创建行为。5.3 磁盘挂载点重叠或异常极少数情况下可能是挂载点mount point出现了问题。例如某个目录被单独挂载了一个设备但这个设备满了而df -h显示的是根分区的使用情况没有显示这个子挂载点。或者有文件系统被以只读ro方式重新挂载导致无法写入。检查所有挂载点mount | column -t或者findmnt查看是否有意料之外的挂载或者/var,/home等目录是否被单独挂载了分区。如果/var/lib/docker被单独挂载了一个小分区那么即使根分区空间充足Docker也会因为自己的小分区满了而无法工作。检查文件系统读写状态mount | grep “ / ”查看根分区的挂载选项确认是rw读写而不是ro只读。处理这类问题需要根据实际的挂载情况调整可能需要修改/etc/fstab文件并重新挂载。6. 总结与个人心得处理/dev/vda1磁盘100%的问题尤其是Docker环境下的已经成了运维的必修课。回顾整个过程我的体会是它更像是一场“侦查”与“排雷”的结合。侦查在于你必须依靠df,du,docker system df,lsof,find这些命令像拼图一样从全局到局部从表象到根源一步步还原出磁盘空间被“谁”、以“何种方式”占用。排雷在于每一个删除或清理操作都带有潜在风险你必须清楚你在删除什么是否会影响正在运行的服务是否有更优雅的替代方案如日志轮转替代直接删除。我个人的几条血泪经验truncate比rm更安全对于正在被进程写入的日志文件永远优先考虑用truncate -s 0或: file来清空内容而不是直接rm。后者可能导致进程报错甚至崩溃。docker system prune是双刃剑它很方便但一定要先docker ps -a确认那些停止的容器是否真的可以丢弃。我曾经不小心用它删除了一个包含未持久化重要配置的测试容器不得不重新构建。监控一定要做在平时不要等到报警电话响起才去处理。将磁盘使用率包括inode纳入日常监控仪表盘设置80%-85%的预警线和90%的告警线。这能给你预留充足的反应和操作时间。理解存储驱动Docker使用overlay2存储驱动时镜像和容器的层管理非常高效但也相对复杂。了解docker system df -v输出中RECLAIMABLE的含义能帮你更好地判断哪些空间是可安全回收的。文档化你的清理步骤将有效的排查和清理命令写成脚本或记录在运维手册中。当下次凌晨三点再次被叫醒处理同样的问题时这份文档能让你保持清醒快速解决问题。最后记住一个原则释放空间是手段优化管理才是目的。每一次磁盘满的告警都应该推动你去思考日志输出是否合理缓存策略是否恰当存储架构是否需要优化只有这样你的系统才会在一次次“治疗”中变得更强健。