LVN与其在Linux上的实现

时间:2021-12-09 17:14:23

参考资料:

LVM详解-骏马金龙-博客园

How to reduce the size of an LVM partition formatted with xfs filesystem on CentOS7?

骏马兄的博文会相对深入一点,并且他是基于ext系列文件系统来演示扩容与缩容,而我使用的是xfs文件系统。

基本概念

传统的磁盘分区,在分区完之后,分区的大小无法改变,无法动态增加或减小分区的大小。

为了解决这个问题,就诞生了LVM(Logical Volume Management)这种方法。

在Linux中的LVM实现是基于Linux内核的设备映射(device mapper,dm)框架来实现的,该框架同时也是软RAID以及LVM快照的基础。对应的软件名称也叫LVM(Logical Volume Manager)。

接下来介绍几个关于LVM的术语:

  • PV:Physical Volume,物理卷。即我们所熟知的磁盘分区,与普通的分区的最大不同点在于,作为PV的磁盘分区的类型必须是ID为8e的Linux LVM分区。
  • VG:Volume Group,卷组。由多个PV组合而成,类似于多个分区组合起来的“虚拟磁盘”(自称)。在创建VG的同时,也确定了该VG中的PE大小。
  • PE:Physical Extent,可以理解为物理存储单元。类似于文件系统中的block、chunk或者簇的概念。一般来说,创建完VG确定了PE大小后,就无法再修改了。默认为4MB。
  • LV:Logical Volume,逻辑卷。VG相当于虚拟磁盘,而LV则是基于虚拟磁盘所划分出的“虚拟分区”(自称),在创建LV的时候,可基于分区大小或者VG中的PE个数。LV创建好之后就像传统的分区那样可以格式化并使用了。
  • LE:Logical Extent,可以理解为逻辑存储单元。本质上是等同于PE的概念,当PV加入VG后,就被划分成多个PE。从VG中划分LV的过程,就是一个划分PE的过程,LV创建好之后,PE就被称之为LE了。PV中的PE和LV中的LE的映射关系,可通过“pvdisplay -m”来查看。

LV的路径

LV创建完毕后,其块设备路径为/dev/dm-N(N为一个数字),有2个字符链接文件指向它。

/dev/mapper/VG_NAME-LV_NAME --> /dev/dm-N
/dev/VG_NAME/LV_NAME --> /dev/dm-N

图示

图片来自网络,侵权请联系。

LVN与其在Linux上的实现

实现

环境

CentOS 7.5

系统自带lvm2程序包

初步使用LVM

首先为虚拟机添加一块磁盘,大小为7GB。创建3个分区类型为LVM的分区,分别是3GB、2GB和1GB。

注意:磁盘的大小大于分区之和的大小,主要是担心可能因为换算等问题导致磁盘划分分区容量的时候出现不足的情况。

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1 8e Linux LVM
/dev/sdb2 8e Linux LVM
/dev/sdb3 8e Linux LVM

将/dev/sdb1和/dev/sdb2这两个LVM格式创建为PV。/dev/sdb3保留,放在后面做扩容。

[root@C7 ~]# pvcreate /dev/sdb{,}
Physical volume "/dev/sdb1" successfully created.
Physical volume "/dev/sdb2" successfully created.

可通过pvs命令查看系统上已有PV的简要信息。

[root@C7 ~]# pvs
PV VG Fmt Attr PSize PFree
/dev/sda2 centos lvm2 a-- <.00g
/dev/sdb1 lvm2 --- .00g .00g
/dev/sdb2 lvm2 --- .00g .00g

留意,/dev/sda2这个PV是CentOS 7系列系统默认安装时候的创建的。

可通过pvdisplay命令查看系统上已有PV的详细信息,默认显示所有,可以基于PV名称查看特定的PV。PV名称就是分区的路径名,例如“/dev/sdb1”这样。

