掘金 后端 ( ) • 2024-04-15 14:27

highlight: a11y-dark

概述

LVM全称Logical Volume Manger,即逻辑卷管理,是Linux环境下对磁盘分区进行管理的一种机制。相比起传统的磁盘分区管理,作用是可以让我们不在容量不足的时候做数据迁移,只需要动态的添加容量即可。

我们来回忆一下传统的服务器磁盘的管理方式。

传统的存储管理

给服务器新增一块硬盘,通过【fdisk -l】的命令来查看硬盘信息,然后对其进行分区,最后使用【mount】命令来挂载。

这样我们就能得到该硬盘对应的挂载路径,像这样:

文件系统                 容量  已用  可用 已用% 挂载点
/dev/sdb1               3.7T 1002G  2.7T   27% /home/data

这种方式简单直接,但是我们在对系统分区,或者添加新硬盘的时候,都会有一个困扰,就是如何精确的评估和分配各个硬盘分区的容量,如果太小,未来容量不够时就要做数据迁移,迁移到另一个更大容量的硬盘上;如果太大,则容易造成磁盘容量的浪费。

浪费磁盘空间还是小事,主要问题在容量不够需要数据迁移时,要对整个文件系统做备份,然后停机、迁移、恢复,过程中还容易导致服务的异常,这是一件非常麻烦的事情,LVM技术则可以解决这个问题。

LVM磁盘分区

LVM在物理设备也就是磁盘和文件系统上做了一层抽象,使得我们最终得到的文件路径可以有动态调整的容量,也就是说,我们一开始将一个硬盘容量映射到指定路径,当容量不够时,添加一个新的硬盘并进行一些LVM的设置,使得两个硬盘的容量可以共同提供给一个文件路径,由此实现了容量的动态扩容,还不会影响上层使用的应用,应用无需再进行数据迁移。

同时,每一个硬盘的容量是可以按需分配的,每一个硬盘就像一个个水桶,需要的地方舀一勺即可,不用理会是哪一个硬盘在提供容量。

两者比较

传统的存储管理方式效率最高,因为LVM做了一层抽象,所以相比较效率会低,虽然说应该根据实际场景来选择,但是LVM是趋势,以后服务器的硬盘只会越来越多,所以学习LVM还是十分有必要的。

LVM必学知识

实战之前先过一遍LVM的几个概念:

  • PV:物理卷(Physical Volume),指磁盘分区,也就是一块磁盘进行了LVM分区后得到的分区路径,如:

    /dev/sdb1
    

    这是添加新磁盘后,对其进行的第一步操作:制作成LVM分区格式。

  • VG:卷组(Volume Group),由一个或多个物理卷PV组成,通过VG我们可以创建一个或多个LV(逻辑卷,下面会说到)。

  • LV:逻辑卷(Logical Volume),在此之上可以建立文件系统,比如映射到/home或/data路径下。

  • PE:物理块(Physical Extent),是物理卷PV的基本划分单元,属于基础知识,新手可以先不理会,先关注PV、VG和LV。

从上面的结构可以初步了解到,相比于传统模式,LVM多了一层VG的封装,正是因为这层封装,使得我们的文件系统变得十分的灵活,接下来我们通过实战来加深理解。

LVM实战

实战的步骤如下:

  • 利用VG剩余可用容量
  • 添加硬盘,制作成LVM分区格式。
    • 重新分区
  • 创建PV及删除PV
  • 创建VG及删除VG
  • 创建LV及删除LV
  • 扩容和缩容

在实战前先查看一下当前的磁盘信息,有一个大概的了解:

# 命令(查看磁盘设备信息):
lsblk

# 输出:
NAME                      MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
loop0                       7:0    0 63.9M  1 loop /snap/core20/2105
loop1                       7:1    0   87M  1 loop /snap/lxd/27037
loop2                       7:2    0 40.4M  1 loop /snap/snapd/20671
loop3                       7:3    0 63.9M  1 loop /snap/core20/2264
# 第一块磁盘(100G)
sda                         8:0    0  100G  0 disk 
├─sda1                      8:1    0    1M  0 part 
├─sda2                      8:2    0    2G  0 part /boot
└─sda3                      8:3    0   98G  0 part 
  # 类型(Type)是LVM,表示此LV上建立了文件系统:根路径
  └─ubuntu--vg-ubuntu--lv 253:0    0   49G  0 lvm  /
