Linux: Working with LVM

  • Working with Linux LVM mostly comes down to manipulating three things: Physical Volumes (PV), Volume Groups (VG) and Logical Volumes (LV.) Each must exist in that order for anything to work; meaning we can't make an LV until we have VG, and we can't make a VG until we have at least one PV.

    The first thing that we need to do is to place the block device(s) that we are going to be working with under LVM management by designating them as PVs. Traditionally we use an entire block device as presented to the operating system for simplicity. You can, of course, create partition(s) on top of the device and only add one or more partitions as PVs, but generally this is avoided.

    In this first example, we need to determine the block device that we are going to work with. In this example we have VIRTIO devices as block devices which show up as /dev/vd* devices.

    ls | grep vd

    The /dev/vda block device is partitions by the installation system and has two partitions: /dev/vda1 and /dev/vda2. What we are interested in is the unused second device, /dev/vdb. This is a device that we added just for the example and is commonly what we would find in the wild. In order to add a block device under LVM, we have to use the pvcreate command. This is very straightforward, just pvcreate and the name of the device.

    # pvcreate /dev/vdb
      Physical volume "/dev/vdb" successfully created

    There are two commands for each layer of the LVM system that shows us the status of the layer. For the PV layer, these commands are pvs and pvdisplay. The short three letter names give quick summaries and the longer "display" formats give us a lot of details.

    First let's look at the summary.

    # pvs
      PV         VG     Fmt  Attr PSize  PFree 
      /dev/vda2  centos lvm2 a--  18.13g 40.00m
      /dev/vdb          lvm2 ---  37.25g 37.25g

    From this we can see that /dev/vda2, which we know was a partition of /dev/vda, is added as a PV under LVM. This is the CentOS and RHEL standard default build configuration so you will see this much of the time. This is CentOS 7 in our example here. What is of more interest to us is that /dev/vdb has been successful added as a PV. The lvs command shows us a quick list of the PVs, tells us if they are actively part of a Volume Group (VG), tells us which LVM format is used (all will be LVM2 currently), any special attributes are shown, the physical size (PSize) of the device and how much of it has been used (PFree.)

    How that we have /dev/vdb as a PV we need to add it to a VG in order to use it. For this we use the vgcreate command. With this command we will make our first VG and add our PV to it at the same time. We can name our VG whatever we want, but calling it something useful is pretty important. A common reason for creating a VG is to hold data files, so calling it "data" is handy. To make it very obvious that we are dealing with a VG, it is common practice to preface the name with "vg_". So in our example we will call our first VG "vg_data", but you could just as easily have name it "saturn", "snoopy", "vg1" or "myvg".

    # vgcreate vg_data /dev/vdb
      Volume group "vg_data" successfully created

    Now, as before, we can look at the summary of our VG with the vgs command.

    # vgs
      VG      #PV #LV #SN Attr   VSize  VFree 
      centos    1   2   0 wz--n- 18.13g 40.00m
      vg_data   1   0   0 wz--n- 37.25g 37.25g

    We can see, again, that the CentOS default install already has create a VG called "centos" that is fully used (all except for 40MB which is really just spillover overhead.) And we can see our new "vg_data" VG is now there. The first column shows us the VG name, the second is #PV and tells us how many PVs are included in this VG. In both cases here, there is only on PV per VG. The third column shows us how many current Logical Volumes (LVs) have been created on top of each VG. The existing "centos" VG has two LVs created on top of it and, of course, our new "vg_data" VG has none so far.

    Now that we have a working VG, we are free to create our LV. We can make just one LV on top of each VG or we can make many. Think of LVs much like partitions in the "old days", but more flexible. In this example, we will make just one LV that uses all of the available free space on the existing VG. There are many ways to specify how large to make an LV, we will use the simplest syntax in this example of "-l 100%FREE" which simply uses all available space. We also specify the name of the LV like this "-n lv_data".

    # lvcreate -l 100%FREE -n lv_data vg_data
      Logical volume "lv_data" created.

    Quick and easy. We now have our first LV. But before we do anything with it, we should take a quick look using, you guessed it, lvs to get a summary of the LVs on our system now.

    # lvs
      LV      VG      Attr       LSize  Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
      root    centos  -wi-ao---- 16.23g                                                    
      swap    centos  -wi-ao----  1.86g                                                    
      lv_data vg_data -wi-a----- 37.25g 

    As we can see, the "centos" VG from the system installation has two LVs created automatically, "root" and "swap". Our new LV "lv_data" is visible there, too. So we know that our new LV is now available for us to use as a normal block device.

    So now we have to find where this new block device exists on the system so that we can use it in standard commands. In CentOS and RHEL, this will show up be default in two locations. The first is under /dev/vgname/lvname and the second is under /dev/mapper/vgname-lvname. For the moment, we will use the former but both work just fine.

    # ls /dev/vg_data/
    # ls /dev/mapper/
    centos-root  centos-swap  control  vg_data-lv_data

    Of course what we have currently is simply a block device, nothing special. To the OS we have just added a new hard drive that is ready to be used. So, just like any block device that we want to use, we need to start by applying a filesystem to it. We've successfully configured our first LV and now are just making a filesystem as usual.

    Continuing with our example and assuming that we will use XFS we would do this:

    # mkfs.xfs /dev/vg_data/lv_data 
    meta-data=/dev/vg_data/lv_data   isize=256    agcount=4, agsize=2441216 blks
             =                       sectsz=512   attr=2, projid32bit=1
             =                       crc=0        finobt=0
    data     =                       bsize=4096   blocks=9764864, imaxpct=25
             =                       sunit=0      swidth=0 blks
    naming   =version 2              bsize=4096   ascii-ci=0 ftype=0
    log      =internal log           bsize=4096   blocks=4768, version=2
             =                       sectsz=512   sunit=0 blks, lazy-count=1
    realtime =none                   extsz=4096   blocks=0, rtextents=0

    Now we are ready to mount our new LV. Since we called it "lv_data" it probably makes sense to mount it under /data, right? So here we go:

    # mkdir /data
    # mount /dev/vg_data/lv_data /data
    # df -x tmpfs -x devtmpfs
    Filesystem                  1K-blocks    Used Available Use% Mounted on
    /dev/mapper/centos-root      17008640 1193816  15814824   8% /
    /dev/vda1                      508588  151840    356748  30% /boot
    /dev/mapper/vg_data-lv_data  39040384   32928  39007456   1% /data

    As you can see, the system mounts using the "/dev/mapper" syntax under the hood automatically regardless of which format we used ourselves. Both syntax are actually symlinks anyway. So this is just fine. Six of one, half dozen of the other.

    Part of a series on Linux Systems Administration by Scott Alan Miller

  • Fixed a typo. Thanks @BRRABill for checking.

  • And by checking, he means learning and just happening to find it.