Migrate from single-partition boot device to LVM in CentOS7

Introduction to Boot Device to LVM

Single Partition boot device to LVM: There are times when you have a system that has a single boot partition that houses all of the important Linux files, such as /boot and /home, on a single root (/) filesystem. This is particularly true with cloud-based images and virtual machine images, such as those in QCOW2 format. Let’s start with migrating root to LVM.

For example, here is the output of a freshly-built virtual machine from a CentOS7 QCOW2 cloud image:

# df -h
  Filesystem      Size  Used Avail Use% Mounted on
  /dev/vda1       8.0G  865M  7.2G  11% /
  devtmpfs        441M     0  441M   0% /dev
  tmpfs           463M     0  463M   0% /dev/shm
  tmpfs           463M   13M  451M   3% /run
  tmpfs           463M     0  463M   0% /sys/fs/cgroup
  tmpfs            93M     0   93M   0% /run/user/1000
  tmpfs            93M     0   93M   0% /run/user/0

You can see that the root (/) filesystem and all of the user files are stored in a single 8GB partition on /dev/vda1. In many cases, this situation would be fine. But what happens when you want to increase the capacity of the root (/), or want to have separate partitions for /boot and /home?

In this article, we will explore a method for converting from a single partition that contains all of the files for Linux and user files into a system that has a separate partition for /boot and an LVM2 volume group containing the Linux root (/) filesystem.

The following is an example only. You can modify the LVM2 volumes and filesystems however you want, such as creating a separate volume for /var or /tmp.

Prepare for Migration

Step 1: Install the required software

Most cloud images will not include the software we need to create volume groups and migrate data. Install LVM2 and rsync for this procedure to initiate migrating root to LVM.

# yum -y install lvm2 rsync

Step 2: Create the Volume Group and Logical Volume(s)

In our example, we will be migrating to a new disk. The current boot/root device is /dev/vda1 and we will be moving to a boot partition on /dev/vdb1 and a Volume Group on /dev/vdb2.

# lsblk
  NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT 
  vda    253:0    0    8G  0 disk 
  └─vda1 253:1    0    8G  0 part /
  vdb    253:16   0   25G  0 disk

Partition the device with a 1GB partition for partition 1 and the remaining space as partition 2. Partition 1 should have the “bootable” flag set, and partition 2 should be type 8e, or “Linux LVM”.

For further information about partitioning disks with FDisk, please see this article.

# fdisk /dev/vdb
  Welcome to fdisk (util-linux 2.23.2).
  Changes will remain in memory only, until you decide to write them.
  Be careful before using the write command.
  Device does not contain a recognized partition table
  Building a new DOS disklabel with disk identifier 0xd921efb5.
  Command (m for help): n
  Partition type:
     p   primary (0 primary, 0 extended, 4 free)
     e   extended
  Select (default p): 
  Using default response p
  Partition number (1-4, default 1): 
  First sector (2048-52428799, default 2048): 
  Using default value 2048
  Last sector, +sectors or +size{K,M,G} (2048-52428799, default 52428799): +1G
  Partition 1 of type Linux and of size 1 GiB is set
  Command (m for help): n
  Partition type:
     p   primary (1 primary, 0 extended, 3 free)
     e   extended
  Select (default p): 
  Using default response p
  Partition number (2-4, default 2): 
  First sector (2099200-52428799, default 2099200): 
  Using default value 2099200
  Last sector, +sectors or +size{K,M,G} (2099200-52428799, default 52428799): 
  Using default value 52428799
  Partition 2 of type Linux and of size 24 GiB is set
  Command (m for help): t
  Partition number (1,2, default 2):  
  Hex code (type L to list all codes): 8e
  Changed type of partition 'Linux' to 'Linux LVM'
  Command (m for help): a
  Partition number (1,2, default 2): 1
  Command (m for help): p
  Disk /dev/vdb: 26.8 GB, 26843545600 bytes, 52428800 sectors
  Units = sectors of 1 * 512 = 512 bytes
  Sector size (logical/physical): 512 bytes / 512 bytes
  I/O size (minimum/optimal): 512 bytes / 512 bytes
  Disk label type: dos
  Disk identifier: 0xd921efb5
  Device Boot      Start         End      Blocks   Id  System
  /dev/vdb1   *        2048     2099199     1048576   83  Linux
  /dev/vdb2         2099200    52428799    25164800   8e  Linux LVM
  Command (m for help): w
  The partition table has been altered!
  Calling ioctl() to re-read partition table.
  Syncing disks.

Now create the Physical Volume and Volume group.