[root@C7 ~]# pvdisplay /dev/sdb{,}
"/dev/sdb2" is a new physical volume of "2.00 GiB"
--- NEW Physical volume ---
PV Name /dev/sdb2
VG Name # 由于我们还未将PV加入VG,因此该字段为空。
PV Size 2.00 GiB
Allocatable NO # 未加入VG,不可分配。
PE Size # PV中的PE的大小,是在加入VG之后决定的,并且在VG创建后该VG的PE大小就无法修改了。同理,未加入VG,因此PE为0。
Total PE
Free PE
Allocated PE
PV UUID OlFvnr-rarx-ebCa-pG3L-qJBe-V8d1-aYkXB2 "/dev/sdb1" is a new physical volume of "3.00 GiB"
--- NEW Physical volume ---
PV Name /dev/sdb1
VG Name
PV Size 3.00 GiB
Allocatable NO
PE Size
Total PE
Free PE
Allocated PE
PV UUID o7uiiy-kXEr-eZee-e0aP-cFKQ-dyjf-oQRvB9

创建好PV之后,我们就可以创建VG,并将刚创建的两个PV加入该VG。

[root@C7 ~]# vgcreate myvg /dev/sdb{,}
Volume group "myvg" successfully created

查看VG的简要和详细信息。

[root@C7 ~]# vgs myvg
VG #PV #LV #SN Attr VSize VFree
myvg wz--n- .99g .99g
[root@C7 ~]# vgdisplay myvg
--- Volume group ---
VG Name myvg
System ID
Format lvm2
Metadata Areas
Metadata Sequence No
VG Access read/write
VG Status resizable
MAX LV
Cur LV
Open LV
Max PV
Cur PV
Act PV
VG Size 4.99 GiB
PE Size 4.00 MiB # 默认的PE大小为4MB。
Total PE # VG中总PE的个数,在划分LV的时候,可基于PE个数。
Alloc PE / Size /
Free PE / Size / 4.99 GiB
VG UUID XKP0mV-zWAx-nscs-jXiT-jLxm-5MgE-aDU8fU

当PV加入VG之后,再次查看PV的信息。

[root@C7 ~]# pvs /dev/sdb{,}
PV VG Fmt Attr PSize PFree
/dev/sdb1 myvg lvm2 a-- <.00g <.00g # PV加入VG后,应该会在PV中创建了一些元数据(metadata),因此这里的size都加上了小于号。
/dev/sdb2 myvg lvm2 a-- <.00g <.00g
[root@C7 ~]# pvdisplay /dev/sdb{,}
--- Physical volume ---
PV Name /dev/sdb1
VG Name myvg
PV Size 3.00 GiB / not usable 4.00 MiB
Allocatable yes
PE Size 4.00 MiB # PV加入VG后就有了PE的概念。
Total PE
Free PE
Allocated PE
PV UUID o7uiiy-kXEr-eZee-e0aP-cFKQ-dyjf-oQRvB9 --- Physical volume ---
PV Name /dev/sdb2
VG Name myvg
PV Size 2.00 GiB / not usable 4.00 MiB
Allocatable yes
PE Size 4.00 MiB
Total PE
Free PE
Allocated PE
PV UUID OlFvnr-rarx-ebCa-pG3L-qJBe-V8d1-aYkXB2

将目前的VG的所有空间都创建为一个LV。

[root@C7 ~]# lvcreate -L 5GB -n mylv myvg
Volume group "myvg" has insufficient free space ( extents): required.

基于size来创建LV失败,因为创建5GB的容量需要1280个PE(默认4MB),这个计算方式是1024进率。

但是我们在创建分区时创建的这5GB,可能是按照1000进率的。这就像市面上销售的硬盘或者U盘,几乎都是按照1000进率来表示一样。

在Linux的工具的使用中,当你使用5GB、5gb、5G和5g,它们可能有的表示1000进率,有的表示1024进率,这个就需要用户具体去查看每个工具的man手册了。

而且分区/文件系统一般都会保留一部分空间给元数据存放。因此一般情况下,容量差不多即可,不需要太过于纠结。

PS:网上也有说是“1024进制”,但是我觉得说“1024进率”可能更准确。

既然无法基于size来创建,那我们就基于VG中的PE个数来创建了,使用全部的PE去创建一个LV。

[root@C7 ~]# lvcreate -l  -n mylv myvg
Logical volume "mylv" created.

查看LV信息。注意:查看LV的信息的时候,lvs和lvdisplay的命令参数应该是LV的路径(/dev/mapper/myvg-mylv或者/dev/myvg/mylv)或者VG的名称,而不是LV名称。

