基础

Linux 中的 LVM:物理卷、卷组与逻辑卷实践指南

更新于: · Skillaria.Top

LVM(逻辑卷管理器)是位于磁盘和文件系统之间的一层:磁盘变成物理卷(PV),合并为卷组(VG),再从中切出任意大小的逻辑卷(LV)。主要优势是可以用 lvextend -r 即时扩容卷、在不停服务的情况下向卷组添加新磁盘,并在高风险操作前创建快照。

LVM(Logical Volume Manager)是 Linux 中的逻辑卷管理器,它把文件系统与物理磁盘分离开来。与其把 /var 固定绑定到分区 /dev/sdb1,不如把磁盘放进一个共享池,再按需要的大小切出卷。空间不够时,一条命令就能扩大卷,无需重启,也不必停止服务。下面介绍 Linux 中 LVM 的工作原理,如何从零创建卷、扩容、制作快照,以及如何避免数据丢失等常见错误。

为什么需要 LVM,普通分区不够吗

普通分区是某块磁盘上一段连续的空间。它的大小在分区时就确定了,只有紧接着的空间空闲时才能扩大。磁盘用完后,就得买新盘、迁移数据、修改 /etc/fstab,并在此期间停止服务。

LVM 解决三类问题:

  • 灵活的大小。 逻辑卷可以随时扩大(对于 ext4,还可以缩小)。
  • 合并磁盘。 多块物理磁盘组成一个池,一个卷甚至可以同时跨越两块磁盘。
  • 快照。 在升级、迁移数据库或其他高风险操作之前,对卷做即时的副本。

因此 Ubuntu Server、RHEL、Rocky 和 AlmaLinux 的安装程序默认都会提供 LVM,而数据服务器上几乎总会使用它。

LVM 的三个层次:PV、VG 和 LV

LVM 的全部逻辑可以归结为三个概念。可以这样类比:磁盘是几袋面粉,卷组是共用的仓库,逻辑卷则是从仓库里称出的一份份面粉。

层次含义名称示例创建命令
PV(Physical Volume,物理卷)为 LVM 准备好的磁盘或分区/dev/sdb、/dev/sdc1pvcreate
VG(Volume Group,卷组)由一个或多个 PV 合并而成的池data_vgvgcreate
LV(Logical Volume,逻辑卷)从 VG 中切出的卷;在其上创建文件系统/dev/data_vg/applvcreate

在卷组内部,空间被划分为区段(PE,physical extents),即固定大小的块,默认为 4 MiB。逻辑卷就是一组区段,它们不必连续,可以位于不同的磁盘上。正因如此,卷很容易扩展:LVM 可以从任何地方为它补充空闲区段。

逻辑卷的设备可以通过两个等价的路径访问:/dev/data_vg/app 和 /dev/mapper/data_vg-app。在底层,它是内核的 device-mapper 设备。

查看现有的 LVM 配置

首先确认软件包已安装:在 Debian 和 Ubuntu 中是 lvm2(sudo apt install lvm2),RHEL 系列默认已安装。查看概览可以用简短或详细的命令:

$ sudo pvs
  PV         VG      Fmt  Attr PSize   PFree
  /dev/sdb   data_vg lvm2 a--  <20.00g      0
  /dev/sdc   data_vg lvm2 a--  <20.00g  <10.00g

$ sudo vgs
  VG      #PV #LV #SN Attr   VSize  VFree
  data_vg   2   1   0 wz--n- 39.99g <10.00g

$ sudo lvs
  LV   VG      Attr       LSize  Pool Origin Data%  Meta%
  app  data_vg -wi-ao---- 30.00g

详细信息可以用 pvdisplay、vgdisplay 和 lvdisplay 查看。要同时看到普通分区,使用 lsblk:逻辑卷会显示为类型 lvm。

$ lsblk /dev/sdb /dev/sdc
NAME             MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sdb                8:16   0  20G  0 disk
└─data_vg-app    253:0    0  30G  0 lvm  /srv/app
sdc                8:32   0  20G  0 disk
└─data_vg-app    253:0    0  30G  0 lvm  /srv/app

可以看到,30 GB 的卷 app 同时位于两块 20 GB 的磁盘上,这是普通分区做不到的。

从零开始的 LVM:从裸磁盘到挂载的卷

假设服务器新增了一块空磁盘 /dev/sdb,需要为应用数据准备一个 15 GB 的卷。请先用 lsblk 核对磁盘名称:下面的所有命令都会销毁指定设备上的数据。

第 1 步:创建物理卷

$ sudo pvcreate /dev/sdb
  Physical volume "/dev/sdb" successfully created.

可以把整块磁盘交给 LVM,也可以先用 fdisk 或 parted 创建类型为“Linux LVM”的分区,再指定它(/dev/sdb1)。使用分区对其他管理员更直观,也能防止有人在 LVM 上误做分区;而整块磁盘在云环境中更容易扩容。