sr0                        11:0    1    2G  0 rom

通过上面我们可以了解到,当前系统只有一块硬盘(100G),并制作成了LVM分区,可以看到根路径就建立在LV上面,容量为49G。

疑问:sda3分区明明有98G,为什么LV只有49G?,剩下的49G哪去了?

因为此LV是通过VG创建,VG在创建LV的时候是可以选择指定的容量的,我们看看VG信息就能明白。

# 命令(查看VG详细信息):
vgdisplay

# 输出:
--- Volume group ---
VG Name               ubuntu-vg
System ID             
Format                lvm2
Metadata Areas        1
Metadata Sequence No  2
VG Access             read/write
VG Status             resizable
MAX LV                0
Cur LV                1
Open LV               1
Max PV                0
Cur PV                1
Act PV                1
# VG总容量
VG Size               <98.00 GiB
PE Size               4.00 MiB
Total PE              25087
# 已分配的容量
Alloc PE / Size       12543 / <49.00 GiB
# 剩余可用容量
Free  PE / Size       12544 / 49.00 GiB
VG UUID               NTQ00F-s9bd-c0ma-h64J-wdEL-vn5x-N3iJMY

上面显示剩余可用容量为49G,也就是说VG总容量98G,其中49G拿去建立了根路径的文件系统,还剩下49闲置着没有用,这闲置的容量,我们可以用来给根路径继续增加容量,也可以去建立新的LV,映射一个新的路径,具体操作后面会说到。

利用VG剩余可用容量进行扩容

查看磁盘使用情况:

# 命令(磁盘使用情况):
df -h

# 输出:
Filesystem                         Size  Used Avail Use% Mounted on
tmpfs                              388M  1.6M  387M   1% /run
# 看这里
/dev/mapper/ubuntu--vg-ubuntu--lv   48G  6.8G   39G  15% /
tmpfs                              1.9G     0  1.9G   0% /dev/shm
tmpfs                              5.0M     0  5.0M   0% /run/lock
/dev/sda2                          2.0G  129M  1.7G   8% /boot
tmpfs                              388M  4.0K  388M   1% /run/user/0

根路径LV的容量为49G,假设现在不够了,要扩容。

查看VG剩余容量,在VG没有更多容量时,我们才考虑添加新的硬盘。

# 命令(查看更为精简的VG信息,也可以使用vgdisplay):
vgs

# 输出:
VG        #PV #LV #SN Attr   VSize   VFree 
ubuntu-vg   1   1   0 wz--n- <98.00g 49.00g

还有剩余容量(VFree)还有49G,那就用上。

扩容命令有以下几种方式。

扩容到指定大小

# 命令格式
lvextend -L <容量> <LV路径>

# 如扩容到60G,相比原来的49G增加了11G
lvextend -L 60G /dev/mapper/ubuntu--vg-ubuntu--lv

# 通过lsblk查看效果
lsblk

在原有基础上增加多少

# 命令格式,注意这里有个【+】号
lvextend -L +<容量> <LV路径>

# 如
lvextend -L 10G /dev/mapper/ubuntu--vg-ubuntu--lv

lvextend -L +38G /dev/mapper/ubuntu--vg-ubuntu--lv

# 通过lsblk查看
lsblk

将VG剩余容量全部分给LV

# 命令格式:
lvextend <LV> <分区>

# 如:
lvextend /dev/mapper/ubuntu--vg-ubuntu--lv /dev/sda3

扩容后要使用resize2fs命令重新计算大小

# 通过df -h查看
df -h

Filesystem                         Size  Used Avail Use% Mounted on
tmpfs                              388M  1.6M  387M   1% /run
# 这里并没有正确显示,因为我们还需要通过resize2fs来修改文件系统的大小
/dev/mapper/ubuntu--vg-ubuntu--lv   48G  6.9G   39G  16% /
tmpfs                              1.9G     0  1.9G   0% /dev/shm
tmpfs                              5.0M     0  5.0M   0% /run/lock
/dev/sda2                          2.0G  129M  1.7G   8% /boot
tmpfs                              388M  4.0K  388M   1% /run/user/0
# 命令格式
resize2fs <LV路径>

# 如:
resize2fs /dev/mapper/ubuntu--vg-ubuntu--lv

扩容后通过【df -h】命令显示的还是原来的数据,需要使用【resize2fs】命令来重新计算才能正确显示。