[root@C7 ~]# lvs myvg
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
mylv myvg -wi-a----- .99g
[root@C7 ~]# lvdisplay myvg
--- Logical volume ---
LV Path /dev/myvg/mylv
LV Name mylv
VG Name myvg
LV UUID 43nlG8-EGra-1jfQ-2K2n-3kxA-s5SW-aR1R3I
LV Write Access read/write
LV Creation host, time C7, -- :: +
LV Status available
# open
LV Size 4.99 GiB
Current LE # 原本在VG中的PE,在分配给LV之后,就换了个名字叫LE了。
Segments
Allocation inherit
Read ahead sectors auto
- currently set to
Block device :

LV创建完毕后,通过“pvdisplay -m”查看PV中的PE和LV中的LE之间的映射关系。

[root@C7 ~]# pvdisplay -m /dev/sdb{,}
--- Physical volume ---
PV Name /dev/sdb1
VG Name myvg
PV Size 3.00 GiB / not usable 4.00 MiB
Allocatable yes (but full)
PE Size 4.00 MiB
Total PE
Free PE
Allocated PE
PV UUID o7uiiy-kXEr-eZee-e0aP-cFKQ-dyjf-oQRvB9 --- Physical Segments ---
Physical extent to :
Logical volume /dev/myvg/mylv
Logical extents to --- Physical volume ---
PV Name /dev/sdb2
VG Name myvg
PV Size 2.00 GiB / not usable 4.00 MiB
Allocatable yes (but full)
PE Size 4.00 MiB
Total PE
Free PE
Allocated PE
PV UUID OlFvnr-rarx-ebCa-pG3L-qJBe-V8d1-aYkXB2 --- Physical Segments ---
Physical extent to :
Logical volume /dev/myvg/mylv
Logical extents to

LVM设备路径查看。

[root@C7 ~]# ls -l /dev/mapper/myvg-mylv
lrwxrwxrwx root root Apr : /dev/mapper/myvg-mylv -> ../dm-
[root@C7 ~]# ls -l /dev/myvg/mylv
lrwxrwxrwx root root Apr : /dev/myvg/mylv -> ../dm-
[root@C7 ~]# ls -l /dev/dm-
brw-rw---- root disk , Apr : /dev/dm-
[root@C7 ~]# file /dev/dm-
/dev/dm-: block special

接下来,就可以将其格式化、挂载并使用、加入开机挂载了。

[root@C7 ~]# mkfs -t xfs /dev/myvg/mylv
...
省略
...
[root@C7 ~]# mount /dev/myvg/mylv /lvm_mount_point/
[root@C7 ~]# cat /etc/fstab
...
/dev/mapper/myvg-mylv /lvm_mount_point xfs defaults

使用LVM扩容

扩容是LVM中最常见的用法,也是为了解决传统磁盘分区容量耗尽的问题。

扩容的原理是将VG中空闲的PE划分给LV,或者通过增加PV的方式增加VG中的空闲PE而后划分给LV。

将此前剩余的分区/dev/sdb3创建为PV并加入VG。

[root@C7 ~]# pvcreate /dev/sdb3
Physical volume "/dev/sdb3" successfully created.
[root@C7 ~]# vgextend myvg /dev/sdb3
Volume group "myvg" successfully extended

查看VG新增出的PE个数。

[root@C7 ~]# vgdisplay myvg
--- Volume group ---
VG Name myvg
System ID
Format lvm2
Metadata Areas
Metadata Sequence No
VG Access read/write
VG Status resizable
MAX LV
Cur LV
Open LV
Max PV
Cur PV
Act PV
VG Size <5.99 GiB
PE Size 4.00 MiB
Total PE
Alloc PE / Size / 4.99 GiB
Free PE / Size 255 / 1020.00 MiB
VG UUID XKP0mV-zWAx-nscs-jXiT-jLxm-5MgE-aDU8fU

将增加的PE全部扩展给此前创建的LV,即mylv。

[root@C7 ~]# lvextend -l + /dev/myvg/mylv
Size of logical volume myvg/mylv changed from 4.99 GiB ( extents) to <5.99 GiB ( extents).
Logical volume myvg/mylv successfully resized.

