LVM 逻辑卷扩容:在线扩大 Linux 文件系统容量
磁盘空间告警时,最危险的动作不是“不扩容”,而是没有确认设备映射就执行扩容命令。Linux 中同一个挂载点可能经过分区、LUKS、LVM PV、VG、LV、文件系统和挂载层。扩容的正确顺序是先辨认真实路径,再确认容量来自现有 VG 空闲、已扩大的底层磁盘,还是一块新磁盘;最后分别扩大逻辑卷和文件系统。
本文以常见 ext4 与 XFS 为例。<挂载点>、<卷组名>、<逻辑卷名>、<PV设备>、<新磁盘设备> 都是占位符,必须替换为真实对象。在线扩容会修改存储元数据和文件系统,操作前需要业务备份、变更窗口和可用回滚路径。尤其要记住:扩容通常可在线完成,但缩容远比扩容危险;XFS 不支持缩小。
一、先确认文件系统的真实来源
先从挂载点开始,而不是猜测设备名。df 显示容量和使用率,findmnt 显示挂载源和文件系统类型,lsblk 显示设备拓扑。
#!/usr/bin/env bash
set -euo pipefail
MOUNT_POINT="<挂载点>"
df -hT "$MOUNT_POINT"
findmnt -T "$MOUNT_POINT" -o TARGET,SOURCE,FSTYPE,OPTIONS
lsblk -o NAME,KNAME,TYPE,SIZE,FSTYPE,MOUNTPOINTS
例如 findmnt 可能显示 /dev/mapper/vgdata-lvapp,也可能显示 UUID 或加密映射。后续命令必须使用经过确认的 LV 或 PV;不要把 df 中显示的挂载名直接当成磁盘分区。
解析设备的符号链接并查看 LVM 映射关系。
MOUNT_POINT="<挂载点>"
SOURCE="$(findmnt -no SOURCE -T "$MOUNT_POINT")"
echo"source=$SOURCE"
readlink -f "$SOURCE"
sudo pvs -o pv_name,vg_name,pv_size,pv_free
sudo vgs -o vg_name,vg_size,vg_free,pv_count,lv_count
sudo lvs -a -o vg_name,lv_name,lv_path,lv_size,lv_attr,devices
若挂载源不是 LVM LV,例如直接是 /dev/sda2、NFS、CephFS 或容器 volume,则本文的 lvextend 流程不适用。必须先停止,改用对应存储类型的扩容方法。
二、确定文件系统类型和在线能力
ext4 可通过 resize2fs 在线扩大已挂载文件系统;XFS 通过 xfs_growfs 对挂载点在线扩大。两者命令和缩容能力不同,绝不能混用。先确认版本和文件系统工具。
findmnt -T <挂载点> -no SOURCE,FSTYPE,OPTIONS
resize2fs -V 2>&1 || true
xfs_growfs -V 2>&1 || true
lvm version
对 ext4,不要在已挂载且读写的文件系统上运行 e2fsck;全面一致性检查需要离线窗口。对 XFS,xfs_repair 同样不是在线检查工具。在线扩容前的重点是设备、挂载和空间关系正确,而不是为了“保险”对生产挂载点跑修复命令。
三、先确认容量来自哪里
最简单情况是 VG 已有足够空闲空间。将 VG Free 与计划扩容量相比;注意 G、GiB 与 PE 的换算,生产变更应保留一定余量,避免把所有 VG 空间一次性用完。
sudo vgs <卷组名> -o vg_name,vg_size,vg_free,vg_extent_size,vg_free_count
sudo lvs <卷组名>/<逻辑卷名> -o lv_name,lv_size,lv_path,segtype,devices
若 VG Free 不足,先判断底层虚拟磁盘是否已在云平台或存储侧扩大,或者是否需要新建 PV。不要直接对错误的块设备执行 pvresize 或 pvcreate。
记录当前块设备大小、分区表和内核识别容量。
lsblk -b -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS <PV设备>
sudo parted -s <底层磁盘设备> unit GiB print
sudo blockdev --getsize64 <PV设备>
parted print 是只读检查。若底层磁盘大小还没有变化,Linux 侧无论执行多少次 pvresize 都不会凭空产生可用空间。
四、扩容前制作可恢复的元数据备份
LVM 会在 /etc/lvm/backup 与 /etc/lvm/archive 保存元数据,但生产变更前仍应主动导出指定 VG 配置并保存当前状态。这个备份只能帮助恢复 LVM 元数据,不能替代业务数据备份或文件系统快照。
#!/usr/bin/env bash
set -euo pipefail
VG_NAME="<卷组名>"
BACKUP_DIR="<备份目录>/lvm-$(date +%Y%m%d-%H%M%S)"
install -d -m 0700 "$BACKUP_DIR"
sudo vgcfgbackup -f "$BACKUP_DIR/$VG_NAME.conf""$VG_NAME"
sudo pvs -a -o+pv_uuid > "$BACKUP_DIR/pvs.txt"
sudo vgs -a -o+vg_uuid > "$BACKUP_DIR/vgs.txt"
sudo lvs -a -o+lv_uuid,devices > "$BACKUP_DIR/lvs.txt"
findmnt -T <挂载点> > "$BACKUP_DIR/findmnt.txt"
确认备份文件可读、备份目录不位于即将扩容的单点存储上,并确认业务已有可恢复的数据备份。若数据一致性要求高,备份或快照应由应用、数据库或存储平台在其一致性边界内完成。
五、路径一:VG 已有空闲空间
先做测试执行。lvm 的 --test 会完成元数据计算但不写入元数据,适合检查计划是否可行;它不是对文件系统扩容的完整 dry-run。
sudo lvextend --test -L +50G <卷组名>/<逻辑卷名>
sudo lvs <卷组名>/<逻辑卷名> -o lv_name,lv_size,lv_path
确认无误后先扩大 LV,再扩大文件系统。不要使用 lvreduce 作为“回滚”手段;错误缩小逻辑卷会直接截断文件系统数据。
sudo lvextend -L +50G <卷组名>/<逻辑卷名>
sudo lvs <卷组名>/<逻辑卷名> -o lv_name,lv_size,lv_path,devices
对于 ext4,使用 LV 路径执行 resize2fs。设备路径以 lvs 输出为准,常见形式为 /dev/<卷组名>/<逻辑卷名>。
sudo resize2fs /dev/<卷组名>/<逻辑卷名>
df -hT <挂载点>
对于 XFS,xfs_growfs 的参数是挂载点,而不是块设备。XFS 的在线扩容会使用底层已扩大的设备空间。
sudo xfs_growfs <挂载点>
df -hT <挂载点>
每一步都应立刻验证。LV 已扩大而文件系统未扩大时,df 不会增长;文件系统命令失败时应保留现场与输出,不要反复尝试不同参数。
六、路径二:底层磁盘或分区已经扩展
云磁盘、SAN LUN 或虚拟机虚拟磁盘扩大后,内核可能需要重新读取容量。下面命令只观察当前块设备和内核日志,不修改分区表。
lsblk -b -o NAME,SIZE,TYPE <底层磁盘设备>
sudo blockdev --getsize64 <底层磁盘设备>
journalctl -k --since '-20 min' --no-pager | grep -Ei 'capacity|rescan|sd |nvme' || true
某些 SCSI 设备可使用 sysfs rescan;路径必须与真实设备对应。此操作会让内核重新扫描容量,不会自动修改分区、PV 或文件系统。
echo 1 | sudotee /sys/class/block/<磁盘名>/device/rescan
sudo partprobe <底层磁盘设备>
lsblk -b -o NAME,SIZE,TYPE <底层磁盘设备>
若 PV 建在整块磁盘上,内核看到更大容量后可先测试 pvresize,再执行实际 pvresize。
sudo pvresize --test <PV设备>
sudo pvresize <PV设备>
sudo pvs <PV设备> -o pv_name,pv_size,pv_free,vg_name
若 PV 建在分区上,必须先扩大正确分区。修改分区表属于高风险操作:需要确认分区号、起始扇区不变、磁盘备份、云平台快照和维护窗口。parted 的 resizepart 只修改分区边界,不会自动执行 pvresize。
sudo parted <底层磁盘设备> print
sudo parted <底层磁盘设备> resizepart <分区号> 100%
sudo partprobe <底层磁盘设备>
lsblk -o NAME,SIZE,TYPE <底层磁盘设备>
执行后仍需先运行 pvresize --test,再运行 pvresize。确认 VG Free 增加后,回到前一节用 lvextend 与对应文件系统命令完成扩容。
七、路径三:新增一块磁盘加入 VG
新磁盘加入 LVM 会覆盖其原有签名或数据。执行前必须用 lsblk、blkid 和 wipefs 确认设备确实为空闲新盘,不能根据设备名猜测;云环境中还要核对卷 ID、序列号和挂载关系。
sudo lsblk -f <新磁盘设备>
sudo blkid <新磁盘设备> || true
sudo wipefs -n <新磁盘设备>
sudo udevadm info --query=all --name=<新磁盘设备> | grep -E 'ID_SERIAL|ID_WWN|DEVNAME'
wipefs -n 只是 dry-run,会列出签名而不修改。只有确认设备没有需要保留的数据,并有审计批准后,才能创建 PV。
sudo pvcreate <新磁盘设备>
sudo vgextend <卷组名> <新磁盘设备>
sudo pvs -o pv_name,vg_name,pv_size,pv_free
sudo vgs <卷组名> -o vg_name,vg_size,vg_free
pvcreate 与 vgextend 会永久修改 LVM 元数据和磁盘签名。若误选设备,影响可能跨越多个业务文件系统;执行前应由第二人复核设备序列号、VG 名和变更单。
扩展完成后,使用前述 lvextend 加 resize2fs 或 xfs_growfs,而不是让新 PV 长期闲置却误以为挂载点已经增长。
sudo lvextend --test -L +50G <卷组名>/<逻辑卷名>
sudo lvextend -L +50G <卷组名>/<逻辑卷名>
sudo xfs_growfs <挂载点>
最后一行仅适用于 XFS;若文件系统是 ext4,应改为 resize2fs 对应 LV。文件系统类型必须以 findmnt 的实际结果为准。
八、处理 PE、条带、镜像和 thin pool
LVM 按 Physical Extent 分配空间。多数场景只需指定 +50G 或 +100%FREE,但条带卷、RAID LV、cache、VDO、thin pool 和快照卷的可用空间与风险不同。先检查 segment type 与 thin pool 使用率。
sudo lvs -a -o lv_name,lv_size,lv_attr,segtype,devices,data_percent,metadata_percent,copy_percent <卷组名>
sudo vgs <卷组名> -o vg_name,vg_free,vg_extent_size,vg_free_count
thin pool 的 Data% 或 Meta% 接近满时,优先扩容 thin pool 或采取平台建议的空间治理,不能只扩大 thin LV。thin LV 的虚拟大小扩大不等于物理空间充足,过度分配会在写入时才暴露风险。
查询实际 PV 上的 extents 有助于判断扩容会落在哪块盘。该命令只读。
sudo pvdisplay -m <PV设备>
sudo lvs -o lv_name,lv_size,devices <卷组名>
不熟悉条带或 RAID 布局时,不要强行指定 --alloc、–stripes 或 PV 列表。让 LVM 自动选择通常更安全;若确实有性能与故障域要求,应先在测试环境验证布局。
九、验证不只看 df
扩容完成后要从三层验证:LV 设备大小、文件系统可用空间、挂载点保持不变。还应确认应用进程没有因 I/O 错误或短暂阻塞产生异常。
sudo lvs <卷组名>/<逻辑卷名> -o lv_name,lv_size,lv_path,devices
findmnt -T <挂载点> -o TARGET,SOURCE,FSTYPE,OPTIONS
df -hT <挂载点>
sudo dmesg --level=err,warn | tail -n 100
如果扩容期间应用出现报错,应优先保护数据与业务一致性,保留内核和应用日志。LV 扩容成功并不保证上层数据库、容器运行时或配额配置已自动适配。
对 ext4 可读取超级块中的 block count;对 XFS 可读取几何信息。这里是只读核验,不是文件系统修复。
sudo tune2fs -l /dev/<卷组名>/<逻辑卷名> 2>/dev/null | grep -E 'Block count|Block size' || true
sudo xfs_info <挂载点> 2>/dev/null || true
十、配置监控和阈值
扩容不是容量治理的终点。应监控文件系统可用率、inode 使用率、VG Free、thin pool 数据与元数据使用率、磁盘错误和应用写入失败。Prometheus 指标名称以实际 exporter 暴露的指标为准,不能照抄不存在的指标。
以下脚本适合低频巡检,输出文件系统、VG 和 LV 状态;它不自动扩容,避免误判后改变存储。
#!/usr/bin/env bash
set -euo pipefail
MOUNT_POINT="<挂载点>"
VG_NAME="<卷组名>"
LV_NAME="<逻辑卷名>"
df -hT "$MOUNT_POINT"
df -ih "$MOUNT_POINT"
sudo vgs "$VG_NAME" -o vg_name,vg_size,vg_free
sudo lvs "$VG_NAME/$LV_NAME" -o lv_name,lv_size,lv_attr,segtype,devices,data_percent,metadata_percent
报警阈值要为业务写入峰值、扩容交付时间和快照空间预留缓冲。只在 100% 满时才处理会让日志、数据库和容器运行时进入不可恢复的失败状态。
十一、理解回滚边界
文件系统扩容完成后通常不应尝试在线“回滚容量”。ext4 缩容必须先离线检查和缩小文件系统,再缩小 LV;XFS 无法缩小,只能新建更小文件系统、复制数据、切换挂载点。任何 lvreduce 都应被视为高风险、独立变更,不是本次在线扩容的回滚命令。
对于新增 PV,如果该 PV 还没有承载任何 extents,才可能在审查后撤销 vgextend 和 pvcreate。先检查是否已分配空间;若已分配,pvmove 会产生大量 I/O,并需要足够空闲空间和维护评估。
sudo pvs <新磁盘设备> -o pv_name,vg_name,pv_used,pv_free
sudo pvdisplay -m <新磁盘设备>
只有在确认新 PV 未使用、业务不受影响并有 LVM 元数据备份时,才考虑撤销。以下命令会修改元数据和磁盘签名,不能作为日常演练命令。
sudo vgreduce <卷组名> <新磁盘设备>
sudo pvremove <新磁盘设备>
若任何一步失败,停止继续执行,保留输出并使用 vgcfgrestore 方案由具备 LVM 经验的人员处理。vgcfgrestore 也会改写元数据,必须依据已验证备份和精确 VG/PV UUID 操作。
十二、将扩容做成可审计的变更
一次完整变更记录应包含:初始 df、findmnt、lsblk、pvs/vgs/lvs 输出;数据备份或快照编号;目标增量;选择的路径;实际命令与时间;执行者和复核者;扩容后验证;是否发现内核 I/O 错误;下一次容量告警阈值。
扩容成功的标准不是某条 lvextend 返回零,而是目标挂载点容量增长、设备路径与挂载关系未改变、业务写入正常、VG 或 thin pool 仍有合理余量、监控已恢复并且团队知道该空间来自哪块底层存储。
十三、几个容易造成事故的误区
第一,不要把 df 显示的文件系统容量与底层磁盘容量混为一谈。云盘扩大后,分区、PV、VG、LV 和文件系统通常都需要各自确认;缺少任何一层,最上层 df 都不会变化。第二,不要把 lvextend 成功误认为业务已扩容。若文件系统命令未运行、运行在错误设备上,或者挂载点不是预期 LV,应用仍会看到原容量。
第三,不要为了腾出少量空间尝试 lvreduce。缩小路径需要先缩文件系统再缩 LV,并且 XFS 根本不支持缩小;生产系统中更常见、更安全的方案是新建较小文件系统、迁移数据、校验、切换挂载和保留回退窗口。第四,不要把 LVM snapshot 当成永久备份。快照消耗 COW 空间,写入高峰时可能快速满;快照失效对业务一致性和备份策略都不能视而不见。
十四、扩容后的容量治理
一次成功扩容通常意味着需要重新评估告警阈值、增长率和责任边界。业务目录、日志目录、容器镜像、数据库表空间、模型权重和临时文件可能都在同一挂载点竞争容量;仅靠总使用率告警无法说明是谁在增长。应在业务允许的范围内建立目录级容量观察、日志轮转、对象存储归档和应用配额,并将扩容交付时间纳入告警提前量。
LVM 层还应保留一定 VG Free,尤其是 thin pool、快照、未来应急扩容和迁移需要空间时。把 VG Free 全部切给某个 LV 虽然能立刻解除告警,却会让下一次故障只能依赖新增磁盘或高风险迁移。容量计划的目标不是让 df 永远接近满,而是让存储层次、增长来源、可用余量和回滚边界始终清楚。