资讯动态

CentOS 7.9 根分区 100% 排查指南:隐藏占用定位与空间释放

发布时间:2026/9/24 19:06:23 来源:尧图企业网站定制
1. 先说清楚根分区满通常不是你以为的那种“满”CentOS 7.9 的根分区/跑到 100%这事我遇到过太多次尤其是在那些跑了几年、日志没怎么管的服务器上。最迷惑的还不是空间真没了而是你执行df -h清清楚楚看到/那一行 Use% 是 100%可用空间是 0但你在根目录下敲du -sh /*一层层找怎么加都加不到df报出来的那个总量。然后就进入“这个目录好像也不大那个目录也才几个G凭什么满”的死循环。这种“看不见的占用”在运维圈里有个比较统一的叫法——隐藏占用。它可能是一次没重启的服务还攥着已删除的日志文件也可能是 systemd 的 journal 日志偷偷攒了几个 G或者是大量小文件把 inode 耗光后系统误报磁盘满。这篇文章就是把我这几年在 CentOS 7.9 上排查这类问题的完整思路、命令和坑点整理出来。不绕弯子直接按“先定位、再处理、后预防”的顺序走。顺带提一句如果你是想在这台机器上做 OpenSSH 升级到 10.5 这类操作第一步就是先把磁盘空间腾出来否则编译到一半No space left on device那才叫欲哭无泪。所以这篇文章的内容也适合那些正准备折腾系统组件升级、担心磁盘不够的人提前过一遍。整体排查思路其实可以概括成一句话先看文件系统视角的分配情况再看进程视角的占用情况最后看日志和元数据层面的隐藏开销。下面我就按这个顺序展开。2. 从根目录逐层往下挖找出你看得见的“大头”2.1 du 和 df 看的是两种东西先别急着困惑很多新手看到df和du不一致就慌。这里先解释清楚df是从文件系统的块设备层面统计已分配的块它关心的是整个分区“用了多少块”du是遍历目录里的文件把每个文件占用块数累加起来。两者统计粒度完全不同存在差异很正常。差异主要来自几方面文件系统元数据inode、超级块、保留块ext4 默认预留 5%、已删除但仍被进程打开的文件、以及像 journal 这类稀疏文件。所以我排查的第一步永远是先用du从根往下找一遍确认哪些能看见的目录占了空间把“明账”先算出来。du -h -x --max-depth1 / 2/dev/null | sort -rh | head -20这里的-x很关键意思是只统计当前文件系统不跨挂载点。如果你机器上有挂载独立的/data或/home不加-x会把别的分区的占用算进来干扰判断。2/dev/null是把权限不足的报错丢掉不然输出会夹杂一堆 Permission denied。2.2 实战里最常见的那几个“大胃王”目录跑完上面命令通常你能看到几个老面孔。我按出现概率排一下目录典型占用原因处理优先级/var/log应用日志、系统日志、切割后没回收的旧日志高/var/lib/dockerDocker 镜像、容器层、overlay2 数据高/var/cache/yumyum 下载的 rpm 包缓存中/home用户数据、备份文件视业务定/tmp临时文件、sess_* 会话文件中/usr系统程序、库文件一般不会突然增大低先说/var/log。CentOS 7.9 默认 rsyslog 会接管系统日志如果开了 auditd、或者装了 tomcat、nginx、mysql 这类应用它们的日志很可能全堆在/var/log下面。有些应用日志没配 logrotate一个 catalina.out 就能长到十几个 G。所以看到/var/log大别慌先看是哪个文件再决定是 truncate 还是配置轮转。再说/var/cache/yum。这地方是 yum 安装包时的下载缓存正常情况下也就几百 M但如果长期不清理或者之前做过大批量安装积攒几个 G 也不奇怪。清理很安全直接yum clean all就能释放。至于/tmp很多服务会把临时文件丢在这里比如 PHP 的 session、Java 程序的临时目录。注意一点CentOS 7.9 默认 systemd-tmpfiles 会定期清理/tmp下超过 10 天未访问的文件但有些进程长期持有的临时文件不会被清走得人工确认。2.3 直接找超过某个阈值的大文件效率更高如果你没有耐心一层层 du 下去用 find 直接扫出超过 1G 的文件往往是最快的路径。find / -xdev -size 1G -exec ls -lh {} \; 2/dev/null这条命令把根文件系统下所有大于 1G 的文件列出来带人类可读的大小。跑完如果有结果根据文件路径基本能判断是哪个应用的问题。顺手把阈值调成500M再扫一轮能看到更小的“中号”文件。这里我个人的习惯是分两步先扫大文件再按目录 du。因为 find 扫大文件是全局扫描很快能定位明显的目标du 适合在缩小范围后看某个目录的整体占用分布。两个命令配合用效率比单抓一个强很多。3. 核心难点已删除但未释放的文件才是真正的“隐藏占用”3.1 deleted 文件是怎么把空间吃掉的这是整个排查里最容易卡住人的环节。场景一般是这样的某个进程比如 java 进程、nginx、数据库持续往一个日志文件里写内容日志文件越来越大。管理员发现后直接用rm -f把日志文件删了以为空间就释放了。结果df -h一看可用空间一点都没变。原因在于 Linux 的文件删除机制一个文件是否真正释放磁盘块取决于还有没有进程持有它的文件描述符fd。rm做的只是把目录项dentry断开文件在文件系统里的 inode 和 data block 仍然被那个进程引用着。只要进程不关闭 fd那些块就一直是“已分配”状态du看不到因为目录里已经没有这个文件了但df统计块分配时照算。换句话说你删的是路径进程攥着的是文件本身。只要进程还活着空间就永远回不来。3.2 用 lsof 一分钟定位“幽灵文件”排查这种情况lsof 是绝对的主角。CentOS 7.9 上如果没装先装一下yum install -y lsof定位 deleted 文件两条命令二选一lsof L1或者lsof | grep deletedL1的意思是列出 link count 为 0 的文件也就是已删除但仍被打开的文件。输出里COMMAND是进程名PID是进程号SIZE/OFF能看到文件大小不是最新的但可以参考。我实际遇到最多的是java、nginx、mysqld、rsyslogd这几个进程。如果你发现有不只一个 deleted 文件挑SIZE最大的处理。比如你看到java 12345 root 1w REG 253,0 8388608 1048577 /data/logs/app.log (deleted)这说明 PID 12345 的 java 进程还攥着一个 8G 的已删除日志。3.3 安全释放空间的两条路径定位到进程和文件描述符后处理方式有两种。第一种清空而不是删除。只要进程还需要继续写这个日志别用rm改用 truncate 把文件内容清成 0。这样文件描述符还指向同一个文件进程写日志不受影响磁盘块立刻释放。truncate -s 0 /proc/12345/fd/1/proc/PID/fd/下能看到进程打开的所有文件描述符fd 后面的数字含义是0 是标准输入1 是标准输出2 是标准错误其他数字是普通文件句柄。找到上面 lsof 输出对应的 fd 编号直接 truncate。第二种重启或重载进程。如果这个进程的日志文件本来就是被删除后遗留的而且业务允许重启直接重启或者 reload 最彻底。进程重新启动后旧 fd 会被回收空间自然释放。systemctl restart java-service # 或者 kill -HUP 12345kill -HUP 对很多守护进程来说等于“重载配置”不一定退出但会重新打开日志文件旧文件描述符被释放。注意这是 Unix 信号的传统用法不是所有进程都响应得看具体应用。这里有个重要提醒在确认进程是什么之前别乱 kill。如果是数据库在写 binlog 或者 redo log直接杀死进程可能导致数据不一致。稳妥做法是先用ls -l /proc/PID/fd/看看这个 fd 对应的文件路径确认是什么类型的文件再动手。ls -l /proc/12345/fd/1如果输出显示这个 fd 指向的是/var/log/messages、catalina.out这类纯日志文件清空基本没风险如果指向的是数据库数据文件、socket 文件那就不能乱动。4. systemd journal 日志CentOS 7.9 上最容易被低估的空间杀手4.1 journal 日志为什么能长到几个 GCentOS 7.9 用 systemd 作为 init 系统所有服务的输出、内核日志、系统消息默认都会进 journald 统一管理。journal 日志存放在/var/log/journal目录它的特点是二进制格式、自动压缩、但不一定自动限制大小。这一点很多人踩坑CentOS 7.9 默认配置/etc/systemd/journald.conf里的SystemMaxUse是注释状态也就是没有限制。journald 在实际使用中会自认为“按磁盘空间的 10%”来估算但服务器上如果装了数据库或 Java 应用日志写入量极大journal 会在短时间内膨胀到几个 G。再加上 rsyslog 那套也同时在写/var/log/messages两边一起囤根分区不爆才怪。4.2 清理 journal 的三板斧清理 journal 我用最多的是journalctl自带的真空命令安全且立竿见影。# 清理到只剩 200M journalctl --vacuum-size200M # 清理 7 天前的日志 journalctl --vacuum-time7d--vacuum-size的意思是清理旧日志直到 journal 总大小降到 200M 以下。--vacuum-time更直观只保留最近 7 天的日志。两条命令按需选一个就行不会误删正在使用的日志文件。如果你确定 journal 日志完全没用了还有一条更彻底的路径直接清空/var/log/journal目录然后重启 journald。rm -rf /var/log/journal/* systemctl restart systemd-journald但我不太推荐开这种“核弹”操作万一出问题排查日志都没了。先用 vacuum 清理是最稳的。4.3 从源头给 journal 设置上限一劳永逸清理只是治标长期来看必须给 journald 套上笼头。编辑/etc/systemd/journald.conf做如下修改[Journal] Storageauto Compressyes SystemMaxUse500M MaxRetentionSec14dSystemMaxUse500Mjournal 文件总大小上限 500M这是最关键的。MaxRetentionSec14d日志最长保留 14 天双重保险。Storageauto保持默认日志自动判断是否持久化存储。改完重启服务生效systemctl restart systemd-journald这里要说一句很多人只设置了SystemMaxUse但忘了重启 journald结果不生效。systemd 的服务很多配置都是启动时读取不重启就没变化。另外一个容易漏的坑是如果你用了 logrotate 切/var/log/messages但 rsyslog 一直不重开文件句柄那也会出现“切割了但空间没释放”的情况。这种情况下需要给 rsyslog 发 HUP 信号让它重新打开日志文件kill -HUP $(cat /var/run/rsyslogd.pid)5. 其他几个不被注意的“空间偷手”5.1 文件系统保留块ext4 默认预留 5%块设备就这么“少”了CentOS 7.9 默认文件系统是 xfs根分区或者 ext4看安装方式。ext4 在格式化时默认预留 5% 的块给 root 用户这是为了防止文件系统碎片化严重或普通用户把空间占满后 root 无法登录修复。问题是对 1T 的分区来说5% 就是 50G严格算起来这 50G 不在普通用户可用的范围内df -h也会把它排除在可用空间外。但对根分区来说这 5% 其实是系统最后的应急池我个人不建议直接把它归零。如果你的根分区确认永远不可能写满可以通过 tune2fs 调低# 查看当前保留块比例 tune2fs -l /dev/mapper/centos-root | grep Reserved block # 调整为 1% tune2fs -m 1 /dev/mapper/centos-root注意这条命令只能用在 ext4 文件系统上。CentOS 7.9 默认安装时根分区如果是 xfs就不适用。xfs 没有直接调整预留比例的简捷命令不过 xfs 的预留机制和 ext4 略有不同影响通常不大别在这上面死磕。5.2 inode 耗尽df 显示有空间系统却报 No space left on device这个坑我见过不止一次。文件系统存储文件除了数据块还要用 inode 存元数据文件名、权限、时间戳、数据块地址。如果一个小分区上塞了上百万个几 KB 的小文件inode 先耗尽系统会直接报No space left on device但df -h看空间可能还剩一大半。验证方法df -i看/那一行的 IUse% 是不是已经 100%。如果是处理思路就是找到那些大量生成小文件的目录通常集中在/var/spool、/tmp、/var/tmp、邮件队列、sess_* 文件等。清理掉无用的碎文件inode 释放后空间就“回来了”。这个地方特别要提醒如果你在/上跑 docker容器内部写入大量临时小文件inode 会快速消耗。容器删了之后 inode 不一定马上释放需要等一会儿或者重启 docker 服务。5.3 Docker overlay2 目录镜像层和容器层叠加的隐形巨头装了 Docker 的 CentOS 7.9/var/lib/docker/overlay2是根分区膨胀的重灾区。overlay2 存储驱动下每个镜像层和容器层都有对应的目录镜像多了、容器频繁构建这个目录几十 G 很正常。注意一个问题你删掉一个容器或者镜像overlay2 里对应的目录不一定立刻消失因为 Docker 的存储驱动有 GC垃圾回收机制并且一些被容器引用的层不能马上删。所以处理顺序应该是# 先停容器再清理悬空镜像 docker stop $(docker ps -aq) docker system prune -adocker system prune -a会清理所有停止的容器、悬空镜像、未使用的网络和构建缓存对释放空间效果明显。如果系统里业务不允许停容器那就至少执行docker image prune清理悬空镜像也能腾出不少空间。5.4 工具推荐ncdu 让你像看图一样找目录占用如果你觉得命令行 du 层层递归不够直观强烈推荐 ncdu。它是一个交互式的磁盘占用分析器界面类似图形化的终端版本上下方向键就能在目录之间跳转看占用比例。yum install -y ncdu ncdu /运行后它会先扫描整个文件系统然后给你一个按大小排序的目录树。你可以用方向键展开/收起目录直接看到哪个子目录最占地方。我个人用它来处理那种“根分区满但不知道哪里大”的问题一眼就锁定target。不过 ncdu 在超大磁盘比如几 T上扫描时间会比较长超时了不要慌先让它跑完。扫描完成后用q退出。6. 日常预防避免下次再被“莫名其妙满掉”折腾6.1 写一个简单的磁盘监控脚本超过阈值就告警根分区被占满这种事最好在它发生之前就发现。写个脚本放在 cron 里每天检查一次超过 80% 就发告警。这里给个最小实现#!/bin/bash THRESHOLD80 CURRENT$(df / | awk NR2 {print $5} | sed s/%//) if [ $CURRENT -gt $THRESHOLD ]; then echo Warning: Root partition usage is ${CURRENT}% at $(date) | mail -s Disk Warning yourmailexample.com fi如果你没配 mail也可以把告警写到系统日志或者接第三方通知。重点是养成“空间快满了就要处理而不是满到 100% 才反应”的习惯。6.2 给所有会写日志的服务都配好 logrotate大多数应用日志出问题是没配轮转或者轮转完没重开文件句柄。系统自带的 logrotate 在 CentOS 7.9 上默认每天执行一次配置文件在/etc/logrotate.d/。以 nginx 为例一个像样的配置长这样/var/log/nginx/*.log { daily rotate 14 compress delaycompress missingok notifempty create 0640 nginx nginx sharedscripts postrotate [ -f /var/run/nginx.pid ] kill -USR1 cat /var/run/nginx.pid endscript }核心是postrotate那一段日志切割后向进程发信号让进程重新打开新的日志文件否则旧文件句柄不释放空间照样回不来。这个思路对 nginx、apache、tomcat 等主流服务都适用具体信号名查一下对应文档就行。6.3 删除真正的大文件前先确认它不在被进程写我处理过这样一个事故某台服务器/data/logs下有个 20G 的应用日志我看到后rm -f删了结果空间没释放。一查是 Java 进程还在往里头写。后来我只能采用 truncate 清空的方式才把空间要回来。所以现在我的操作习惯是删大文件前先lsof | grep filename看看有没有进程在写。如果有就用 truncate 或重启服务如果没有直接rm安全。这个习惯也能避免数据库数据文件被误删的惨剧。6.4 一个排查顺序速查表方便贴在你工位边上为了方便快速反应我把整个排查流程压缩成了一张表遇到问题直接按顺序跑步骤命令或操作目的1df -h确认是不是根分区真的满2df -i排除 inode 耗尽3du -h -x --max-depth1 /sort -rh4find / -xdev -size 1G -exec ls -lh {} \;直接找大文件5lsof L1定位已删除但未释放的文件6journalctl --vacuum-size200M清理 systemd journal7docker system prune -a如果装了docker清理容器和镜像残留8按上面结果逐项处理实际释放空间中间任何一步发现目标后先处理再继续往下走。通常跑到第 5 步就能解决一半问题跑到第 6 步能解决剩下的大部分。7. 最后再分享一个我自己的处理习惯我在实际排查中最怕的不是空间小而是空间明明很大却不知道被谁吃了。后来养成的习惯是每台服务器装好后第一时间就把 journald 的SystemMaxUse调小把所有日志目录都跑一遍 logrotate 配置同时在 cron 里加一个磁盘占用阈值检查。这三件事加起来不到十分钟但能避免后面无数个“根分区被塞满导致服务挂掉”的凌晨三点事故。另外还有个小技巧就是每当你要准备升级系统里的大组件比如把 opensshd 升级到 10.5、装数据库、跑编译任务之前先跑一次df -h /确认可用空间足够。很多组件升级失败根本不是版本兼容性的问题而是编译或安装过程中磁盘空间不足报错信息又隐藏得特别深排查起来很费劲。磁盘满这件事本质上不是技术难度高而是问题隐藏得太深。只要按照“df 看总量 → du 找目录 → lsof 抓幽灵 → journal 扫地”这条路径走再顽固的隐藏占用也能揪出来。希望这篇东西能帮你省下几个小时的无头苍蝇时间。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价