查看扩容后的LV。

[root@C7 ~]# lvdisplay /dev/myvg/mylv
--- Logical volume ---
LV Path /dev/myvg/mylv
LV Name mylv
VG Name myvg
LV UUID 43nlG8-EGra-1jfQ-2K2n-3kxA-s5SW-aR1R3I
LV Write Access read/write
LV Creation host, time C7, -- :: +
LV Status available
# open
LV Size <5.99 GiB
Current LE 1533

Segments
Allocation inherit
Read ahead sectors auto
- currently set to
Block device :

虽然LV已经扩容成功,但其实文件系统并没有扩容,即便是重新挂载。

[root@C7 ~]# df -hT | grep "mylv"
/dev/mapper/myvg-mylv xfs 5.0G 33M 5.0G % /lvm_mount_point

原因在于文件系统还未扩容,ext系列文件系统使用resize2fs命令,此处使用xfs文件系统,因此使用xfs_growfs。

[root@C7 ~]# xfs_growfs /dev/mapper/myvg-mylv
...
省略
...
[root@C7 ~]# df -hT | grep "mylv"
/dev/mapper/myvg-mylv xfs 6.0G 33M 6.0G % /lvm_mount_point

扩容除了可以使用原磁盘的剩余空间,也可以使用新的磁盘。

后来我又创建了一块1GB的虚拟磁盘,整盘创建一个LVM分区,创建PV,加入VG,而后扩展LV,也是可以的。

[root@C7 ~]# pvcreate /dev/sdc1
[root@C7 ~]# vgextend myvg /dev/sdc1
[root@C7 ~]# lvextend -l + /dev/myvg/mylv
[root@C7 ~]# xfs_growfs /dev/myvg/mylv
[root@C7 ~]# df -hT
...
/dev/mapper/myvg-mylv xfs .0G 33M .0G % /lvm_mount_point
...

使用LVM缩容

首先查看缩容前的LV的大小,方便之后对比。

[root@C7 ~]# lvdisplay /dev/myvg/mylv
--- Logical volume ---
LV Path /dev/myvg/mylv
LV Name mylv
VG Name myvg
LV UUID 43nlG8-EGra-1jfQ-2K2n-3kxA-s5SW-aR1R3I
LV Write Access read/write
LV Creation host, time C7, -- :: +
LV Status available
# open
LV Size 6.98 GiB
Current LE
Segments
Allocation inherit
Read ahead sectors auto
- currently set to
Block device :

确认承载于LV上的需要的文件的大小。在实验环境中只有一个521字节的文件(不足一个PE),因此只要缩容后的LV只要还有1个PE,那么文件就还是可用的。

[root@C7 ~]# ls -lh /lvm_mount_point/fstab
-rw-r--r-- root root Apr : /lvm_mount_point/fstab

假设我们要缩容的容量刚好是/dev/sdb3和/dev/sdc1,查看其PV的PE数。

[root@C7 ~]# pvdisplay /dev/sd{b3,c1} | grep -iE "pv name|pe"
PV Name /dev/sdb3
PE Size 4.00 MiB
Total PE
Free PE
Allocated PE
PV Name /dev/sdc1
PE Size 4.00 MiB
Total PE
Free PE
Allocated PE

缩容与扩容的过程正好是相反的,我们首先需要对文件系统进行缩容操作。

如果是ext系列的文件系统:

  1. 停止需要卸载的文件系统所提供的服务(例如nginx、MySQL等)。
  2. 使用umount命令卸载文件系统。
  3. 使用“e2fsck -f”强制检测文件系统。
  4. 使用resize2fs命令调整文件系统的容量。
  5. 使用lvreduce命令缩容LV的容量。其实到这里,LV的缩容就已经完成了,如果缩容的容量大于等于某个/某几个PV的话,可以考虑将PV从VG中释放出来。
  6. 使用pvdisplay命令查看PV中的PE是否完全为空闲状态。若否则使用pvmove命令将非空闲PE移出。
  7. 使用vgreduce命令将全空闲的PV移出VG,并使用pvremove命令删除其PV状态。

本文所使用的文件系统是XFS。

xfs文件系统不支持缩容(XFS文件系统的缺点),想实现类似缩容的功能,可以通过备份(xfsdump)和还原(xfsrestore)的方式。

