暗色模式

LVM 逻辑卷管理:从物理卷到在线扩容与快照回滚

技术教程
2026-08-10
11
0
本文要点
  • LVM 用 PV(物理卷)/ VG(卷组)/ LV(逻辑卷)三层抽象解决传统分区的扩容难、跨盘难问题
  • 在线扩容:先 lvextend 扩展逻辑卷,再 resize2fs 扩展文件系统,全程无需卸载与重启
  • 快照回滚:lvcreate -s 秒级创建快照,lvconvert --merge 一键恢复误删的数据
  • 缩小顺序与扩容相反:必须先 resize2fs 缩小文件系统、再 lvreduce 缩小逻辑卷,且 ext4 缩小前需要卸载
  • 所有命令均在 Ubuntu 24.04(内核 6.8,LVM 2.03)上真实执行验证

为什么需要 LVM

传统分区方案有个让人头疼的痛点:一块磁盘划分好分区后,分区大小就基本固定了。想扩容?要么重新分区并迁移数据,要么依赖 fdisk 在未分配空间上折腾,还常常需要重启;更麻烦的是,一个分区只能落在一块物理磁盘上,两块小硬盘凑不出一个大分区,空间就白白浪费了。

LVM(Logical Volume Manager,逻辑卷管理器) 就是来解决这个问题的:它在「物理磁盘」和「文件系统」之间加了一个虚拟化层,让你把多块磁盘(或分区)汇成一个大的「空间池」,再从这个池子里切出任意大小、任意数量的「卷」来使用。

关于 LVM 的详细概念与背景,可以参考 ArchWiki 上的 LVM 条目,写得很系统。

LVM 的三层结构与核心概念

LVM 由三个抽象层组成,从底向上分别是:

层级名称作用
PVPhysical Volume 物理卷真实磁盘(或磁盘分区)被打上的标签,是 LVM 的基本存储单元
VGVolume Group 卷组若干 PV 汇聚而成的「空间池」,可以跨磁盘
LVLogical Volume 逻辑卷从 VG 里切出来的「逻辑分区」,格式化后挂载使用,就是我们日常打交道的那个

除此之外还有一个单位概念 PE(Physical Extent,物理扩展块),它是 VG 内空间分配的最小单位(默认 4 MiB)。创建 LV 时指定的大小会被向上取整到 PE 的整数倍——后面实测扩容时 +150M 被舍入成 152.00 MiB(38 个 PE),就是它在起作用。

整个关系可以这样理解:

/dev/sdb ──► PV ──┐
                  ├──► VG(空间池)──► LV(逻辑卷)──► mkfs ──► 挂载
/dev/sdc ──► PV ──┘

环境准备:用镜像文件模拟两块磁盘

真实的 LVM 演示通常会拿两块干净的硬盘/分区来做,但演示服务器上只有一块系统盘,直接对它动手既危险也不可逆。这里换一个更安全的做法:用稀疏文件 + loop 设备模拟两块物理磁盘——truncate 创建一个看似 512M、实际几乎不占空间的文件,再通过 /dev/loop 回环设备把它当成块设备用,pvcreate 照常能在上面打标签。

mkdir -p /lvm-demo
cd /lvm-demo

# 创建两块 512M 的"虚拟磁盘"(稀疏文件,实际几乎不占空间)
truncate -s 512M disk1.img disk2.img

# 挂载为 loop 设备,-f 自动分配空闲的 loop 号
losetup -f --show disk1.img   # 输出 /dev/loop9
losetup -f --show disk2.img   # 输出 /dev/loop10

losetup -l | grep disk

这里把 disk1.imgdisk2.img 分别映射成了 /dev/loop9/dev/loop10,后面就把这两个设备当作「两块物理磁盘」来用。真实服务器上对应的场景就是两块全新的 /dev/sdb/dev/sdc

创建物理卷(PV)

pvcreate 会给设备打上 LVM 标签,使它成为物理卷。一块盘一个 PV,两块盘就创建两个:

pvcreate /dev/loop9 /dev/loop10

查看物理卷用 pvs(简洁表格)或 pvdisplay(详细信息):

pvs
pvdisplay /dev/loop9

下图把创建两个 PV、把它们归入同一个 VG、并查看最终状态的过程串起来执行了一遍:

创建 PV 与 VG 并查看状态