For further information about LVM2 and creating Volume Groups and Logical Volumes, please see this article.

# pvcreate /dev/vdb2
 Physical volume "/dev/vdb2" successfully created.
# vgcreate rootvg /dev/vdb2
 Volume group "rootvg" successfully created

Create the Logical Volumes. We will be creating a 1 GB volume for swap and a volume for root (/) with the remaining space in the Volume Group. If you want to create additional volumes, this is the time to do it, but you will need to modify the subsequent steps accordingly.

# lvcreate -n swap -L 1G rootvg
   Logical volume "swap" created.
# lvcreate -n root -l 100%FREE rootvg
  Logical volume "root" created. 

Format the filesystems. We will use EXT4 for /boot and XFS for root (/), but you may use other filesystem types as you see fit.

For further information about creating and mounting filesystems, please see this article.

# mkfs.ext4 /dev/vdb1
  mke2fs 1.42.9 (28-Dec-2013)
  Filesystem label=
  OS type: Linux
  Block size=4096 (log=2)
  Fragment size=4096 (log=2)
  Stride=0 blocks, Stripe width=0 blocks
  65536 inodes, 262144 blocks
  13107 blocks (5.00%) reserved for the super user
  First data block=0
  Maximum filesystem blocks=268435456
  8 block groups
  32768 blocks per group, 32768 fragments per group
  8192 inodes per group
  Superblock backups stored on blocks: 
      32768, 98304, 163840, 229376
  Allocating group tables: done                            
  Writing inode tables: done                            
  Creating journal (8192 blocks): done
  Writing superblocks and filesystem accounting information: done

# mkfs.xfs /dev/rootvg/root 
  meta-data=/dev/rootvg/root       isize=512    agcount=4, agsize=1507072 blks
           =                       sectsz=512   attr=2, projid32bit=1
           =                       crc=1        finobt=0, sparse=0
  data     =                       bsize=4096   blocks=6028288, imaxpct=25
           =                       sunit=0      swidth=0 blks
  naming   =version 2              bsize=4096   ascii-ci=0 ftype=1
  log      =internal log           bsize=4096   blocks=2943, version=2
           =                       sectsz=512   sunit=0 blks, lazy-count=1
  realtime =none                   extsz=4096   blocks=0, rtextents=0

# mkswap /dev/rootvg/swap 
  Setting up swapspace version 1, size = 1048572 KiB
  no label, UUID=f76bcb12-8100-432f-9c50-49306a65ffce

Perform the Migration

Step 3: Create directories to mount the volumes

In order to do the copy, we need to mount our new volumes into their target structure. Note that we do not need to activate swap at this time.

# mkdir /newroot
# mount /dev/rootvg/root /newroot/
# mkdir /newroot/boot
# mount /dev/vdb1 /newroot/boot/


We will also need to create a bind mount for the existing root (/) filesystem. This allows us to copy data without copying files we don’t need, like the tmpfs filesystems and /dev.

# mkdir /oldroot
# mount -o bind / /oldroot

Step 4: Copy the data

We will use rsync to copy the data, but other tools should work just as well, so long as they preserve ownership and permissions. This step could take some time depending on how much data there is to copy.

# rsync -avx /oldroot/ /newroot/
 …
 sent 890,446,727 bytes  received 455,945 bytes  23,757,404.59 bytes/sec
 total size is 888,695,623  speedup is 1.00

Step 5: Update configuration files for the new root disk

There are several configuration files that need to be updated in order for the system to boot properly. It is very important that these files are modified carefully, or else the system may become unbootable!

Let’s start with the new fstab. Modify the file /newroot/etc/fstab and make modifications for the new devices that we created.

First, we need to know the UUID of the new /boot filesystem on /dev/vdb1. (Hint: It’s the one that ends in “a13a”!)

# blkid
  /dev/vda1: UUID="de86ba8a-914b-4104-9fd8-f9de800452ea" TYPE="xfs" 
  /dev/vdb1: UUID="3dfa0ae6-e70b-4600-a740-007a0d72a13a" TYPE="ext4" 
  /dev/vdb2: UUID="xsttrD-aJ6R-Sm2k-OEbq-k7wF-eJfG-k9mp7h" TYPE="LVM2_member" 
  /dev/mapper/rootvg-swap: UUID="f76bcb12-8100-432f-9c50-49306a65ffce" TYPE="swap" 
  /dev/mapper/rootvg-root: UUID="49f23775-802b-4f2e-8b82-cecb81118a9d" TYPE="xfs"

Here is the old fstab:

# cat /newroot/etc/fstab
  UUID=de86ba8a-914b-4104-9fd8-f9de800452ea /         xfs     defaults        0 0

And here is the new one:

# cat /newroot/etc/fstab
 /dev/mapper/rootvg-root                   /         xfs     defaults        0 0
 UUID=3dfa0ae6-e70b-4600-a740-007a0d72a13a /boot     ext4    defaults        1 2
 /dev/mapper/rootvg-swap                   swap      swap    defaults        0 0

Now we need to update the Grub defaults file in /newroot/etc/default/grub.

Here is the line we need to edit in the old file:

# grep CMDLINE /newroot/etc/default/grub
  GRUB_CMDLINE_LINUX="console=tty0 crashkernel=auto console=ttyS0,115200"

And here is the new line:

# grep CMDLINE /newroot/etc/default/grub
  GRUB_CMDLINE_LINUX="crashkernel=auto console=ttyS0,115200 console=tty0 rd.lvm.lv=rootvg/root"

VERY IMPORTANT NOTE!
Notice there are multiple differences in the two versions. The most important for booting is the addition of “rd.lvm.lv=rootvg/root”. However, we also moved the text “console=tty0” from the beginning of the line to after the text “console=ttyS0,115200”. This is very important for SELinux auto labelling that we will do later. Failure to do this correctly will lead to a system that will boot, but login will fail (/bin/bash will fail to execute).

Step 6: Modify boot parameters

Next, we need to create a new grub.cfg file, a new initramfs, and install grub to the MBR of the new disk. To do these things, we need to create a chroot jail on our new root volume.

The commands we will run require access to /dev, /sys, and /proc, but those don’t exist in a chroot jail, so we have to bind mount them under the mount point of the new root volume (/newroot).

# mount -o bind /dev /newroot/dev
# mount -o bind /sys /newroot/sys
# mount -o bind /proc /newroot/proc

Now we go into the chroot jail. You can tell we are there because df will show us our new volumes. Note: it is normal that /proc and /sys do not appear in the df output.

# chroot /newroot/
# df -h
 Filesystem               Size  Used Avail Use% Mounted on
 /dev/mapper/rootvg-root   23G  840M   23G   4% /
 devtmpfs                 441M     0  441M   0% /dev
 /dev/vdb1                976M  109M  801M  12% /boot

Create a new grub.cfg file. The errors about lvmetad can be ignored.

# grub2-mkconfig -o /boot/grub2/grub.cfg
 Generating grub configuration file …
   WARNING: Failed to connect to lvmetad. Falling back to device scanning.
   WARNING: Failed to connect to lvmetad. Falling back to device scanning.
 Found linux image: /boot/vmlinuz-3.10.0-862.14.4.el7.x86_64
 Found initrd image: /boot/initramfs-3.10.0-862.14.4.el7.x86_64.img
 Found linux image: /boot/vmlinuz-0-rescue-5003025f93c1a84914ea5ae66519c100
 Found initrd image: /boot/initramfs-0-rescue-5003025f93c1a84914ea5ae66519c100.img
   WARNING: Failed to connect to lvmetad. Falling back to device scanning.
   WARNING: Failed to connect to lvmetad. Falling back to device scanning.
 done

Create a new initramfs file that contains the modules for LVM. It’s important that you use the correct kernel version for this. In this example, there is only one version installed, so it’s easy.

# dracut -f -v /boot/initramfs-3.10.0-862.14.4.el7.x86_64.img
 Executing: /usr/sbin/dracut -f -v /boot/initramfs-3.10.0-862.14.4.el7.x86_64.img
 …
 *** Creating image file done ***
 *** Creating initramfs image file '/boot/initramfs-3.10.0-862.14.4.el7.x86_64.img' done ***

Now we need to install Grub to the boot sector of /dev/vdb. We need the –recheck option so that grub will write a correct device map.

# grub2-install --recheck /dev/vdb
 Installing for i386-pc platform.
 Installation finished. No error reported.

Finally, we need to ensure that SELinux relabels the copied files when the system boots. There are many ways to do this, but the easiest is just to create the file /.autorelabel. If your system is not using SELinux, this step can be skipped. If you’re not sure, just do it; it won’t hurt anything.

# touch /.autorelabel

Exit the chroot jail. And shutdown (not reboot!) the server.

# exit
# shutdown -h now

Step 7: Modify the boot parameters of the VM or physical server.

If the server is a virtual machine, then remove the original boot disk. It is probably best to simply detach it instead of deleting it, just in case there are problems and you need to boot off of it again later.