备份XFS文件系统。需要用户输入2个label,填写test即可。

[root@C7 ~]# xfsdump -f /tmp/lvm_mount_point.xfsdump /lvm_mount_point
xfsdump: using file dump (drive_simple) strategy
xfsdump: version 3.1. (dump format 3.0) - type ^C for status and control ============================= dump label dialog ============================== please enter label for this dump session (timeout in sec)
-> test
session label entered: "test" --------------------------------- end dialog --------------------------------- xfsdump: level dump of C7:/lvm_mount_point
xfsdump: dump date: Wed Apr ::
xfsdump: session id: e2cfa0fa-d73d-4e94-8f55-cb3f2c581c08
xfsdump: session label: "test"
xfsdump: ino map phase : constructing initial dump list
xfsdump: ino map phase : skipping (no pruning necessary)
xfsdump: ino map phase : skipping (only one dump stream)
xfsdump: ino map construction complete
xfsdump: estimated dump size: bytes ============================= media label dialog ============================= please enter label for media in drive (timeout in sec)
-> test
media label entered: "test" --------------------------------- end dialog --------------------------------- xfsdump: creating dump session media file (media , file )
xfsdump: dumping ino map
xfsdump: dumping directories
xfsdump: dumping non-directory files
xfsdump: ending media file
xfsdump: media file size bytes
xfsdump: dump size (non-dir files) : bytes
xfsdump: dump complete: seconds elapsed
xfsdump: Dump Summary:
xfsdump: stream /tmp/lvm_mount_point.xfsdump OK (success)
xfsdump: Dump Status: SUCCESS

在备份XFS文件系统的时候,必须保持挂载状态,如果先卸载再备份,会报错。

xfsdump: ERROR: /dev/{dm-,mapper/myvg-mylv,myvg/mylv} does not identify a file system

如果备份的命令参数填写的是挂载点,并且在挂载点末尾加上了斜线的话,也会报这个错误,即“/lvm_mount_point/”会报错而“/lvm_mount_point”不会。

备份完成后,卸载文件系统。

[root@C7 ~]# umount /lvm_mount_point/

缩容LV。

[root@C7 ~]# lvreduce -l - /dev/myvg/mylv
WARNING: Reducing active logical volume to 4.99 GiB.
THIS MAY DESTROY YOUR DATA (filesystem etc.)
Do you really want to reduce myvg/mylv? [y/n]: y
Size of logical volume myvg/mylv changed from 6.98 GiB ( extents) to 4.99 GiB ( extents).
Logical volume myvg/mylv successfully resized.

产生警告,意思就是说缩容可能会摧毁数据,由于缩容后的容量大于我们的数据容量,因此不会影响到我们的数据,这就是LVM缩容的优点。但是前提条件是文件系统有事先缩容,由于我们使用的是XFS文件系统,无法实现文件系统的缩容,所以其实这个操作,应该是破坏了数据了,后面也会看到。

缩容完毕后,再次查看LV大小。

[root@C7 ~]# lvdisplay /dev/myvg/mylv
--- Logical volume ---
LV Path /dev/myvg/mylv
LV Name mylv
VG Name myvg
LV UUID 43nlG8-EGra-1jfQ-2K2n-3kxA-s5SW-aR1R3I
LV Write Access read/write
LV Creation host, time C7, -- :: +
LV Status available
# open
LV Size 4.99 GiB
Current LE
Segments
Allocation inherit
Read ahead sectors auto
- currently set to
Block device :

由于我们缩容的容量刚好为2个PV的PE数,并且这2个PV是最后两个加入VG的。因此缩容的PE正好是我们想要移除的2个PV,大大降低了缩容的难度。

[root@C7 ~]# pvdisplay /dev/sd{b3,c1}
--- Physical volume ---
PV Name /dev/sdb3
VG Name myvg
PV Size 1.00 GiB / not usable 4.00 MiB
Allocatable yes
PE Size 4.00 MiB
Total PE
Free PE
Allocated PE
PV UUID yazajZ-jt1o-rqLY-L0RB-ASzf-gBxT-smDm4j --- Physical volume ---
PV Name /dev/sdc1
VG Name myvg
PV Size 1023.00 MiB / not usable 3.00 MiB
Allocatable yes
PE Size 4.00 MiB
Total PE
Free PE
Allocated PE
PV UUID DURMQf-UVgh-Mjtz-MKqY-emaY-zWFl-QSWFKh