数据精确问题

命令默认显示的容量单位是G,有时候我们扩容时会提示容量不够,比如剩余容量显示为【<10G】,当我们扩容10G会提示不够空间,是因为实际上剩余的容量是9G-10G,那么这时候我们就应该把单位修改成M了,通过给命令增加【--units m】来显示M单位的信息:

pvs --units m

输出:

PV         VG        Fmt  Attr PSize      PFree    
/dev/sda3  ubuntu-vg lvm2 a--  100348.00m 38908.00m

那么扩容命令就可以为:

lvextend -L +38908m /dev/mapper/ubuntu--vg-ubuntu--lv

注:df -h命令改成df -hm,也能看到mb单位的显示。

新增硬盘扩容

好了,现有VG全部用完,

先插入一块硬盘,我使用的是VMware,所以很容易模拟。

# 通过lsblk查看
lsblk

NAME                      MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
loop0                       7:0    0 63.9M  1 loop /snap/core20/2105
loop1                       7:1    0   87M  1 loop /snap/lxd/27037
loop2                       7:2    0 40.4M  1 loop /snap/snapd/20671
loop3                       7:3    0 63.9M  1 loop /snap/core20/2264
sda                         8:0    0  100G  0 disk 
├─sda1                      8:1    0    1M  0 part 
├─sda2                      8:2    0    2G  0 part /boot
└─sda3                      8:3    0   98G  0 part 
  └─ubuntu--vg-ubuntu--lv 253:0    0   98G  0 lvm  /
# 看到我们新增加的硬盘,容量20G(看不到可以重启虚拟机试试)
sdb                         8:16   0   20G  0 disk 
sr0                        11:0    1    2G  0 rom

制作成LVM分区

# 通过fdisk -l或lsblk得知,新硬盘的路径为:/dev/sdb

# 分区
fdisk /dev/sdb

# 输入m查看所有可用命令,输入n表示创建新分区,这里输入:n
Command (m for help): n
# p为主分区,e为扩展分区,这里输入:p
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p): p
# 确定主分区,默认1,这里输入:1(或不输出直接回车)
Partition number (1-4, default 1): 
# 起始扇区,有默认就直接回车了
First sector (2048-41943039, default 2048):
# 结束扇区,默认回车
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-41943039, default 41943039): 
# 然后提示下面这句话,表示成功创建了一个20G容量的分区
Created a new partition 1 of type 'Linux' and of size 20 GiB.
# 最后这里输入w表示保存
Command (m for help): w
# 输出此句表示分区完成。
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

# 此时用fdisk -l或lsblk可以看到分区信息
fdisk -l
# 输出:
Device     Boot Start      End  Sectors Size Id Type
/dev/sdb1        2048 41943039 41940992  20G 83 Linux

新硬盘分完区,我们才能实际进行使用。

创建PV

# 该路径为新磁盘分区后得到的路径
pvcreate /dev/sdb1

# 查看pv列表
pvs
# 输出:
PV         VG        Fmt  Attr PSize   PFree  
/dev/sda3  ubuntu-vg lvm2 a--  <98.00g      0 
/dev/sdb1            lvm2 ---  <20.00g <20.00g

上面显示,/dev/sdb1的PV还没有分配给VG,所以我们可以选择将其分配给新创建的VG,或者是原有的VG。

扩容旧VG

# 命令格式
vgextend <VG> <PV>

# 如:
vgextend ubuntu-vg /dev/sdb1

# 查看vg信息
vgs
# 输出:
VG        #PV #LV #SN Attr   VSize   VFree  
ubuntu-vg   2   1   0 wz--n- 117.99g <20.00g

这就成了,旧VG的容量原本是98G,现在增加了20G,就变成117.99G了,可用容量20G。

觉得单位不精确可以加上【--units -m】命令来显示Mb单位的信息。

移除已添加的PV卷

# 命令格式
vgreduce <VG> <PV>
# 如:
vgreduce ubuntu-vg /dev/sdb1

后悔药,加错了可以撤销。

创建VG

# 命令格式
vgcreate <VG> <分区路径>

# 如:
vgcreate test-vg /dev/sdb1

# 命令:
vps
# 输出:
VG        #PV #LV #SN Attr   VSize   VFree  
test-vg     1   0   0 wz--n- <20.00g <20.00g
ubuntu-vg   1   1   0 wz--n- <98.00g      0 