第 2 步:创建卷组

$ sudo vgcreate data_vg /dev/sdb
  Volume group "data_vg" successfully created

第 3 步:切出逻辑卷

$ sudo lvcreate -n app -L 15G data_vg
  Logical volume "app" created.

参数 -L 指定绝对大小,-l 以区段数或百分比指定大小:-l 100%FREE 占用卷组中全部空闲空间,-l 50%VG 占用卷组的一半。建议在卷组中保留一部分空闲空间,以后可以分配给任何卷,或用于快照。

第 4 步:文件系统和挂载

$ sudo mkfs.ext4 /dev/data_vg/app
$ sudo mkdir -p /srv/app
$ sudo mount /dev/data_vg/app /srv/app
$ df -h /srv/app
Filesystem               Size  Used Avail Use% Mounted on
/dev/mapper/data_vg-app   15G   24K   14G   1% /srv/app

要让逻辑卷在重启后自动挂载,需要在 /etc/fstab 中添加一行。对于逻辑卷,/dev/mapper/... 路径是稳定的,所以可以使用它,也可以使用 blkid 给出的 UUID:

/dev/mapper/data_vg-app  /srv/app  ext4  defaults  0 2

重启前用 sudo mount -a 检查这一行:fstab 中的错误可能使服务器进入紧急模式。

如何在不停止服务的情况下扩展 LVM 卷

这正是喜欢 LVM 的主要原因。卷在挂载的文件系统上扩大,应用可以继续写入数据。具体步骤取决于卷组中是否还有空闲空间。

卷组中有空闲空间

$ sudo vgs data_vg
  VG      #PV #LV #SN Attr   VSize   VFree
  data_vg   1   1   0 wz--n- <20.00g <5.00g

$ sudo lvextend -r -L +4G /dev/data_vg/app
  Size of logical volume data_vg/app changed from 15.00 GiB to 19.00 GiB.
  Logical volume data_vg/app successfully resized.
resize2fs 1.47.0
Filesystem at /dev/mapper/data_vg-app is mounted on /srv/app; on-line resizing required

关键参数是 -r(--resizefs):它在扩大卷之后立即扩展文件系统。如果没有它,卷会变大,但 df -h 仍显示旧的大小,因为文件系统并不知道多出来的空间。这是最常见的错误:此时只需手动执行 ext4 的 resize2fs 或 XFS 的 xfs_growfs。

$ sudo resize2fs /dev/data_vg/app      # ext4,指定设备
$ sudo xfs_growfs /srv/app             # XFS,指定挂载点

注意区别:-L +4G 表示增加 4 GB,-L 4G 表示把最终大小设为 4 GB。要把剩余的所有空间都给卷,使用 -l +100%FREE。

卷组空间用完:添加磁盘

接入新磁盘(在云中则是挂载新的云盘),用 lsblk 找到它,并添加到卷组中:

$ sudo pvcreate /dev/sdc
$ sudo vgextend data_vg /dev/sdc
  Volume group "data_vg" successfully extended
$ sudo lvextend -r -l +100%FREE /dev/data_vg/app

三条命令,无需停机。文件系统现在位于两块磁盘上,而应用对此毫无察觉。

云中磁盘已经扩容

在云环境中,通常不必添加新磁盘,而是直接扩大现有的磁盘。这时内核会看到设备的新大小,需要用 pvresize 扩展物理卷:

$ sudo pvresize /dev/sdb
  Physical volume "/dev/sdb" changed
$ sudo lvextend -r -l +100%FREE /dev/data_vg/app

如果 PV 建立在分区(/dev/sdb1)上,先扩大分区,例如使用 cloud-guest-utils 软件包中的 growpart /dev/sdb 1,然后再执行 pvresize /dev/sdb1。

LVM 卷能否缩小

可以,但并非所有文件系统都支持,而且不能在运行中进行。XFS 根本不支持缩小,只能重新创建并迁移数据。ext4 可以缩小,但必须在卸载状态下进行:

$ sudo umount /srv/app
$ sudo lvreduce -r -L 10G /dev/data_vg/app

加上 -r 后,LVM 会在缩小之前自动检查文件系统(e2fsck)并将其压缩。如果不加 -r,很容易把卷缩得比文件系统还小,从而损坏数据。缩小之前务必先做备份。

LVM 快照:高风险操作前的卷副本

快照是逻辑卷的即时副本。它几乎瞬间完成,并不复制全部数据:LVM 采用写时复制(copy-on-write)机制,只把快照创建之后在原卷上发生变化的数据块保存到快照中。因此快照只需要卷组中用于存放变化数据的空间,而不需要整个卷的空间。

$ sudo lvcreate -s -n app_before_upgrade -L 2G /dev/data_vg/app
  Logical volume "app_before_upgrade" created.

$ sudo lvs data_vg
  LV                 VG      Attr       LSize  Origin Data%
  app                data_vg owi-aos--- 19.00g
  app_before_upgrade data_vg swi-a-s---  2.00g app    0.01