注意:PV中的PE是否处于空闲状态,看的是PE是否被划入LV中,而不是看PE是否存储了数据。

如果不是刚好的话,就需要用户使用pvmove命令将待移除的PV中的非空闲的PE移动到其他PV上去。

现在就可以将这2个不要的PV移出VG,并删除PV。

[root@C7 ~]# vgreduce myvg /dev/sdb3 /dev/sdc1
Removed "/dev/sdb3" from volume group "myvg"
Removed "/dev/sdc1" from volume group "myvg"
[root@C7 ~]# pvremove /dev/sdb3 /dev/sdc1
Labels on physical volume "/dev/sdb3" successfully wiped.
Labels on physical volume "/dev/sdc1" successfully wiped.
[root@C7 ~]# pvs
PV VG Fmt Attr PSize PFree
/dev/sda2 centos lvm2 a-- <.00g
/dev/sdb1 myvg lvm2 a-- <.00g
/dev/sdb2 myvg lvm2 a-- <.00g

既然LV已经缩容成功,那么就可以挂载使用了。

由于我们使用的是XFS文件系统,该文件系统无法缩容,因此在缩容LV的时候,该文件系统的元数据已经受到破坏。

[root@C7 ~]# mount /dev/myvg/mylv /lvm_mount_point/
mount: /dev/mapper/myvg-mylv: can't read superblock

不过上文也说了,我们是使用备份还原的机制来变相实现“缩容”。

我们只需要重新格式化LV为XFS文件系统,挂载并使用文件系统备份文件还原即可。

[root@C7 ~]# mkfs -t xfs /dev/myvg/mylv
mkfs.xfs: /dev/myvg/mylv appears to contain an existing filesystem (xfs).
mkfs.xfs: Use the -f option to force overwrite.
[root@C7 ~]# mkfs -t xfs -f /dev/myvg/mylv
...
省略
...
[root@C7 ~]# mount /dev/myvg/mylv /lvm_mount_point/
[root@C7 ~]# xfsrestore -f /tmp/lvm_mount_point.xfsdump /lvm_mount_point/
xfsrestore: using file dump (drive_simple) strategy
xfsrestore: version 3.1. (dump format 3.0) - type ^C for status and control
xfsrestore: searching media for dump
xfsrestore: examining media file
xfsrestore: dump description:
xfsrestore: hostname: C7
xfsrestore: mount point: /lvm_mount_point
xfsrestore: volume: /dev/mapper/myvg-mylv
xfsrestore: session time: Wed Apr ::
xfsrestore: level:
xfsrestore: session label: "test"
xfsrestore: media label: "test"
xfsrestore: file system id: b7c9c46e-0d54-4ee3-a3e0-e1964799f47d
xfsrestore: session id: e2cfa0fa-d73d-4e94-8f55-cb3f2c581c08
xfsrestore: media id: 57ba6b11-c688-486c-9a73-0e09a1ecd287
xfsrestore: using online session inventory
xfsrestore: searching media for directory dump
xfsrestore: reading directories
xfsrestore: directories and entries processed
xfsrestore: directory post-processing
xfsrestore: restoring non-directory files
xfsrestore: restore complete: seconds elapsed
xfsrestore: Restore Summary:
xfsrestore: stream /tmp/lvm_mount_point.xfsdump OK (success)
xfsrestore: Restore Status: SUCCESS
[root@C7 ~]# ls -l /lvm_mount_point/
total
-rw-r--r-- root root Apr : fstab
[root@C7 ~]# cat /lvm_mount_point/fstab #
# /etc/fstab
# Created by anaconda on Thu Sep ::
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(), findfs(), mount() and/or blkid() for more info
#
/dev/mapper/centos-root / xfs defaults
UUID=9d2e5bb3-799f--a6bd-6ff2d7f55254 /boot xfs defaults
/dev/mapper/centos-swap swap swap defaults
/dev/mapper/myvg-mylv /lvm_mount_point xfs defaults