创建了一个新VG,容量20G,等下想想怎么用。

创建LV

# 命令格式一:(剩余可用PE数可以用命令【vgdisplay <VG> --units m】查看)
lvcreate -L <剩余可用PE数> <VG> -n <LV>

# 如:
lvcreate -L 20476m test-vg -n test-lv
# 或
lvcreate -L 10G test-vg -n test-lv

# 删除lv命令
lvremove <VG> <LV> 
# 如:
lvremove test-vg test-lv

# 命令格式二:(使用100%的剩余可用空间,这里-l要记得小写,然后注意vg和lv顺序)
lvcreate -l +100%FREE -n <VG> <LV>
# 如:
lvcreate -l +100%FREE -n test-lv test-vg

# 格式化逻辑卷
# 命令格式
mkfs.ext4 /dev/<VG>/<LV>
# 如:
mkfs.ext4 /dev/test-vg/test-lv

# 挂载路径(临时挂载,重启后会失效)
# 命令格式
mount /dev/<VG>/<LV> <路径>
# 如:
mount /dev/test-vg/test-lv /data

# 命令(查看磁盘使用情况):
df -h

# 输出:
Filesystem                         Size  Used Avail Use% Mounted on
tmpfs                              388M  1.6M  387M   1% /run
/dev/mapper/ubuntu--vg-ubuntu--lv   97G  7.6G   85G   9% /
tmpfs                              1.9G     0  1.9G   0% /dev/shm
tmpfs                              5.0M     0  5.0M   0% /run/lock
/dev/sda2                          2.0G  252M  1.6G  14% /boot
tmpfs                              388M  4.0K  388M   1% /run/user/0
# 新LV有10G的容量(9.7G差不多了)
/dev/mapper/test--vg-test--lv      9.7G   24K  9.2G   1% /data

缩容和删除

LVM缩容之前需要先取消挂载源路径,是因为缩小逻辑卷可能会导致文件系统的大小超过逻辑卷的大小,从而导致数据丢失或文件系统损坏,所以要先取消挂载源路径,确保文件系统不再使用状态。

基于此原因,不建议工作路径使用根路径,应该创建一个LV路径来使用,这样以后还可以缩容,而挂载根路径缩容是很麻烦的。

缩容前切记先对数据进行备份,安全第一。

LV

先取消挂载:

# 命令格式:
umount <挂载点路径>
# 如:
umount /data

缩容:

# 命令格式
lvreduce --resizefs -L <缩容大小> <LV>
# 如(缩小5G):
lvreduce --resizefs -L 5G /dev/mapper/test--vg-test--lv

删除LV:

干脆不要了:

# 删除lv命令
lvremove <VG> <LV> 
# 如:
lvremove test-vg test-lv

VG

vgreduce:从卷组VG中一处物理卷PV,但是卷组中剩余的最后一个物理卷是不能删除的。

# 命令格式
vgreduce <VG> <PV>
# 如:
vgreduce test-vg /dev/sdb1

vgremove:删除指定的卷组VG,如果VG上有逻辑卷LV时,需要进行确认删除,防止误删。

# 命令格式:
vgremove <VG>
# 如(会有提示是否确定,根据内容进行回答即可):
vgremove test-vg

# 输出:
Do you really want to remove volume group "test-vg" containing 1 logical volumes? [y/n]: y
Do you really want to remove and DISCARD active logical volume test-vg/test-lv? [y/n]: y
  Logical volume "test-lv" successfully removed
  Volume group "test-vg" successfully removed

PV

删除:
# 命令格式,如果有关联VG,需要先执行vgreduce命令移除后再执行
pvremove <PV>
# 如:
pvremove /dev/sdb1

转移:

# 命令格式:
pvmove <PV> <PV>
# 如:
pvmove /dev/sdc1 /dev/sdb1

数据安全问题

LVM中,一个LV可能由多个磁盘供应容量,其中任意一块磁盘损坏都会导致服务不可用,也就是说服务稳定性低了很多,因为传统模式下只有一块硬盘,而在LVM模式下可能有多块硬盘,数据就不够安全了。

所以我们需要有一系列的措施来保障数据安全,可以有:

  • 准备一个足够大容量的硬盘,将原来多个小硬盘的数据转移到这个大硬盘上,减少一点硬盘损坏概率。
  • 定期做数据备份

参考资料

Linux LVM逻辑卷相关管理