Linux 磁盘空间排查与 LVM 在线扩容实战:从 df 定位到云盘扩容

对个人站长来说,服务器磁盘满是最常见的"隐形杀手":网站突然打不开、数据库写入报错、后台登录后一片空白,排查半天才发现是磁盘满了。更麻烦的是,很多 VPS 默认分区方案没有预留扩展空间,真到磁盘告急时才发现系统盘分区没法直接扩大。这篇文章从磁盘告警的定位排查讲起,重点分享 LVM 在线扩容的完整实操,以及新数据盘从格式化到开机自动挂载的标准流程,帮你把"磁盘危机"变成"常规操作"。

一、磁盘告警先别慌:三步定位空间去哪了

收到磁盘使用率告警或者发现网站异常,第一步是确认到底哪个分区满了。df 是磁盘空间查看的第一工具:

df -h
df -i

df -h 看空间使用率,df -i 看 inode 使用率。这里有个很多人不知道的坑:有时候程序报 No space left on device,但 df -h 显示还有大量剩余空间,这时候十有八九是 inode 耗尽了。inode 是文件系统用来记录文件元数据的索引节点,小文件特别多的情况下(比如邮件队列、PHP session 目录、缓存目录堆积了上百万个几 KB 的小文件),inode 会先于空间耗尽。df -i 里 IUse% 到 100% 就是这个原因。

确认分区后,用 du 逐层定位大目录和大文件。不要一上来就在根目录跑全盘 du,那样又慢又费 IO,从最可疑的目录开始层层下钻:

du -h --max-depth=1 /var 2>/dev/null | sort -rh | head -10
du -h --max-depth=1 /home 2>/dev/null | sort -rh | head -10
find /var/log -type f -size +100M -exec ls -lh {} \;

个人站长服务器上最常见的空间杀手就那几类:Nginx 和 PHP 的日志文件、MySQL 的 binlog 中继日志、Docker 的 overlay2 存储目录、系统更新缓存、邮件队列。逐一排查基本都能找到元凶。日志类的问题可以用 journalctl 限制系统日志体积,比如只保留 200MB:

journalctl --vacuum-size=200M

Docker 环境里别忘了 docker system df 查看容器、镜像、卷各占多少空间,悬空镜像和废弃构建缓存往往能清出好几个 G:

docker system df
docker image prune -f

二、删除文件后空间没释放?文件被进程占用了

新手最容易困惑的场景:明明 rm 掉了一个 5G 的日志文件,df -h 一看空间一点没变。这是因为文件正在被某个进程写入,rm 只是删除了目录项,进程持有的文件描述符还指向这块空间,要等进程关闭文件或重启后才真正释放。用 lsof 找出这些"已删除但仍被占用"的文件:

lsof +L1 | grep deleted

找到对应的进程 PID 后,重启该服务即可释放空间。如果是 Nginx 的 access.log 这类被常驻进程持续写入的日志,正确做法不是 rm 而是清空:

truncate -s 0 /var/log/nginx/access.log

这也解释了为什么生产环境要配置 logrotate 日志轮转:按天或按大小切割日志、保留固定份数、旧日志自动压缩,从机制上避免日志无限增长。配置在 /etc/logrotate.d/ 下,Nginx 的典型配置长这样:

/var/log/nginx/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 640 nginx adm
    sharedscripts
    postrotate
        [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
    endscript
}

三、LVM 是什么?为什么扩容离不开它

定位并清理完空间后,要考虑根治方案。如果你的系统盘分区是普通分区(没有 LVM),分区扩大非常麻烦,往往只能重装或者迁移数据。而 LVM(逻辑卷管理)把物理磁盘抽象成三层:PV(物理卷)是底层磁盘或分区,VG(卷组)是把多个 PV 汇聚成的资源池,LV(逻辑卷)是从 VG 里划分出来、格式化后挂载使用的逻辑分区。容量不够时,往 VG 里加一块新 PV,再把空间划给 LV,全程在线、不卸载、不重启,数据无损。

如何判断自己的服务器是否用了 LVM?执行 lsblk 看结构,如果看到 vg 相关的名字(比如 vg_data-lv_root、centos-root、ubuntu-vg),说明就是 LVM 布局。很多云厂商的 CentOS/Ubuntu 系统盘默认就是 LVM,这是最理想的情况。如果 lsblk 显示 sda1 直接挂载在 / 下、没有中间层,那就是普通分区,扩容只能靠云厂商的在线扩容功能配合 growpart 处理。

四、LVM 在线扩容完整实操

最常见的场景:服务器加了一块新数据盘,要把容量并进现有的数据卷。假设新盘是 /dev/sdb,现有卷组 vg_data,数据卷 vg_data-lv_data 挂载在 /data 下。第一步,给新盘创建 LVM 分区(也可以跳过分区直接 pvcreate 整块盘,但规范做法是分区,方便以后调整):

fdisk /dev/sdb
# 交互输入:n 新建分区 → p 主分区 → 回车默认 → 回车默认 → t 改类型 → 8e(Linux LVM) → w 保存
partprobe /dev/sdb

第二步,把新分区初始化为 PV 并加入现有 VG:

pvcreate /dev/sdb1
vgextend vg_data /dev/sdb1
vgdisplay