列 Data% 显示用于保存变化数据的空间占用了多少。如果达到 100%,经典快照会失效,只能删除。请按预期的变化量留出足够的余量,并通过 lvs 监控长期存在的快照,或使用 lvextend 扩大它们。

如何回滚到快照

如果升级出了问题,可以把卷恢复到快照时的状态:

$ sudo umount /srv/app
$ sudo lvconvert --merge /dev/data_vg/app_before_upgrade
  Merging of volume data_vg/app_before_upgrade started.
$ sudo mount /srv/app

合并完成后快照会消失。如果无法卸载该卷(例如它是系统根分区),合并会推迟到下次激活该卷时进行,也就是重启之后。

一切顺利时

直接删除快照,避免它继续拖慢写入并占用空间:

$ sudo lvremove /dev/data_vg/app_before_upgrade

快照很适合以只读方式挂载,用来为正在运行的系统做一致性备份:使用 mount -o ro,再通过 rsync 或 tar 复制。对于 XFS,需要加上 nouuid 选项,否则由于 UUID 与原卷相同,挂载会失败。

LVM 与 RAID:不要混淆两者的用途

LVM 本身无法防止磁盘故障。如果一个卷横跨两块磁盘,其中一块损坏,整个卷都会丢失,而不只是一半。要获得容错能力,应在 LVM 下面使用 RAID(mdadm),或使用 LVM 内置的镜像模式(lvcreate --type raid1 -m 1)。

RAID 和快照都无法替代备份。快照与原卷位于同一批磁盘上,而 RAID 会忠实地镜像误执行的 rm -rf。需要在另一种存储介质上保存单独的副本。

LVM 命令速查表

任务命令
查看 PV、VG、LV 概况pvs、vgs、lvs
创建物理卷pvcreate /dev/sdb
创建卷组vgcreate data_vg /dev/sdb
向卷组添加磁盘vgextend data_vg /dev/sdc
创建卷lvcreate -n app -L 15G data_vg
扩展卷和文件系统lvextend -r -L +5G /dev/data_vg/app
磁盘扩容后拉伸 PVpvresize /dev/sdb
快照lvcreate -s -n snap -L 2G /dev/data_vg/app
回滚到快照lvconvert --merge /dev/data_vg/snap
将磁盘移出卷组pvmove /dev/sdb,然后 vgreduce data_vg /dev/sdb
删除卷lvremove /dev/data_vg/app

常见错误

  • 卷已扩大,但 df 仍显示旧大小。 忘记了 -r。执行 resize2fs 或 xfs_growfs。
  • Insufficient free space。 卷组中没有空闲区段:用 vgs 检查,然后通过 vgextend 添加磁盘,或拉伸 PV。
  • Device /dev/sdc excluded by a filter。 磁盘上还残留着旧的分区表或文件系统签名。如果确定其中的数据不需要,使用 wipefs -a /dev/sdc 清除。
  • 快照变为 Invalid。 用于变化数据的空间达到了 100%。无法恢复,只能删除,下次应分配更多空间。

总结

Linux 中的 LVM 分为三层:物理卷(PV)组成卷组(VG),卷组中切出逻辑卷(LV)。这带来了普通分区没有的能力:用 lvextend -r 即时扩容,无停机地添加磁盘,以及在高风险改动前制作快照。要记住,LVM 无法替代 RAID,也无法替代备份。磁盘分区、LVM、RAID 和配额的实际操作,我们在 Linux 课程的讲座中都有演示。

常见问题

能否把已经在使用的带数据分区转换为 LVM?

不能直接转换:pvcreate 会覆盖设备的开头部分。正确做法是在另一块磁盘上创建 LVM,用 rsync 复制数据,修改 fstab,然后再释放旧分区。

LVM 会降低磁盘速度吗?

没有快照时,device-mapper 的额外开销几乎察觉不到。活动的经典快照会因写时复制而降低对原卷的写入速度,因此不应长期保留。

家用电脑或笔记本需要 LVM 吗?

不是必需的,但在磁盘加密(内部使用 LVM 的 LUKS)时很有用,而且不必重新分区就能调整分区大小。数据服务器上 LVM 几乎已成标准。

LVM 中的 thin provisioning 是什么?

这是精简卷:先创建一个池(lvcreate --type thin-pool),池中的卷只在写入数据时才占用实际空间。这样可以分配超过物理容量的卷,但必须注意监控池的使用率。

如何在不丢失数据的情况下把磁盘移出卷组?

使用 pvmove /dev/sdb 把其上的区段迁移到卷组中的其他磁盘(需要有足够的空闲空间),然后执行 vgreduce data_vg /dev/sdb 和 pvremove /dev/sdb。

通过视频学习本主题

课程“Linux: с нуля до уверенного администрирования”结合实践详细讲解了这一主题。

前往课程