ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Linux根分区爆满排查与清理指南:从df到lsof的实战技巧

Linux根分区爆满排查与清理指南:从df到lsof的实战技巧 “df -h 一看根分区又100%了。”这句话我在过去几年里听过太多遍也说过太多遍。作为常年和 Linux 服务器打交道的人根分区爆满几乎是每个运维和开发都会遇到的经典场景。你明明感觉没装什么东西可/就是莫名其妙被塞满然后系统开始出现各种诡异问题ssh 登录变慢、服务起不来、mysql 写入报错、cron 任务失败甚至整个机器直接卡死。这篇文章我想把根分区爆满的排查思路、命令用法和踩坑经验完整梳理一遍。核心目标就一个帮你在几分钟内找出空间到底被谁吃掉了是真文件还是删除后没释放的“假占用”并且给出可复用的预防方案。内容不绕弯子新手能照着做老手也能从中看到一些自己容易忽略的细节。1. 根分区为什么会爆满先理解问题的本质1.1 根分区是所有路径的“兜底”Linux 的目录结构是一棵倒挂的树/就是树根。挂载在/上的分区是所有路径的兜底——只要某个目录没有被单独挂载到独立分区它写入的数据就会落在根分区里。这句话听着简单但它解释了绝大多数根分区爆满的原因。比如你的数据盘挂在了/data没问题但如果你忘了给/var分独立分区那么/var/log下的日志、/var/cache下的缓存、/var/lib/docker下的容器数据就全部算在根分区头上。尤其是运行了 Docker、数据库、Java 服务这类会持续产生日志的机器根分区被无声填满只是时间问题。1.2 常见的“空间刺客”有哪些根据我排查过的案例根分区被撑爆的高频元凶基本集中在以下几类日志文件/var/log下的系统日志、应用日志、journal 日志。常见陷阱是journal日志没配置大小上限日积月累能吃掉几十 GB。临时文件/tmp下遗留的安装包、解压产物、进程运行产生的临时文件。包管理缓存/var/cache/apt、/var/cache/yum、/var/cache/dnf下的软件包缓存清理一次往往能释放好几个 GB。Docker 相关/var/lib/docker下的容器日志、无主镜像、悬空卷这是现代服务器上最容易被忽视的大户。core dump进程崩溃产生的 core 文件有的默认丢到工作目录或/下一个核心转储可能就占几个 GB。用户主目录尤其是/root和/home下的隐藏文件、备份包、安装包。1.3 还有一种“看不见”的占用除了真实存在的文件还有一类极其常见的场景文件明明被rm删了但df显示的空间始终没释放。这是因为有进程还持有这个文件的文件句柄文件虽然从目录里消失了但它占用的磁盘块依然被标记为使用中。服务器的服务不会因为你删了日志文件就把句柄关掉排除这个问题经常是磁盘空间排查任务里最关键的一环。理解了这三类原因接下来的排查命令才有用武之地。目标很明确定位真实文件找出隐藏占用确定清理对象。2. 排查命令的原理与正确用法2.1 df先确认整体状态而不是凭感觉猜排查磁盘空间问题第一步永远是df不是ls。很多新手一上来就在/下面执行ls -lh看着哪个目录“大”就去翻效率很低而且ls看到的文件和实际占用的文件系统块经常对不上。df看的是文件系统层面的使用情况帮你迅速确认根分区是否真的满了以及挂载点、容量、已用空间、使用率都分别是什么状态。df -h df -idf -h是按人类可读的 KB/MB/GB 显示容量信息。重点关注挂载点为/的那一行。另一个容易忽略的是df -i查看 inode 使用率。磁盘空间没满但 inode 满了一样会报“No space left on device”。这一点放到第四章细讲。2.2 du深入目录统计真实占用df告诉你“哪里满了”du告诉你“哪个目录大”。它是靠遍历目录、统计每个文件实际占用的块数量得出结果的。常用姿势du -h --max-depth1 / | sort -hr | head -30 du -x -h --max-depth1 / | sort -hr | head -30这两条命令里的细节值得多说两句--max-depth1表示只统计一层子目录不会把整个文件系统全遍历一遍速度快很多。先看一层确定嫌疑目录再往下一层深入。-x或者说--one-file-system很关键作用是不要跨越文件系统边界。如果没有它du /会把所有挂载在/下面的其他磁盘分区也统计进来结果就失真了分不清根分区本身到底占了多少。注意一下du的统计口径它统计的是文件实际占用的磁盘块大小跟ls -l看到的文件逻辑大小可能不同尤其对于稀疏文件、小文件较多的目录两者差异会很大。所以别拿ls的大小去质疑du那不是一个维度。2.3 lsof 与 fuser揪出“删不掉”的隐藏占用如果你的du怎么查都查不出大文件但df明明显示根分区爆满那基本可以断定是“已删除但未释放”的问题。Linux 的删除语义是这样的rm只是从目录项里移除文件名但如果某个进程已经打开过这个文件文件对应的 inode 和磁盘块仍然被引用着直到所有打开它的进程关闭文件句柄这块空间才会真正释放。排查命令是lsof L1 lsof | grep deleted fuser -v /var/loglsof L1是专门列出所有 link count 为 0 但仍被进程打开的文件也就是“删了但没释放”的文件。输出里能看到进程 PID、命令名、文件路径。找到之后重启对应的进程或服务空间就可能立刻释放。fuser的作用和场景稍有不同它更擅长检查哪些进程正在使用某个指定文件或目录。在你想删掉某个目录却提示 “device or resource busy” 的时候用fuser -v /path就能看到是谁在占用。2.4 find按文件大小快速定位“巨无霸”有些场景你并不知道是哪个目录大只想知道全盘最大的几个文件是谁。这时候find比du更直接find / -xdev -type f -size 100M -exec ls -lh {} \; | sort -k5 -rh | head -20这个命令的意思是在/下、不跨文件系统-xdev找出大于 100MB 的普通文件列出详细信息并按大小排序只显示前 20 个。-xdev和du里的-x作用相同都是为了不跑到挂载的其他磁盘里去。这条命令一口气把整个根分区上的大文件全捞出来是“直捣黄龙”的高效做法。3. 从“满”到“查出真凶”的完整实操流程3.1 第一步先用 df 确认根分区状态无论你之前听到了什么消息先自己上手确认一遍。执行df -h重点关注下面这类输出Filesystem Size Used Avail Use% Mounted on /dev/mapper/centos-root 50G 50G 20K 100% /看到 Use% 100% 就意味着根分区真的满了。注意 Avail 只剩 20K这种情况系统已经处于危险状态连 root 用户创建临时文件都费劲必须马上处理。如果你看到/这一行的 Avail 明明是 0但df -i显示 inode 使用率也不低那就还要关注一下 inode 是否也接近上限df -i如果 inode 满了即使空间还有剩余也会表现出“磁盘已满”的症状。这个双确认能帮你避免方向性错误。3.2 第二步du 逐层深入锁定重量级目录确定是根分区问题之后执行du -x -h --max-depth1 / 2/dev/null | sort -hr | head -202/dev/null是为了过滤掉大量“Permission denied”的报错因为有很多目录普通权限进不去。如果当前用户权限不够输出的结果会不完整建议直接使用能 sudo 的用户操作别用普通用户排查到一半发现什么都看不到。实际输出大概长这样15G / 12G /var 2.1G /usr 500M /root 300M /tmp ...一目了然问题集中在/var那就继续往下钻du -x -h --max-depth1 /var 2/dev/null | sort -hr | head -20如果看到/var/log特别大再深入一层du -h --max-depth1 /var/log 2/dev/null | sort -hr | head -20这个逐层下钻的过程就像剥洋葱每层都能过滤掉大部分无关目录很快就能定位到真正吃空间的目标。3.3 第三步按场景深入常见大户目录不同机器有不同的“大户”目录这里说几个我实际处理过的高频场景。场景一日志撑爆/var/log典型情况是/var/log/journal目录巨大。journal 是 systemd 的日志服务默认情况下不会限制日志文件的总大小跑得久的服务器能攒出几十 GB。journalctl --disk-usage journalctl --vacuum-size200M第一条命令查看当前 journal 日志占用第二条把它们清理到 200MB 以内。这个操作对系统没有实质性影响只是清理历史日志可以放心执行。场景二Docker 容器日志和镜像占用/var/lib/docker如果机器是 Docker 服务器/var/lib/docker经常是磁盘杀手。容器长期运行会不断写 stdout 日志默认的 json-file 驱动不会自动切割日志文件能膨胀到 GB 级别。先查看占用du -h -d 1 /var/lib/docker 2/dev/null | sort -hr | head -10对于容器日志可以在/etc/docker/daemon.json里配置 log-opts 限制单容器日志大小{ log-driver: json-file, log-opts: { max-size: 50m, max-file: 3 } }修改后重启 docker 才生效这个配置对后续的容器有效已经产生的旧日志还需要用truncate -s 0或清理工具手动处理。对于不再使用的镜像和悬空卷用下面的命令清理docker system df docker image prune -a docker system prune -f注意docker system prune -f会删除所有停止的容器、无用网络、悬空镜像和构建缓存。如果机器上有需要保留的停止容器执行前一定要先确认。场景三/tmp 和 /root 下的隐藏大文件很多人忽略/root目录。备份包、下载的安装包、生成的 core dump经常静悄悄地躺在/root里。别忘了在/root下也执行一遍 dudu -x -h -d 1 /root 2/dev/null | sort -hr | head -10同时建议用ls -la /root看看有没有名字很长的隐藏文件很多粗心操作留下的核心转储文件名都是一长串时间戳。3.4 第四步处理“已删除但未释放”的文件如果du排查了半天发现根分区占用并没超出预期但df依然显示满了那问题大概率出在“文件已删除但进程仍持有句柄”上。用下面这条命令直接搜出所有相关文件lsof L1重点看其中的/var/log或各种.log文件行。比如看到java 12345 root 45w REG 253,0 2075893452 0 /var/log/xxx.log (deleted)就说明 PID 12345 的 Java 进程还抱着一个已经删掉的日志文件不放占着两个多 GB 空间。处理方法分两种如果进程可以重启重启那个服务句柄释放空间立即归还。如果进程不能重启选择对路径执行: /proc/12345/fd/45之类的截断操作通过/proc/PID/fd把文件内容清空而不是尝试新建同名文件因为旧的句柄指向的 inode 已经被标记删除了。fuser也可以帮你确认占用者fuser -v /var/log/xxx.log它会显示出使用该文件的具体进程帮你决定到底重启谁比较合适。3.5 第五步整理一份可以直接抄的排查命令速查表把上面所有步骤浓缩成一张表贴在键盘边上下次排查直接抄作业目的命令说明查看磁盘空间概况df -h先看根分区是否真满查看 inode 使用情况df -i避免 inode 满导致的假“磁盘满”统计根分区一层子目录占用du -x -h --max-depth1 / | sort -hr-x不跨文件系统结果才准继续深入嫌疑目录du -h --max-depth1 /var/log | sort -hr逐层下钻推荐用 sudo全盘找超大文件find / -xdev -type f -size 100M | head -20配合 ls -lh 可以看到具体大小找出已删除但未释放的文件lsof L1定位到 PID 后重启对应进程查看某个目录被哪些进程占用fuser -v /目录适用“资源被占用”场景清理 journal 日志journalctl --vacuum-size200M直接限制日志体积查看 Docker 各类占用docker system df快速了解镜像、容器、卷、缓存情况清理 Docker 悬空对象docker system prune -f慎用先确认再执行这张表是一个完整的最小闭环先df确认问题再du定位目录用find找大文件用lsof找隐藏占用最后“对症下药”清理再用df验证结果。实际上大多数根分区爆满的现场照这个顺序走一遍20分钟内就能搞定。4. 实操中容易踩的坑与解决思路4.1 du 和 df 的统计结果不一致该信谁很多人在排查时会发现一个让人困惑的现象du统计整个根目录所有文件加起来远远不到df显示的已用空间。于是开始怀疑自己是不是漏看了什么甚至把/下所有目录都翻一遍也找不出差别。出现这种偏差原因通常有三个文件系统保留块。ext4 等文件系统默认会预留 5% 的空间给 root 用户防止系统因空间耗尽而无法启动。这部分空间在df里不显示为可用空间但也没被任何文件占用du自然算不出来。已删除但未释放的文件。du是按目录遍历文件名来统计的已经删除的文件在目录里看不到du就统计不到但df是按文件系统块使用情况计算的只要进程还持有句柄空间就算作已用。所以df比du大先跑lsof L1查验。挂载点遮蔽。某个目录下面挂载了其他分区du加-x会跳过它们不加-x又会把其他分区的占用也算进来。理解挂载关系选择正确的统计方式才能得到有参考价值的结果。遇到du和df不一致时我的原则很明确df是磁盘空间是否充足的最终裁决者du只是辅助定位角色的工具。两者不一致优先查隐藏占用而不是重复翻目录。4.2 误删重要文件后的补救与预防清理磁盘空间时误删数据是很多人的噩梦。我自己也犯过类似的错误在find出来的大文件列表里看到/data/backup/old_backup.tar.gz很大顺手rm掉结果后来才发现是生产环境需要的归档备份。如果误删发生在 ext4 文件系统上第一时间卸载分区或至少改为只读挂载再用debugfs尝试找回已被删除的 inode。但这里要泼点冷水如果你的系统一直在高强度写入文件块很快就会被覆盖恢复成功率并不高。这就是为什么磁盘清理的基本原则是“先备份再删除”不确定用途的文件宁可先挪到另一个分区也不要直接rm。实际操作中我建议处理大文件前先执行du和file命令确认文件类型再看文件的创建时间、路径、属主信息给自己足够多的决策依据动手之前再问自己一句“删了之后如果出问题我能不能恢复”答案是否定的就不要删。4.3 清理日志千万别只靠 rm要用 logrotate很多新手处理日志目录过大时第一反应是rm -rf /var/log/xxx.log这种做法有两个副作用第一如果服务进程持有该文件句柄rm之后空间根本不会释放这就是上一节说的情况。第二就算空间释放了日志文件被直接删掉之后有些应用会“找不到日志文件”而拒绝写日志需要重启进程才能恢复。更稳妥的做法是使用日志轮转机制。Linux 下的logrotate是标准解决方案配置在/etc/logrotate.d/目录下。核心思路是把当前日志改名轮转、压缩旧日志、保留指定份数最关键的是会通知进程重新打开日志文件。比如常见配置/var/log/myapp/*.log { daily rotate 7 compress missingok notifempty copytruncate }copytruncate选项特别适合不能被重启的应用它的原理是先复制一份日志文件再把原文件截断为空进程的句柄不受影响日志继续写入原文件不用重启服务。这一招在处理 java、python 等不能轻易重启的服务时非常实用。4.4 inode 满了比磁盘满更棘手磁盘还剩几个 GB但创建文件时却提示空间不足这种场景对新手来说很费解。原因就是 inode 耗尽。文件系统里除了数据块还要为每个文件或目录分配一个 inode用来存储文件的元数据权限、属主、大小、时间戳等。小文件越多inode 消耗越快。检查方法就是df -idf -i如果IUse%达到 100%即使df -h显示还有空间你也无法创建新文件。常见元凶是邮件队列、squid 缓存目录、containerd 的临时目录等产生了海量小文件。处理思路是找到小文件数量巨大的目录批量删除无用文件或者调整文件系统参数。补救动作本身不算复杂但定位过程比空间满还要耗费精力因为du -h按容量排序时根本不会把这些小文件放在前面。可以先查 inode 占用最重的目录find / -xdev -printf %h\n | sort | uniq -c | sort -k1 -nr | head -20这条命令统计每个目录下的文件数量按数量排序帮你定位 inode 占用大头。4.5 swap 文件和系统休眠文件的隐形占用还有一类较少见但确实存在的场景系统配置了休眠功能或者用户手动创建了大 swap 文件。比如/swapfile如果当初创建时给得很大而系统休眠镜像又默认写在根分区里这个文件会安静地吃掉十几个 GB。这类文件不属于日志也不属于应用数据非常容易被忽略。排查时用df看到根分区整体偏大用du又查不到对应的大目录这种情况下就去根目录下看有没有 swap 文件、休眠镜像文件ls -lh /swapfile ls -lh /var/lib/systemd/swapfile如果确认是 swap 文件且确实不需要那么大可以用swapoff /swapfile关闭交换空间删除文件重新创建更小尺寸的 swap 文件再swapon启用。需要注意的是调整 swap 涉及系统内存交换策略生产环境操作前要评估好风险。5. 根分区长期健康维护别等爆满才想起排查5.1 建立常规巡检意识根分区爆满这种事最怕的是被动响应。每次都是别人报警了才去排查不仅紧急而且容易因为时间压力做出错误的清理决定。更合理的思路是建立常规巡检。最简单的做法是把df和关键目录的du放进定时任务每天自动记录0 8 * * * df -h /var/log/disk_usage.log 0 8 * * * du -x -h --max-depth1 / /var/log/disk_daily.log每天扫一眼这两个文件就能清楚看到根分区的增长趋势。哪个目录增长特别快提前动手清理完全不用等到爆满再急救。如果公司有条件可以在监控系统里加一个磁盘使用率超过 80% 的告警那当然更好。5.2 从分区规划上解决问题排查做得再好也只是治标。根分区频繁爆满本质上通常不是“命令不够熟练”而是分区规划不合理。运维初期就该考虑这几件事给/var单独分一个区这样日志再怎么疯长都不会拖垮根分区给/home单独分一个区用户数据不影响系统盘数据库数据目录不要放在根分区启用 LVM让分区可以在线扩展哪天空间真的不够也不用迁移数据重装系统直接扩容就行。如果条件有限没有办法修改分区方案至少可以给日志配置logrotate加上大小限制给 journal 日志配置上限。systemd 的 journal 上限在/etc/systemd/journald.conf里配置SystemMaxUse500M配置完之后重启 journald 生效。这些动作花不了多少时间但能在很长一段时间内让你不用再半夜爬起来处理磁盘告警。5.3 我自己的日常使用习惯这些年处理了大大小小几十个磁盘空间问题之后我给自己定了几条规矩重要的日志目录全部用 logrotate 管理坚决不用裸rm清理。Docker 的容器日志从创建容器时就加上--log-opt max-size50m --log-opt max-file3限制。下载的安装包、备份文件统一放到~/downloads目录而不是散落在/root。每次做清理操作前先du -sh确认目标大小再记住它原来的大小清理后回头对比一下释放量。这几条看着简单但长期坚持下来你会发现磁盘空间问题出现的频率低多了。就算真的出现也能很快定位到原因而不是在服务器上盲目乱翻。排查根分区爆满这件事说到底是个熟能生巧的活思路清晰命令熟练心态稳定一层层往下剥真相总会浮出来。最关键的一点是别慌别一看到100%就顺手删东西先搞明白空间被谁占了、为什么占、删了会不会释放、删了会不会出事这四个问题都想清楚了再动手。掌握了这套方法根分区爆满就不是什么吓人的事故只是个普通的日常问题而已。
返回列表