ARTICLE DETAIL

资讯详情

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

df -h 全解析:Linux 磁盘空间、inode 满与虚拟机扩容排查

df -h 全解析:Linux 磁盘空间、inode 满与虚拟机扩容排查 凌晨两点被磁盘告警叫起来登录机器第一件事就是敲df -h这条命令大概是所有和服务器、虚拟机、甚至自己笔记本打过交道的人闭着眼睛都能敲出来的那条。但真正让我花时间的地方从来不是敲它而是看懂它吐出来的那几行数字——为什么明明Use%只有 78%服务却报磁盘空间不足为什么删了几个 G 的日志df -h里的数字一点没动为什么 Ubuntu 虚拟机扩了盘进去一看可用空间还是老样子这篇就把df -h从敲出来到读明白再到能动手处理这条链路完整捋一遍顺带把 Linux 系统怎么看磁盘空间、虚拟机扩盘后为什么看不到新空间、桌面端提示磁盘空间不足该怎么下手这些高频问题一起说清楚。内容偏实操命令可以直接抄适合刚接手服务器的新人也适合做了几年但一直靠重启大法解决问题的老手对照着查漏补缺。1.df -h的输出到底该怎么读1.1-h只是换了个单位别把它当成万能df这个命令来自 coreutils名字是 disk free 的缩写作用是读取内核维护的文件系统统计信息。默认输出是以 1K 块为单位的裸数字看一列八位数没人受得了所以-hhuman-readable就成了默认习惯——它把数字换算成 K、M、G、T 这种人类能一眼扫过的单位。但这里有个很多人没注意的细节-h走的是 1024 进制而-H走的是 1000 进制。硬盘厂商标称容量用的是 1000 进制所以一块标着 500G 的盘df -h里大概显示 465G 左右这个差额不是谁偷了你的空间纯粹是两套计数体系打架。我在给非技术同事解释为什么买来 500G 只剩 465G的时候基本都用这个例子一次就说明白。更值得警惕的是-h的取整。当一个分区还剩 900M 的时候df -h会显示1.0G看着挺宽裕实际上再写点东西就爆了。所以做容量判断尤其是写监控脚本、算阈值的时候我一律不用-h改用df -k或者df -B M拿到精确的数值再自己格式化。看到1.0G这种刚好卡在整数边界的数字心里要默认它可能已经接近临界值了。还有一个实用参数是-T它会在输出里多加一列文件系统类型ext4、xfs、overlay、tmpfs一目了然。后面排查扩容问题时这列非常关键因为 ext4 和 xfs 的扩容命令完全不一样看错了会白折腾半天。df -hT1.2 Size、Used、Avail 三个数字对不上是正常的新手第一次认真看df -h输出时最常见的困惑是为什么Size减去Used不等于Avail比如 Size 是 100GUsed 是 40GAvail 却只有 55G中间那 5G 去哪了答案在 ext4 的预留块机制。ext4 默认会拿出文件系统总容量的 5% 留给 root 用户专用这部分空间在df里不计入Avail普通用户写不进去但 root 进程在磁盘写满的危急时刻还能落盘、还能生成日志不至于把系统彻底锁死。这个设计救过很多人——磁盘满了但 root 还能登录、还能改配置、还能删日志。想确认预留了多少可以看tune2fs -l /dev/sda1 | grep -i reserved输出里的Reserved block count就是预留块数量乘上块大小就是预留的字节数。如果这是个纯数据盘跑的都是普通用户进程留着 5% 纯粹是浪费可以考虑下调tune2fs -m 1 /dev/sda1这条命令把预留比例从 5% 改成 1%一个 1T 的盘直接多出 40G 可用空间。但根分区千万别这么干-m 0更是要慎之又慎见过有人为了多挤出几十个 G 把根分区预留清零结果某天日志写爆sshd 都起不来只能挂恢复模式救机器。这也解释了一个经典现象普通用户保存文件报设备上没有空间root 执行同样操作却成功。不是权限问题是预留块在起作用。看Use%这一列要养成习惯它算的是Used / (Used Avail)已经把预留排除在外了所以Use%到 100% 才是真的写不进去。1.3Mounted on这一列藏着的坑最深df -h的最后一行是挂载点。绝大多数情况下它很老实但有几个场景会让人看走眼。第一种是同一个设备挂了多个挂载点比如用 bind mount 把/data/logs又挂到/var/log下面那/var/log会单独出现一行容量和/data一模一样。这时候你在/var/log下删文件看着df里/data的空间涨了别慌那是同一块盘。第二种是挂载点遮罩。假设/data目录本身在根分区上之前往里面写了 20G 数据后来运维把一块新盘挂到了/data。挂载之后df -h里根分区看起来空间被莫名占用但你du遍历/data却找不到那 20G。原因就是原来的数据被新挂载的设备盖住了文件还在只是看不见。第三种是容器环境。Docker 默认用 overlay2 存储驱动df -h会吐出一大串overlay行每条都指向容器内的某个路径容量显示的是底层根分区的容量。刚接触容器的人看到一个 20G 的根分区下面挂了三十个 overlay 全显示 20G容易误以为是三十个独立磁盘其实它们共享同一个底层文件系统写满了是一起完蛋。2. 三种满长得一样处理方式完全不同2.1 块满了最直白的那种这是最常见的情况df -h里某个分区的Use%顶到 95% 以上甚至 100%Avail接近零。根因无非几类日志没做切割、临时文件没清理、用户上传目录失控、数据库文件膨胀、某个程序把日志重复写了一遍又一遍。这种情况往下走就是定位大目录、找大文件、做清理路径清晰第三节会详细说。2.2 inode 满了df -h显示还有空间你却写不进去这是最容易让人懵的一种。df -h显示根分区还剩 30G但新建文件报No space left on device。这时候敲df -i看IUse%那一列。如果它到 100% 了就是 inode 耗尽。inode 是文件系统给每个文件分配的元数据结构存的是权限、时间戳、指向数据块的位置等信息。ext4 在格式化的时候就把 inode 总数定死了之后不能增加。所以如果你有一个目录里面躺着几百万个小文件——比如某个会话缓存目录、某个从来没清理过的邮件队列、某个爬虫程序产生的碎文件那么即使总容量还很空inode 先用完了。我遇到过一次非常典型的场景某服务把 access log 按每分钟一个文件切割还带日期后缀跑了两年单目录里堆了上百万个几 KB 的小文件df -h看着还有一半空间df -i早就红了。查哪个目录文件最多for d in /var /home /data; do echo $d: $(find $d -xdev -type f 2/dev/null | wc -l); done或者按目录逐层统计find / -xdev -type f -printf %h\n 2/dev/null | sort | uniq -c | sort -rn | head -20-xdev这个参数很重要它保证不跨越文件系统边界否则会把挂载的子盘也算进来数字完全失真。处理 inode 满没有别的办法只能删小文件。如果确有业务需要保留海量小文件那就得在设计阶段换个方案比如用对象存储、把多个小文件打包成大文件、或者改用 XFSXFS 的 inode 是动态分配的不像 ext4 那样固定这是架构层面的取舍靠运维现场清理只能救急。2.3 已删除但没释放删了文件空间没回来删了一个 8G 的日志文件df -h纹丝不动du遍历也找不到那 8G这大概是运维生涯里最经典的灵异事件之一。原理不复杂Linux 下文件的删除是删掉目录项unlink只有当没有进程持有这个文件的句柄时数据块才会真正释放。如果某个进程还开着这个文件的文件描述符在写——比如一个没重启的 java 服务、一个还在输出的 Python 脚本——那文件在文件系统里已经没有名字了但数据块仍然被占用df自然把它算进去du遍历目录树又看不到它两边就打架了。定位方法lsof L1或者用更原始的办法直接扒/procls -l /proc/*/fd 2/dev/null | grep deleted找到之后两种处理方式一是重启那个进程句柄释放空间立即回来二是如果这文件还在被持续写入、重启代价太大可以用truncate通过/proc/PID/fd/N路径把内容清空注意是清空不是删除truncate -s 0 /proc/12345/fd/9千万别对/proc/PID/fd/N直接rm那是删链接本身对释放空间没有任何帮助还会让你误以为处理成功了。这个坑我踩过处理完一看df没变又白排查一遍。注意lsof L1这条命令在文件数极多的机器上可能跑几分钟业务高峰期慎用可以先加-p限定到可疑进程。3. 从告警到回收一套能直接复用的处理流程3.1 第一步永远是先分清块满还是 inode 满收到磁盘空间不足告警别急着删文件按顺序敲三条命令df -hT # 看容量和文件系统类型 df -i # 看 inode 使用率 date # 记下时间方便和监控曲线对齐三条命令下来基本能确定是哪一类问题。如果IUse%高而Use%正常直接跳到第 2.2 节的思路如果Use%高继续往下走。这一步花 10 秒能省下后面半小时的瞎找。3.2 第二步用du由粗到细层层收窄定位大目录的核心思路是逐层下钻而不是一上来就find / -size 1G那玩意儿在全盘遍历时会拖垮 IO。正确姿势是从根目录开始只看第一层du -xhd1 / 2/dev/null | sort -rh | head -20几个参数的用意-x不跨文件系统保证统计范围就是当前这个分区-d1只输出一层避免输出爆炸sort -rh按人类可读单位倒序排head -20只看前二十。假设排在第一的是/var那就接着钻du -xhd1 /var 2/dev/null | sort -rh | head -20再往下/var/log、/var/lib/docker一层层收窄一般三层之内就能锁定元凶。这套方法比ncdu慢一点但胜在任何机器上都有不用装东西。如果是有网的环境ncdu的交互式界面确实更舒服方向键点进去点出来还能直接删。磁盘 IO 本身就紧张的时候du会跑得很慢可以给它降优先级避免把业务拖垮nice -n 19 ionice -c3 du -xhd1 /var 2/dev/null | sort -rh | head -203.3 第三步常见的几个吃盘大户和对应处理扫下来十有八九是这几个地方目录常见原因处理方式/var/log日志没切割、journald 无上限journalctl --vacuum-size500M配置 logrotate/var/lib/docker镜像、容器层、构建缓存堆积docker system prune -a注意会删掉未使用镜像/var/cache/apt离线包缓存apt clean/tmp程序退出没清理临时文件确认无进程占用后清理配置 systemd-tmpfiles/home/*/.cache编辑器、浏览器缓存用户侧清理或软链到其他盘数据库数据目录binlog、慢查询日志、膨胀的表走数据库自身的清理和归档流程重点说日志。给正在被写入的日志文件做清理一定要用truncate而不是rmtruncate -s 0 /var/log/app/access.log原因和 2.3 节一样rm之后写日志的进程还拿着句柄空间不释放。而且很多进程是按 inode 定位的文件被删掉后它虽然还能写但写入的是一个幽灵文件日志系统里的文件路径已经不存在了出新问题的时候你连日志都找不到追悔莫及。journald 是另一个隐性大户。默认配置下它最多能占到所在文件系统的 10%一个 100G 的根分区就是 10G。想手动压一下journalctl --vacuum-time7d journalctl --vacuum-size1G长期方案是改/etc/systemd/journald.conf里的SystemMaxUse然后重启 journald。这个改动很小但收益明显我在几台日志汹涌的机器上都配了。3.4 第四步回收完一定要验证并且记录下来清理完别急着关终端再跑一次df -h确认数字变化同时df -i也看一眼。有时候块空间回来了但 inode 还是红的说明小文件没删干净。顺手把这次处理记录下来哪个分区、涨到多少、根因是什么、清理释放了多少、有没有做长期措施。这份记录攒上三个月你就会发现磁盘告警来来回回就那么几种下次处理能快一半更重要的是能做预防——在到达阈值之前就把 logrotate、prune 定时任务配好。还有一招是可以提前预防的给根分区和关键数据分区都配上 80% 和 90% 两级告警并且Use%和IUse%两个指标都要监控。只管容量不管 inode 的监控配置迟早会在某个深夜给你惊喜。4. Ubuntu 虚拟机扩了盘df -h却纹丝不动4.1 扩容分三层少做一层都白干这是搜索量特别高的一个问题。在虚拟化平台上把磁盘从 40G 调到 100G开机进系统一敲df -h还是 40G人就懵了。问题在于扩容其实是三个独立动作虚拟磁盘文件本身变大在虚拟化平台侧完成的动作把 vmdk/vdi/vhd 的容量从 40G 改成 100G。分区变大虚拟磁盘尾部多出的 60G 是未分配空间分区表里的分区还是 40G。文件系统变大分区是 100G 了但上面的 ext4/xfs 文件系统还是按原来的大小建的df -h读的就是文件系统的信息。很多人只做了第一步就以为完事了。df -h读的是第三层的信息所以一点点变化都没有这是完全正常的现象不是扩容失败。判断当前卡在哪一层可以用lsblk看块设备结构lsblk如果磁盘容量已经变成 100G 但下面某个分区还是 40G说明卡在第二层如果分区也变成 100G 了但df -h还是 40G说明卡在第三层。4.2 分区扩容growpart比fdisk省心Ubuntu 上最省事的工具是 cloud-guest-utils 里的growpart它专门用来把分区撑到占用后面紧邻的未分配空间growpart /dev/sda 3参数是设备和分区号注意分区号不要带/dev/前缀写成growpart /dev/sda3会报错这个细节坑过不少人。如果系统里没有这个工具apt install cloud-guest-utils不想装工具的话用parted或者fdisk删了重建分区也行操作本身不复杂但步骤多了出错概率也高我的习惯是能用growpart就用它。分区表改完之后内核不一定立即感知有些老版本需要partprobe /dev/sda # 或者 partx -u /dev/sda这一步漏掉的话后面resize2fs会告诉你不认识新的大小。4.3 文件系统扩容ext4 和 xfs 命令不一样分区搞定了接着让文件系统吃满分区# ext4 / ext3 resize2fs /dev/sda3 # xfs xfs_growfs /resize2fs后面跟设备路径xfs_growfs后面跟的是挂载点不是设备这也是常见的命令写错现场。XFS 只能扩大不能缩小ext4 理论上能缩但操作风险高真要缩盘我更倾向于新建一个小盘、把数据迁过去、再换盘。Ubuntu 默认的云镜像、服务器版安装很多用的是 LVM。LVM 场景下多了一层顺序是# 1. 让物理卷吃到新的分区空间 pvresize /dev/sda3 # 2. 用查到的卷组空闲空间扩逻辑卷 vgs # 看清 VFree 有多少 lvextend -l 100%FREE /dev/ubuntu-vg/ubuntu-lv # 3. 扩文件系统 resize2fs /dev/ubuntu-vg/ubuntu-lvlvextend还有个更方便的用法-r参数可以在扩容的同时自动调用对应的文件系统扩容命令省掉第三步lvextend -r -l 100%FREE /dev/ubuntu-vg/ubuntu-lv这个参数我强烈推荐能少一次忘了 resize2fs的机会。4.4 扩完df -h还是没变化按这个顺序查第一确认lsblk里磁盘容量真的变大了。有些虚拟化平台对正在运行的虚拟机扩容需要关机再开机才能让客户机识别到新容量热添加不一定生效。第二确认扩的是不是差分盘或者快照。有些平台的快照链里你扩的是父盘实际运行用的是子盘那自然看不到变化。第三确认分区是不是被后面的分区挡住了。比如/dev/sda2后面紧跟着sda3那sda2即使前面有空间也扩不动growpart会直接报错。这种情况下只能调整分区顺序或者选择其他方案。第四确认resize2fs有没有报Nothing to do!。报了就是文件系统认为没有可扩的空间多半是分区没扩成功或者设备路径写错了。第五多分区的情况下很多人扩容前没确认df -h里根分区对应的到底是哪个设备。看着df -h显示/dev/mapper/ubuntu--vg-ubuntu--lv结果去扩了/dev/sda3发现没变化——其实扩的是物理卷逻辑卷没动第二步的lvextend漏了。把这五条按顺序过一遍基本没有解决不了的扩容问题。5. 桌面端提示磁盘空间不足但不知道空间去哪了5.1 Windows 上等价于df -h的是哪几条服务端习惯的人回到 Windows 上会手足无措其实对应关系很清楚。看每个盘符的容量和使用情况最直观的是图形界面的此电脑命令行等价物是Get-PSDrive -PSProvider FileSystem或者老一点的写法wmic logicaldisk get caption,freespace,size注意这两条命令给的数字单位是字节1T 的盘会显示成 999653638144 这种得自己换算。想知道某个目录多大PowerShell 里Get-ChildItem -Path C:\Users -Recurse -Force -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum这条命令在文件多的目录上会跑很久跟 Linux 的du是一回事慢是正常的。5.2 云盘同步目录为什么特别容易把 C 盘撑满搜索里出现频率很高的一类求助是装了某个云盘客户端之后C 盘莫名其妙少了几十 G然后编辑文档时软件跳出内存或磁盘空间不足的提示。这里要拆开看两个误区。第一个误区是把这个提示当成内存不足。多数办公软件弹的这句话指的是可用磁盘空间不够它需要在本地写临时文件、写自动保存副本、写缓存磁盘一旦接近满写不进去就报这个错跟物理内存没多大关系。第二个误区是以为文件都存在云端本地不该占地方。实际上主流的同步客户端默认会做本地缓存你打开过的文件会留一份在本地方便下次秒开删除文件会进回收站占着配额多设备同时改一个文件会产生冲突副本这些副本每一份都是完整的文件体积再加上客户端自身的版本历史本地占用会比你想的多得多。处理思路是把同步目录整体挪到空间更大的盘客户端设置里把本地缓存上限调低关掉不常用目录的同步定期清空云端回收站。移动同步目录一定要走客户端自带的更改位置功能直接剪切粘贴文件夹客户端会重新把整个目录同步一遍白白下一遍数据还容易产生冲突副本。5.3 磁盘容量显示异常或者干脆不显示还有一类情况是容量压根显示不出来。比如在此电脑里看不到某个盘但在磁盘管理里能看到它处于未分配状态。这就是分区表和文件系统层面的问题了新加的盘还没分区、盘符被误删、分区表被某些操作破坏。这种情况不要用第三方工具乱修先在磁盘管理里确认分区状态有数据的盘动手前务必先做镜像备份。还有一种显示异常系统盘显示 500G但所有可见文件加起来只有 200G。剩下的可能是休眠文件、系统还原点、WinSxS 组件库、以及被隐藏属性保护的系统文件。看休眠文件powercfg /hibernate off关掉休眠会直接释放一个和内存等大的文件16G 内存的机器就是 16G 空间。系统还原点可以在系统属性里限制占用上限。WinSxS 目录千万别手动删要用官方提供的清理命令Dism.exe /online /Cleanup-Image /StartComponentCleanup这条命令会清掉旧版本的组件一般能腾出几个 G而且安全。6. 常见问题速查与踩坑记录6.1 现象、原因、处理速查表现象大概率原因处理方向df -h显示有空间但写不进去inode 耗尽 / 预留块挡普通用户df -i确认tune2fs -l看预留删了文件df -h不变进程持有已删除文件的句柄lsof L1定位重启进程或truncatedf和du结果差很多已删除未释放、挂载点遮罩、稀疏文件优先查lsof L1再查挂载关系扩容后df -h不变只扩了虚拟磁盘没扩分区和文件系统lsblk分层确认growpartresize2fsresize2fs报Nothing to do分区没扩成功或设备路径错了用growpart重做检查lsblk/data下du和预期差很多挂载点遮罩旧数据被新盘盖住卸载后看原始目录清理或迁移Windows 提示磁盘空间不足同步目录缓存、休眠文件、还原点关休眠、清缓存、挪同步目录df -h里一堆 overlay容器存储驱动共享底层文件系统看底层根分区用量docker system prune6.2 几个我踩过或者见别人踩过的坑第一个用rm -rf /var/log/*清理日志。在日志被持续写入的机器上这么干轻则空间不释放重则把目录权限和结构也一起搞乱某些服务的日志目录被删之后不会再自动创建服务重启直接失败。正确做法是truncate -s 0或者find /var/log -type f -name *.log -exec truncate -s 0 {} \;。第二个du -sh /*不带-x。这条命令在挂了多块盘的机器上会把所有挂载点的内容一起统计而且可能重复统计跑出来的数字比实际用量大好几倍看着像发现了巨型文件其实是自己吓自己。养成加-x的习惯。第三个把根分区预留空间设成 0。前面说过这个预留是系统最后的救命稻草尤其在日志写爆、内存吃紧的场景下root 还能写日志、还能改配置。省那一两个 G 不值得。第四个Use%只看容量不看 inode。监控只配了容量告警的迟早会被 inode 满打脸而且 inode 满通常伴随的都是海量小文件清理起来比删几个大日志麻烦十倍。第五个扩容前不备份。涉及分区表操作再熟练也可能出意外尤其是用fdisk删分区重建的路线。有快照的先打快照有重要数据的先备份这是底线不是谨慎过头。第六个df -h里看到tmpfs就以为占了物理磁盘。tmpfs是内存文件系统用多少内存算多少内存跟硬盘没关系/dev/shm、/run这些挂载点显示几 G 容量很正常别去清理它们清理了会出别的问题。6.3 顺手写个轻量巡检脚本告警响应多了之后我习惯在每台机器上放一个只读的巡检脚本有问题的时候一条命令出结果比临时敲五六条命令稳#!/bin/bash # 只读巡检磁盘容量 inode 已删除占用 echo 容量 df -hT | grep -Ev tmpfs|overlay echo inode df -i | grep -Ev tmpfs|overlay | awk $50 80 {print 高 inode 使用:, $0} echo 已删除未释放可能较慢 lsof L1 2/dev/null | head -20 echo 块设备结构 lsblk -o NAME,SIZE,FSTYPE,MOUNTPOINT这个脚本没有任何写操作随便跑适合在排查第一步快速建立现场认知。inode 那行用了 awk 过滤只打印使用率超过 80% 的输出干净很多一眼就能看出问题在哪。最后分享一个我自己判断问题的顺序先df -hT看容量再df -i看 inode两个都正常就往lsof L1和挂载关系上想确认是真的满了还是看起来满了再动手清理。这套顺序用了几年基本没走过冤枉路。至于扩容这件事记住虚拟磁盘、分区、文件系统这三层每次扩完用lsblk和df -h各确认一遍比事后追查省事得多。
返回列表