ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

根分区满了却找不到大文件?CentOS 7.9隐藏占用排查与清理

根分区满了却找不到大文件?CentOS 7.9隐藏占用排查与清理 1. 确认根分区是真的满了还是被看不见的空间占了先把话说在前面CentOS 7.9 根分区/显示 100% 占用这是运维群里被问烂的经典问题。很多人第一反应是du -sh *一顿扫结果发现所有目录加起来跟df -h显示的容量差出一大截十几 GB 甚至几十 GB 不翼而飞。这种隐藏占用通常不是磁盘坏道也不是什么玄学十有八九是下面三类情况之一有进程占用了已被删除的文件文件句柄还开着空间没释放某个子目录底下挂载了独立分区把原本根分区里的数据盖住了du看不到系统日志、临时文件、崩溃转储等异常膨胀藏在/var底下你没仔细翻。排查之前我建议先把基础信息抓全不要一上来就du。命令三条df -hT df -i mount | grep / df -hT看容量和文件系统类型df -i看 inode。注意一个容易被忽略的坑inode 用满和 block 用满表现完全不一样。inode 满了的时候df -h照样显示几十 GB 可用但系统就是写不进文件任何touch、mkdir都报 No space left on device。所以排查第一步永远是两块一起看别只盯容量。du和df统计维度不同这也是出现隐藏占用的根源。df统计的是整个文件系统里所有已分配块的总和不管这些块能不能通过目录树看到du则是从目录项出发累加每个可访问文件的大小。换句话说如果一个文件已经被rm删除但某个进程还握着它的文件描述符df会算它du却找不到它。中间这个差值就是我们要在根分区里揪出来的东西。我见过不少同事在这时候直接重启服务器重启完空间确实回来了但这属于治标不治本——如果是有问题的服务还在持续占用删除文件过几天又会把磁盘吃满。正确做法是先定位再决定重启还是在线清理。2. 定位已删除但仍被进程占用的文件lsof 是主武器这类隐藏占用在三者里最隐蔽也最常见。典型场景是日志轮转logrotate把/var/log/nginx/access.log重命名成access.log.1并新建了一个空文件但如果 nginx 没有收到信号去重新打开日志文件它仍然在往旧文件句柄里写。由于文件名已经不存在了du扫目录时完全看不到它而df里它占的空间一直没释放。排查命令用lsof最直接lsof L1 | grep deletedL1的意思是把链接数小于 1 的文件也就是已删除但仍打开的列出来。输出里会看到类似这样的行nginx 1234 www-data 12w REG 253,0 5368709120 1048577 /var/log/nginx/access.log (deleted)重点看三列进程名和 PID、文件大小、原始路径。文件大小 5GB路径是access.log (deleted)说明轮转后 nginx 没重新打开日志文件旧句柄一直在写。这时候有两种处理方式方式一优雅重启服务systemctl reload nginx # 或者对老派用法 nginx -s reopen重启后旧句柄关闭空间立即释放这是首选方案。但注意reload不一定每次都触发日志句柄重建有些服务只响应USR1信号比如 nginx 就是kill -USR1 $(cat /var/run/nginx.pid)。操作完记得df -h验证。方式二不清除服务在线截断文件描述符有些核心服务不能随便重启比如数据库、支付网关重启一次影响面太大。这时候可以用/proc文件系统直接截断该进程打开的已删除文件ls -l /proc/1234/fd/12 : /proc/1234/fd/12先确认 1234 是 PID12 是文件描述符编号然后用空重定向把文件截断成 0 字节。空间一样能释放服务完全不用停。这里有两道红线第一ls -l /proc/PID/fd/N看到的是个软链接指向/var/log/nginx/access.log (deleted)du和ls在/proc下不一定能直接给出大小多试几次心态别崩第二截断操作只对日志类追加写入的文件安全如果是数据库的数据文件或者进程在随机读写的内容直接截断会把文件搞坏宁可等维护窗口重启。再补一个不用 lsof 的土办法万一机器上没装 lsoffind /proc/*/fd -type l -lname *deleted* 2/dev/null | awk {print $9} | cut -d/ -f3拿到 PID 列表后去ls -l /proc/PID/fd里找对应句柄。3. 挂载点覆盖根分区里藏了一块看不见的数据第二个隐蔽大户是挂载点覆盖。CentOS 7.9 上常见操作是把数据盘挂载到/home、/var或/opt挂载之后原目录下遗留的文件并不会消失它们还在根分区里躺着只是被新挂载点挡住常规路径访问不到。如果是根分区/被某个子目录的挂载点盖住du -x /会跳过其他文件系统但原本藏在挂载点下方的旧数据依然占用着根分区的空间而du不会再统计它们。用findmnt或者直接看挂载表快速判断有没有这种情况findmnt -R / # 递归显示根分区下的所有挂载点 cat /proc/mounts对比一下df -hT里每个挂载点的大小和根分区总大小把独立的挂载点如/dev/sdb1挂在/var上全部挑出来。假如/var是独立分区而根分区/的df显示 100%那就需要去检查挂载点下面的旧目录残留。具体操作# 临时卸载挂载点看看底下到底藏了多少东西 umount /var du -sh /var mount /var如果/var底下冒出来几十 GB 的数据说明以前有人在挂载前就在/var里存了大量文件后来直接挂盘上去盖住了。处理办法是把旧数据迁走mkdir -p /srv/old-var mv /var/* /srv/old-var/再重新挂载。同类问题也常发生在/home、/tmp、/opt等目录。这里有个关键提醒卸载挂载点前必须先保证没有进程在访问挂载点路径。可以用lsof /var检查也可以先systemctl stop相关服务。直接umount会报 target is busy强卸umount -l虽然能卸掉但会造成不可预期的后果尤其是数据库、日志这类持续写入的路径千万别用-l。要排查这种挂载掩盖问题更稳妥的做法是先看这张表心里有个底挂载路径独立分区根分区空间占用风险/否主分区本身/var是挂载点下方旧数据残留/home是挂载点下方旧数据残留/tmp是挂载点下方旧数据残留 tmp 文件未清还有一种变种根分区本身没有新增挂载但挂载点目录在被挂载前就已存在大量数据。很多人以为mount到空目录就完事了实际上非空目录挂载后底下旧文件不会提示、不会删除只会静静躺在那里占空间。CentOS 7.9 默认的/home在安装系统时如果没单独分区装上数据盘再挂载到/home很容易复现这个坑。4. journald、yum 缓存与临时文件日志盲区里的三大常规刺客排除前两类之后如果根分区还是高居不下那就该把重点放到系统日志和缓存上了。CentOS 7.9 上最容易被忽视的三个位置/var/log/journal、/var/cache/yum、/tmp及/var/tmp。先看 journald 日志占了多少journalctl --disk-usage输出类似Archived and active journals take up 4.2G in the file system.。默认情况下 journald 不会限制磁盘占用只要系统里跑着各种服务日志会一路膨胀特别是rsyslog和journald双写的情况下空间消耗翻倍。限制和清理两条路# 立即压缩到 200M 以内 journalctl --vacuum-size200M # 只保留最近 3 天的日志 journalctl --vacuum-time3d然后修改/etc/systemd/journald.conf在[Journal]段加两行SystemMaxUse200M SystemKeepFree500M改完重启 journaldsystemctl restart systemd-journald。SystemKeepFree是软性保护线意思是日志占用达到一定量后系统会为别的用途预留空间这两个参数配合起来比单设上限更稳。二看 yum 缓存CentOS 7.9 用 yum 更新过系统后/var/cache/yum里会留下大量 rpm 包。清理命令很简单yum clean all du -sh /var/cache/yum如果系统上线很久没做过yum clean这里攒出几个 GB 很正常。另外yum install时的临时下载文件会留在/var/tmp/yum-*目录也得顺手清掉。三看 /tmp 和 /var/tmp/tmp 下常见大文件来源有被遗忘的安装包、core dump、解压一半的压缩包。/var/crash、/var/lib/systemd/coredump也经常出现超大文件。命令du -sh /var/crash /var/lib/systemd/coredump /tmp /var/tmp 2/dev/null顺带提一下 ABRT自动 bug 报告工具CentOS 7.9 默认可能装了 abrtd程序崩溃会往/var/spool/abrt里写转储有的转储文件几百 MB 甚至按 GB 计。du -sh /var/spool/abrt查一下不想要就systemctl stop abrtd rm -rf /var/spool/abrt/*。这些目录排查完之后真正的大头通常已经浮出水面。逐级用du -x往下钻注意-x参数很关键它让du不跨越文件系统边界避免把其他挂载分区的数据统计进来干扰判断du -x -h --max-depth1 / | sort -hr | head -30--max-depth1列出根目录下每个一级子目录的占用排序后一眼看出谁最大。再针对大的子目录继续往下钻。这一步要和df交叉验证如果du -x /的总和明显小于df显示的已用空间差值基本就是已经删除但被进程占用的文件。5. 清理动作要快但收尾动作更要谨慎验证、重启、防复发清理完空间后别急着下班还有三件事必须做扎实。第一步验证清理效果。df -h确认根分区百分比降下来同时观察系统是否出现异常比如 journald 截断后某些服务日志被切走rsyslog 是否正常收日志lsof 截断过的服务是否还有告警。如果清理完不到一天又涨上去说明源头没断还要回头排查是不是有什么程序在疯狂写日志或产生临时文件。第二步查遗补漏把能释放但还没释放的空间全部扫掉。lsof L1 | grep deleted find / -xdev -size 1G -exec ls -lh {} \; 2/dev/null第一条再跑一遍确认没有漏网的文件描述符。第二条用-xdev限制在根分区内扫描超过 1GB 的大文件目的是找出漏掉的大块头比如没被轮转的旧日志、格式不对的转储文件。如果扫出/var/log/messages这种被 rsyslog 持续写入的日志可以用清空而不是删除的方式处理: /var/log/messages然后systemctl restart rsyslog。直接rm会引发和第一节一样的 deleted 句柄问题等于白清。第三步建立防复发机制核心是让这些隐藏占用永远不要有机会再出现。我的建议是配置三件套CentOS 7.9 上全部可用定时任务守护磁盘写一个脚本扔进/etc/cron.d/disk-check每日跑一次根分区超过 85% 就告警。logrotate 配置修正检查/etc/logrotate.d/下各服务配置确认有copytruncate或create指令。copytruncate对不停服场景更友好边复制边截断日志服务不用重启也能释放空间如果服务支持信号重开日志如 nginx 的USR1用postrotatekill -USR1方案更干净。journald 配额落地上一节说的SystemMaxUse和SystemKeepFree配置完一定要systemctl restart systemd-journald验证生效避免下次又因为日志吃满。再补一个我自己常用的脚本片段每天凌晨执行把已删除但仍被占用的句柄数量打出来#!/bin/bash # /usr/local/sbin/check_deleted_fd.sh LIST$(lsof L1 2/dev/null | grep deleted) if [ -n $LIST ]; then echo $LIST | mail -s Deleted FD Alert on $(hostname) root fi配合 cron 定期跑基本能做到提前发现不用每次都等到df -h显示 100% 才被动处理。最后说一个长期维护的体会CentOS 7.9 这类问题真正麻烦的不是清理而是定位到隐藏点。du只看得到目录树里的活文件df只管整个文件系统的块分配两者之间的差值就是判断隐藏占用最核心的线索。排查顺序固定为先df -i排除 inode 用满再lsof L1找 deleted 句柄再findmnt查挂载掩盖最后才去翻 journald 和/var缓存。按这个顺序走根分区 100% 的问题基本能在一个小时内解决而且能保证不是用重启糊弄过去的。
返回列表