第三步,扩展 LV。可以用固定大小 -L +100G,也可以把 VG 剩余空间全部划进去 -l +100%FREE:

lvextend -l +100%FREE /dev/vg_data/lv_data

第四步,也是最关键的一步:扩展文件系统。很多人 lvextend 做完看 df 没变化,以为失败了,其实 LV 层扩大了,文件系统层还没跟上。不同文件系统命令不一样:XFS 用 xfs_growfs,EXT4 用 resize2fs,千万别混用。

xfs_growfs /data        # XFS 文件系统
resize2fs /dev/vg_data/lv_data   # EXT4 文件系统

最后 df -h 确认容量生效。整个过程服务不需要停止,正在写入的数据也不受影响,这就是 LVM 的价值。注意 XFS 只能扩大不能缩小,EXT4 可以双向调整,规划分区时要想清楚。

五、云盘扩容的经典三步:growpart + pvresize

如果扩容的是云厂商的系统盘(比如在阿里云、腾讯云控制台把系统盘从 40G 升到 80G),流程略有不同。云盘扩容后操作系统里看到的磁盘设备还是原来的大小,需要先让内核重新读取分区表,再让 PV 感知新容量。漏掉任何一步,df 都不会变化。

partprobe /dev/vda
growpart /dev/vda 1
pvresize /dev/vda1
pvdisplay

growpart 用于把分区扩展到磁盘末尾,pvresize 让 PV 吸收新空间。做完这三步后,如果根分区在 VG 里,再执行 lvextend -l +100%FREE 加 xfs_growfs / 或 resize2fs,和上面的流程就接上了。很多教程只写 pvresize 不写 growpart,导致用户照做后空间依然没变,就是这个环节缺失。如果你的 PV 直接建在整块盘上(没有分区表),那 growpart 这步可以跳过,pvresize 直接作用于整盘即可。

六、新数据盘挂载:从格式化到开机自动挂载

没有 LVM 需求时,新盘直接格式化挂载更简单。以一块全新的 /dev/sdb、要挂到 /data 为例:

mkfs.ext4 /dev/sdb
mkdir -p /data
mount /dev/sdb /data

mount 之后 df -h 能看到,但重启就丢了,必须写进 /etc/fstab 实现开机自动挂载。这里强烈建议用 UUID 而不是设备名:设备名在系统重启后可能变化(比如加盘后 sdb 变成 sdc),UUID 是文件系统创建时生成的唯一标识,永远不变。先查 UUID:

blkid /dev/sdb

拿到 UUID 后在 /etc/fstab 末尾追加一行:

UUID=你的UUID值 /data ext4 defaults,noatime 0 2

挂载选项里 noatime 值得加上:它禁止系统在每次读取文件时更新访问时间戳,能减少大量无谓的磁盘写入,对数据库和静态站性能都有好处。写完 fstab 一定要先验证再重启,直接执行 mount -a,如果报错说明 fstab 写错了,立刻修正,否则重启后系统会因挂载失败卡在维护模式。最后一列的数字:0 表示不参与 dump 备份,2 表示开机时按顺序做文件系统检查(根分区是 1)。

七、预防大于救火:监控与巡检

磁盘问题的最高级处理方式是不让它发生。日志轮转、binlog 保留策略、Docker 定期清理这些常规手段做好之后,再加一道监控:写一个简单的巡检脚本放进 crontab,每天检查磁盘和 inode 使用率,超过阈值就发邮件提醒:

0 8 * * * df -h | awk 'NR>1 && $5+0>85 {print "磁盘告警: "$6" 使用率 "$5}' | mail -s "磁盘使用率告警" you@example.com

另外强烈建议养成两个习惯:一是大操作前先做云快照或 LVM 快照,扩容、迁移、删数据前花一分钟拍个快照,出问题随时回滚,这是成本最低的保险;二是每季度做一次磁盘体检,把日志、缓存、备份文件这些"只增不减"的东西过一遍,别让服务器在不知不觉中逼近临界点。

最后补充磁盘健康层面的检查。软件层面的空间问题好解决,硬件层面的磁盘故障才是真正的数据杀手。服务器磁盘出现坏道或即将损坏时,内核日志里通常会有 I/O error 之类的报错,建议定期看一眼系统日志:dmesg | grep -i error。机械硬盘可以用 smartmontools 的 smartctl 查看健康状态,SSD 也有对应的 SMART 属性,执行 smartctl -H /dev/sda 能快速判断磁盘是否已经亮起健康红灯,配合 smartctl -a 还能看到坏道重映射等详细计数。另外建议每季度做一次文件系统检查:在维护窗口先卸载分区再执行 fsck,或者用 tune2fs 设置最大挂载次数让系统自动触发检查,把潜在的文件系统损坏消灭在萌芽阶段。数据无价,磁盘有价,平时多花几分钟做健康检查,能省下数据丢失后彻夜恢复的巨大代价。磁盘管理没有高深技术,靠的就是清晰的排查思路、规范的扩容流程和充足的提前量,做到这几点,磁盘告警就再也不会让你半夜爬起来救火。

Last modification:September 5th, 2026 at 07:57 am

Leave a Comment