本文要点
- 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 由三个抽象层组成,从底向上分别是:
| 层级 | 名称 | 作用 |
|---|---|---|
| PV | Physical Volume 物理卷 | 真实磁盘(或磁盘分区)被打上的标签,是 LVM 的基本存储单元 |
| VG | Volume Group 卷组 | 若干 PV 汇聚而成的「空间池」,可以跨磁盘 |
| LV | Logical 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.img、disk2.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、并查看最终状态的过程串起来执行了一遍:

注意图中 pvs 输出里 PV 的 PSize 是 508.00m 而不是整块磁盘的 512.00m——因为 PV 加入卷组时,LVM 会预留一小块区域存放卷组元数据(PV 头部标签 + VG 元数据镜像区),这是正常现象,不影响使用。
创建卷组(VG)
vgcreate 把若干 PV 汇聚成一个卷组。卷组名建议用「用途 + 类型」的语义化命名,比如数据盘用 vg_data、应用盘用 vg_app:
vgcreate vg_data /dev/loop9 /dev/loop10vgs # 简洁表格:卷组大小、空闲空间、包含几个 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_snap 的 Origin 列指向 lv_web,Data% 表示当前快照已记录了多少变化量(如果快照空间被写满,快照会失效,所以要给够余量)。
模拟一次事故:删除 /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_weblvreduce 执行时会弹出红色警告并要求输入 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 |
| 在线扩容 LV | lvextend -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(先卸载) |
| 缩小 LV | lvreduce -L 15G /dev/vg_data/lv_web |
| 删除 LV / VG / PV | lvremove → vgremove → pvremove |
小结
LVM 的核心思想就是「把磁盘管理从物理层面解放出来」:用 PV 收集物理空间,用 VG 形成弹性池,用 LV 按需切卷,再用快照和在线扩容两条能力应对日常运维里最常见的两个场景——磁盘满了、数据误删了。
如果你的服务器上还有大块空闲磁盘或者多块磁盘闲置,不妨用这套流程把它们纳入 LVM 统一管理。演示用的「稀疏文件 + loop 设备」方案在练习时也很实用,可以在不碰真实磁盘的前提下把 LVM 的每一条命令都过一遍。
评论 (0)
暂无评论,快来抢沙发吧!