注意图中 pvs 输出里 PV 的 PSize508.00m 而不是整块磁盘的 512.00m——因为 PV 加入卷组时,LVM 会预留一小块区域存放卷组元数据(PV 头部标签 + VG 元数据镜像区),这是正常现象,不影响使用。

创建卷组(VG)

vgcreate 把若干 PV 汇聚成一个卷组。卷组名建议用「用途 + 类型」的语义化命名,比如数据盘用 vg_data、应用盘用 vg_app

vgcreate vg_data /dev/loop9 /dev/loop10
vgs        # 简洁表格:卷组大小、空闲空间、包含几个 PV/LV
vgdisplay vg_data   # 详细信息:PV 数量、VG 总大小、PE 大小等

vgdisplay 的关键输出中,VG Size 1016.00 MiB 就是两个 508M PV 汇合后的总池子大小,PE Size 4.00 MiB 则是之前提到的最小分配单元。至此,一个「空间池」已经就绪,两块 512M 的虚拟磁盘被合并成了约 1G 的可用空间。

创建逻辑卷(LV)并格式化挂载

现在从池子里切卷。lvcreate -L 指定大小、-n 指定卷名,比如创建 300M 的 lv_web

lvcreate -n lv_web -L 300M vg_data
lvs    # 查看逻辑卷列表

LV 创建后对应一个块设备 /dev/vg_data/lv_web(映射为 /dev/mapper/vg_data-lv_web),接下来就是格式化 + 挂载 + 写入数据,和普通分区一模一样:

mkfs.ext4 /dev/vg_data/lv_web
mkdir -p /mnt/lv_web
mount /dev/vg_data/lv_web /mnt/lv_web
df -h /mnt/lv_web

下图完整走了一遍「创建 LV → 格式化为 ext4 → 挂载 → 写入一个 100M 的文件 → 查看磁盘占用」:

创建逻辑卷并格式化挂载

从图中可以看到,挂载点 /mnt/lv_web 的容量是 265M——比 LV 的 300M 略小,因为 ext4 文件系统本身会占用少量空间用于元数据(inode 表、日志等)。写入 100M 数据后 Used 101M / Use% 42%,一切正常。

在线扩容:lvextend + resize2fs

这是 LVM 最核心的价值:磁盘空间不够时,不用迁移数据、不用重启、甚至不用卸载,直接在系统运行状态下把卷变大。

扩容分两步,顺序不能反:先扩 LV,再扩文件系统。

# 第 1 步:给逻辑卷增加 150M
lvextend -L +150M /dev/vg_data/lv_web

# 第 2 步:在线扩展 ext4 文件系统(ext4 支持在线扩容,无需卸载)
resize2fs /dev/vg_data/lv_web

下图是实际执行的效果:

逻辑卷在线扩容

几个值得注意的细节:

  • lvextend 输出 Rounding size to boundary between physical extents: 152.00 MiB——请求的 +150M 不是 4M 的整数倍,被自动向上舍入为 152M(38 个 PE)。这是符合预期的行为。
  • resize2fs 输出 on-line resizing required,说明它在挂载状态下直接完成了文件系统扩展,整个过程 LV 一直是可用状态。
  • 扩容前后对比:LV 从 300.00 MiB 变成 452.00 MiB,文件系统块数从 76800 变成 115712,挂载点容量从 265M 涨到 411M,且原本的 100M 数据完好无损

扩容就这么简单。下次遇到「磁盘要满了」的告警,只要卷在 VG 里有富余空间,一条 lvextend + 一条 resize2fs 就搞定了。

快照与回滚:误删数据的一键后悔药

LVM 快照可以理解为逻辑卷的「时间点副本」:创建快照几乎瞬时完成,之后对原卷的所有修改都会被记录到快照预留的空间里,原卷数据保持不变。一旦原卷数据出了问题(比如误删、写坏),把快照合并回原卷就能恢复到创建快照那一刻的状态。

创建快照用 lvcreate -s

lvcreate -s -n lv_web_snap -L 100M /dev/vg_data/lv_web
lvs

此时 lvs 里能看到快照卷:lv_web_snapOrigin 列指向 lv_webData% 表示当前快照已记录了多少变化量(如果快照空间被写满,快照会失效,所以要给够余量)。

模拟一次事故:删除 /mnt/lv_web 下的 100M 数据文件,然后通过快照找回:

