4 分钟
Linux LVM 逻辑卷管理
背景知识
在文章 《用户态文件系统 fuse》 中,介绍了 Linux 对文件系统的抽象,fuse 文件系统的底层数据存储是可以任意定制的,并不一定会和块设备打交道。
但是,Linux 常用的本地文件系统 (如 ext4) 数据存储都是基于块设备实现的。
Linux 作为冯诺依曼架构计算机的操作系统,必然实现对输入输出(IO)硬件的支持。
但是,IO 硬件千奇百怪,且会不断迭代发展。因此, Linux 根据和 IO 硬件数据读写方式的不同,对 IO 类硬件的抽象,称之为设备 (Device),主要包含如下两类:
- 字符设备(杂项设备),数据是流式的读写,如:鼠标、键盘、终端。
- 块设备,数据支持随机读写,如:硬盘。
如上这些设备在内核启动过程中,由这些设备驱动(据说 Linux 内核中,驱动代码占比一半),将这些设备加载到内核中。通过 /dev
文件系统(udev)暴露给应用程序使用。以硬盘为例,将存在类似于 /dev/sda
的设备文件。
Linux 还支持将同一块硬盘划分为多个独立相互不影响的区域,即对硬盘进行分区。以实现在不同的区域,格式化为不同的文件系统。
在 Linux 中,可以使用 parted 命令进行分区(可以用 parted -l
查看本机磁盘分区情况)。完成分区后,在 /dev
目录中,除了会看到硬盘对应的设备文件外,每个分区也会对应一个块设备文件(如 /dev/sda1
)。通过 lsblk (list block devices) 命令可以看到所有块设备以及分区关系,示例如下(debian 12 默认 + 挂载两块 1G 的空硬盘):
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 32G 0 disk
├─sda1 8:1 0 31G 0 part /
├─sda2 8:2 0 1K 0 part
└─sda5 8:5 0 975M 0 part [SWAP]
sdb 8:16 0 1G 0 disk
sdc 8:32 0 1G 0 disk
有了块设备后,即可使用 mkfs.xxx
命令,对硬盘或分区进行格式化。
mkfs.ext4 /dev/sdb
之后就可以使用 mount 命令将这个磁盘 mount 到文件系统中了。
mkdir -p /mnt/test-disk1
mount -t ext4 /dev/sdb /mnt/test-disk1
# umount /mnt/test-disk1 # 恢复现场
此时这个文件系统就可以用了(注意, mount 命令只对本次启动有效,重启后将会失效)。
Linux 根目录的挂载和上述类似。即:在启动过程中,通过读取 /etc/fstab
配置文件(该文件的 UUID 可通过 blkid
命令查看),然后用 mount 系统调用挂载。
概述
上述介绍的分区,是将一个硬盘划分多个相互独立的区域,本质上是在当前硬盘中记录了每个区域的起始结束偏移量,难以实现如下场景:
- 分区空间调整,对一个分区的调整受限于前后分区的情况,基本难以扩缩容。
- 分区是针对单块硬盘的,分区的最大大小受限于磁盘的大小。
LVM (Logical Volume Manager) 逻辑卷管理(lvm2, kernel>=2.6),就来解决单 Linux 主机,多磁盘管理的技术,具有如下能力:
- 管理多块硬盘
- 自由扩缩容
- 软 RAID
- 快照
简单来说, LVM 就是在多个块设备(PV)之上,虚拟出多个块设备(LV)。
核心概念
- VG (Volume Group) 卷组,可以理解为存储池。
- PV (Physical Volume) 物理卷,对应一个块设备可以是整块磁盘或某个物理分区。一个 PV 如果要被使用,必须加入一个 VG。
- LV (Logical Volume) 逻辑卷,LVM 生成的虚拟块设备。从 VG 中创建,必然属于某个 VG。
安装
apt install lvm2
lvm 分为两个部分: 内核和命令行工具。debian 12 内核相关功能已存在,如上命令会安装 lvm2 命令行工具。
场景
实验环境
debian 12 附加两块 1 G 的硬盘,所有命令以 root 用户执行。
创建存储池(PV 和 VG)
# 查看块设备
lsblk
# 创建 pv
pvcreate /dev/sdb # 将块设备格式化为 pv,该盘所有数据将丢失!
pvcreate /dev/sdc # 将块设备格式化为 pv,该盘所有数据将丢失!
pvs # 打印所有 pv
# 创建 vg
vgcreate VG_TEST /dev/sdb /dev/sdc
vgdisplay VG_TEST # 打印某 vg 属性
创建文件系统(LV)
# 创建 lv
lvcreate -L 500M -n lv_test1 VG_TEST
lvs # 查看所有 lv
# 查看对应的块设备(位于 /dev/mapper/ 目录下以及 /dev/<VG_NAME>/<LV_NAME>)
ls -al /dev/mapper/VG_TEST-lv_test1
mkfs.ext4 /dev/mapper/VG_TEST-lv_test1 # 格式化文件系统
mkdir -p /mnt/test-lv1
mount -t ext4 /dev/mapper/VG_TEST-lv_test1 /mnt/test-lv1 # 挂载
echo abc > /mnt/test-lv1/abc
cat /mnt/test-lv1/abc
df -h /mnt/test-lv1
# umount /mnt/test-lv1 # 恢复现场
文件系统在线扩容 (LV)
# 第一步: 扩容 LV
lvresize -L +700M /dev/mapper/VG_TEST-lv_test1
lvs
# 第二步: 文件系统扩容 (ext4 文件系统支持,其他文件系统可能不支持)
resize2fs /dev/mapper/VG_TEST-lv_test1
df -h /mnt/test-lv1
注意:
- 不要求 umount
- 执行顺序不能颠倒
文件系统离线缩容 (LV)
# 第一步: umount
umount /mnt/test-lv1
# 第二步: 检查文件系统 (ext4 文件系统支持,其他文件系统可能不支持)
e2fsck -f /dev/mapper/VG_TEST-lv_test1
# 第三步: 文件系统缩容 (ext4 文件系统支持,其他文件系统可能不支持)
resize2fs /dev/mapper/VG_TEST-lv_test1 500M
# 第四步: 缩容 LV
lvreduce -L 500M /dev/mapper/VG_TEST-lv_test1
# 验证
mount -t ext4 /dev/mapper/VG_TEST-lv_test1 /mnt/test-lv1 # 挂载
cat /mnt/test-lv1/abc
df -h /mnt/test-lv1
# umount /mnt/test-lv1 # 恢复现场
注意:
- 必须要 umount
- 执行顺序不能颠倒
硬盘迁移到新设备
注意:如下是不停机迁移,如果旧设备可以关机,则直接关机将硬盘插入新设备即可,无需做任何操作。
如下命令在旧设备中执行。
# 0. 卸载 lv 所有挂载点
# umount /mnt/test-lv1
# 1. 停用所有 lv
lvchange -an /dev/mapper/VG_TEST-lv_test1
# 2. 停用卷组
vgchange -an VG_TEST
# 3. 导出卷组
vgexport VG_TEST
拔下所有硬盘,插入新设备。
如下命令在新设备中执行。
# 1. 扫描 pv
pvscan
vgs
# 2. 导入 vg
vgimport VG_TEST
# 3. 激活 vg
vgchange -ay VG_TEST
# 4. 挂载到挂载点
mkdir -p /mnt/test-lv1
mount -t ext4 /dev/mapper/VG_TEST-lv_test1 /mnt/test-lv1 # 挂载
cat /mnt/test-lv1/abc
扩充存储池(VG)
pvcreate /dev/sdd
vgextend VG_TEST /dev/sdd
软 RAID
略,参见: