Table of Contents
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.
Thanks Matthew! Great in-depth how-to that was very understandable and usable. Worked like a charm on Amazon Red-hat ec2
Thanks Matt for this excellent article, it helped me develop some skills with grub!!
I am unable to the kernel and getting below error.
“error: you need to load the kernel first”
any suggestion ???
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!
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!
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`
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.`
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.