rm -f /mnt/lv_web/bigfile          # 手滑删掉了数据
ls -lh /mnt/lv_web/                # bigfile 没了

# 合并快照,回滚到创建快照时的状态
lvconvert --merge /dev/vg_data/lv_web_snap

由于原卷处于挂载/使用状态,lvconvert --merge 会把合并推迟到卷下一次激活时执行(输出 Delaying merge since origin is open)。所以需要先卸载并重启激活一次卷,让合并真正发生:

umount /mnt/lv_web
lvchange -an /dev/vg_data/lv_web   # deactivate,合并在此触发
lvchange -ay /dev/vg_data/lv_web   # 重新 activate
mount /dev/vg_data/lv_web /mnt/lv_web
ls -lh /mnt/lv_web/                # bigfile 恢复,数据回来了

合并完成后,快照卷本身会被移除,lvs 里只剩 lv_web。这一整套「快照 + 合并」操作,是备份数据库、升级软件前的一剂强力后悔药。

缩小逻辑卷:与扩容顺序相反

缩小比扩容危险得多,顺序也恰好相反:先缩文件系统,再缩 LV。顺序一旦搞反(先缩 LV),文件系统会被截断损坏,数据几乎无法找回。

另外要注意 ext4 不支持在线缩小,所以缩小前必须卸载卷:

umount /mnt/lv_web

# 第 1 步:先把文件系统缩小到 300M
e2fsck -fy /dev/vg_data/lv_web     # 缩小前检查文件系统,强烈建议先 fsck
resize2fs /dev/vg_data/lv_web 300M

# 第 2 步:再把逻辑卷缩到 300M(会弹出危险确认,-y 跳过或手动输入 y)
lvreduce -L 300M /dev/vg_data/lv_web

mount /dev/vg_data/lv_web /mnt/lv_web
df -h /mnt/lv_web

lvreduce 执行时会弹出红色警告并要求输入 y/n 确认(THIS MAY DESTROY YOUR DATA),这是防止手滑的最后一道防线。实测中卷从 452.00 MiB 安全缩回 300.00 MiB,文件系统同步缩到 76800 块,数据无损。

开机自动挂载

mount 挂载只在本次开机有效,重启后需要重新挂载。把 LV 写进 /etc/fstab 即可开机自动挂载。推荐用 UUID 而不是设备路径(设备路径可能因磁盘顺序变化而改变)。

blkid 查 LV 的 UUID:

blkid /dev/vg_data/lv_web

输出形如 UUID="2b07d23b-..." TYPE="ext4",然后在 /etc/fstab 末尾加一行:

UUID=2b07d23b-34cf-4383-bcf6-12aa81dd375e  /mnt/lv_web  ext4  defaults,noatime  0  2

写完可以用 mount -a 验证配置正确(不报错即通过),再 reboot 确认。注意:如果某天 LV 被删除重建,UUID 会变化,fstab 里也要同步更新。

常用命令速查表

操作命令
创建物理卷pvcreate /dev/sdb
查看物理卷pvs / pvdisplay
创建卷组vgcreate vg_data /dev/sdb /dev/sdc
查看卷组vgs / vgdisplay
创建逻辑卷lvcreate -n lv_web -L 20G vg_data
查看逻辑卷lvs / lvdisplay
在线扩容 LVlvextend -L +5G /dev/vg_data/lv_web
扩容文件系统resize2fs /dev/vg_data/lv_web
创建快照lvcreate -s -n lv_snap -L 2G /dev/vg_data/lv_web
快照回滚lvconvert --merge /dev/vg_data/lv_snap
缩小文件系统resize2fs /dev/vg_data/lv_web 15G(先卸载)
缩小 LVlvreduce -L 15G /dev/vg_data/lv_web
删除 LV / VG / PVlvremovevgremovepvremove

小结

LVM 的核心思想就是「把磁盘管理从物理层面解放出来」:用 PV 收集物理空间,用 VG 形成弹性池,用 LV 按需切卷,再用快照和在线扩容两条能力应对日常运维里最常见的两个场景——磁盘满了、数据误删了。

如果你的服务器上还有大块空闲磁盘或者多块磁盘闲置,不妨用这套流程把它们纳入 LVM 统一管理。演示用的「稀疏文件 + loop 设备」方案在练习时也很实用,可以在不碰真实磁盘的前提下把 LVM 的每一条命令都过一遍。

发表评论

暂无评论,快来抢沙发吧!