If the server is physical, then you will need to change the boot order in the BIOS to boot off of the new disk first.

(In the demonstration environment, the virtual machine runs on oVirt. oVirt remembers the order of disks when they are added to the system, so removing the original boot disk puts the DVD-ROM drive first in the boot order. When the system POSTs and finds no DVD in the drive, it fails to boot. To fix this, the new disk needs to have the “bootable” option set on the virtual disk, which moves the new disk to the first in the boot order).

Step 8: Boot the server

If you’ve done things properly, you should be presented with the Grub boot menu. If you wish, you can edit the boot menu option to validate the line starting with “linux16”, which is the kernel command line. It should look like the one below with your root Volume Group and Volume at the end of the line, and your /boot UUID in the “search” line.

...

search --no-floppy --fs-uuid --set=root 3dfa0ae6-e70b-4600-a740-007a0d72a13a

linux16 /vmlinuz-0-rescue-5003025f93c1a84914ea5ae66519c100 root=/dev/mapper/rootvg-root ro crashkernel=auto console=ttyS0,115200 console=tty0 rd.lvm.lv=rootvg/root 

...

Once you are satisfied that the boot menu option is correct, boot the system.

If you are watching the console of your server, you will see the boot process pause with messages similar to the following:

...

*** Warning -- SELinux targeted policy relabel is required.
*** Relabeling could take a very long time, depending on file
*** system size and speed of hard drives.

...

When the SELinux relabeling process has completed, the system will reboot again automatically.

When you login after the final reboot, you will see that the system has been migrated to LVM with a separate /boot partition and swap already activated.

# df -h
  Filesystem               Size  Used Avail Use% Mounted on
  /dev/mapper/rootvg-root   23G  839M   23G   4% /
  devtmpfs                 444M     0  444M   0% /dev
  tmpfs                    463M     0  463M   0% /dev/shm
  tmpfs                    463M   13M  451M   3% /run
  tmpfs                    463M     0  463M   0% /sys/fs/cgroup
  /dev/vda1                976M  103M  807M  12% /boot
  tmpfs                     93M     0   93M   0% /run/user/0

# swapon -s
  Filename            Type        Size    Used    Priority
  /dev/dm-1                        partition   1048572 0   -1

We have successfully migrated a single partitioned boot device to lvm.

Conclusion

Performing maintenance on a root (/) filesystem is always a hassle, and so having a system running from LVM2 can add a great deal of flexibility for disk maintenance, such as increasing a filling filesystem. As we have shown above, migrating from a single partition boot device to LVM2 is not so difficult and does not require a long outage if it’s done right.

Pre-installed virtual machine images are very beneficial for starting up a system quickly, but they are not always built for use over a long period of time. This procedure will help to prevent you from being stuck with a system that does not meet your needs and will allow you to expand your storage as your demand grows.

8 thoughts on “Migrate from single-partition boot device to LVM in CentOS7

  1. Thanks Matthew! Great in-depth how-to that was very understandable and usable. Worked like a charm on Amazon Red-hat ec2

  2. Thanks Matt for this excellent article, it helped me develop some skills with grub!!

  3. I am unable to the kernel and getting below error.

    “error: you need to load the kernel first”

    any suggestion ???

    1. I don’t have a lot of information from you as to where you are seeing that error, but that appears to be an error from GRUB indicating that the configuration doesn’t have a line to load the kernel, or the line is not typed correctly.

      There are a bunch of ways this could happen, including:
      – a typo in the GRUB configuration in Step 5
      – an incomplete file copy and/or permissions and ownership not copied in Step 4
      – incorrect filesytem mounting in Step 3

      It’s really difficult to say. However, the procedure does work, so give it another try and be careful to follow the instructions and I think you’ll be more successful next time!

  4. Thank you so much for this guide, Matthew!! The chroot magic (with mounts) finally allowed me to get the system to update the grub to use my lvm root partition where before it was a standard partition.

    You are a life saver!

  5. i found the problem, because of using recent versions of xfsprogs with older kernels. i have to use these options to create a v4 filesystem

    `mkfs.xfs -f -m crc=0,finobt=0 /dev/rootvg/root`

  6. thanks a lot for your comprehensive guide.
    i followed your instruction and all is good. until in this syntax

    `mount /dev/rootvg/root /newroot/`

    i got error like this

    `mount: /newroot: wrong fs type, bad option, bad superblock on /dev/mapper/rootvg-root, missing codepage or helper program, or other error.`

  7. Thanks a lot for this how-to. I was stuck with that for several day and finally I found missing steps (grub magic) through this comprehensive guide.

Comments are closed.