Installing CentOS 7 with a chroot

I needed to install CentOS 7 on an embedded PC with UEFI and 2 SSD disks in mdadm RAID1.

While I’m sure the guys at Red Hat work very hard on CentOS, the installer is a piece of cr*p, especially when it comes to disk partitioning. I have never hated any installer more than the CentOS disk partitioner. I don’t know what happened. The disk partitioning tool in CentOS 6 installer was fine, I had no problems using it, but in the 7 installer it’s just a nightmare to do anything. In my opinion Windows installer does a better job of disk partitioning than the CentOS 7 installer.

While many people who like CentOS will proclaim that the error is between the keyboard and chair, I welcome them to provide a write-up and screenshots of how to accomplish my desired partitioning scheme using the graphical installer. If the instructions are shorter than this blog post, next time I won’t use a chroot to install.

I loosely based my method off of this post, but immediately found I had to deviate because I didn’t have USB install media with all the required commands on it.

Required tools:
1) USB stick with some Linux distro on it (I prefer the Gentoo minimal installer, it’s small and it includes lots of useful utilities)
2) USB stick with the CentOS minimal installer on it
3) About 2GB of free space, you can use a ramdisk, or create an extra partition using the free space on the USB sticks (CentOS occupies about 800MB, Gentoo about 300MB)

Steps:
1) Boot the Gentoo installer off the USB stick
2) Partition your disks however you like using gdisk, fdisk, or parted
3) Create your mdadm array(s)
4) Plug in the CentOS 7 USB stick and mount it to a temporary mount point (e.g. /tmp/centos)

Inside the CentOS USB stick you will find LiveOS/squashfs.img, you need to loopback mount this:

livecd ~ # mount -o loop /tmp/cinstall/LiveOS/squashfs.img /tmp/csquashfs/

Now we have yet another image to mount, this one within the squashfs image:

livecd ~ # mount -o loop /tmp/csquashfs/LiveOS/rootfs.img /tmp/croot

Finally we have a Linux filesystem. But unfortunately we cannot use it for anything as it is mounted read-only and there is no resolv.conf present, so no domain names can be resolved. This is why you need ~2GB of free space somewhere (or 4GB of RAM).

livecd ~ # mkfs -q /dev/ram1 1572864
livecd ~ # mkdir -p /tmp/ramdisk
livecd ~ # mount /dev/ram1 /tmp/ramdisk
livecd ~ # rsync -avHp /tmp/croot /tmp/ramdisk

Now that we have the installer rootfs somewhere writable, copy /etc/resolv.conf to the filesystem:

livecd ~ # cp /etc/resolv.conf /tmp/ramdisk/etc/resolv.conf

Mount your destination partition for CentOS somewhere you can access from within the chroot:

livecd ~ # mount /dev/vg0/centos /tmp/ramdisk/mnt

Chroot to the installer environment:

livecd ~ # chroot /tmp/ramdisk

Download the CentOS release RPM and install it to the destination partition:

bash-4.2# wget http://mirror.centos.org/centos/7/os/x86_64/Packages/centos-release-7-0.1406.el7.centos.2.3.x86_64.rpm
bash-4.2# rpm --root=/mnt --nodeps -i centos-release-7-0.1406.el7.centos.2.3.x86_64.rpm

Because yum is missing the yummain module in the installation environment, we need to download and install the yum RPM on the installer partition:

bash-4.2# wget http://mirror.centos.org/centos/7/os/x86_64/Packages/yum-3.4.3-118.el7.centos.noarch.rpm
bash-4.2# rpm -i --nodeps yum-3.4.3-118.el7.centos.noarch.rpm

Now finally we can run yum on the destination partition to install CentOS:

bash-4.2# yum --installroot=/mnt update
bash-4.2# yum --installroot=/mnt install -y yum
bash-4.2# yum --installroot=/mnt install -y @core kernel
bash-4.2# yum --installroot=/mnt install -y grub2-efi efibootmgr lvm2 mdadm \
dosfstools kernel

Now unfortunately I hit a small snag: the Gentoo installer isn’t EFI aware. Exit the chroot, but remember to copy /etc/resolv.conf to the destination partition:

livecd ~ # cp /etc/resolv.conf /tmp/ramdisk/mnt/etc/

Poweroff and unplug the Gentoo installer USB stick. Plug in the CentOS installer stick and boot to the rescue environment. Skip rootfs detection.

Mount the partition/LV slice containing your CentOS installation:

sh-4.2# mkdir /mnt/centos
sh-4.2# mount /dev/vg0/centos /mnt/centos
sh-4.2# mount -t proc proc /mnt/centos/proc
sh-4.2# mount --rbind /dev /mnt/centos/dev
sh-4.2# mount --rbind /sys /mnt/centos/sys
sh-4.2# chroot /mnt/centos /bin/bash

Save the mdadm array information to the mdadm.conf configuration file:

bash-4.2# mdadm --detail --scan > /etc/mdadm.conf

Format your EFI boot partition:

bash-4.2# mkfs.vfat /dev/sda1
bash-4.2# mkdir /boot/efi
bash-4.2# mount /dev/sda1 /boot/efi

Install grub:

bash-4.2# grub2-install
Installing for x86_64-efi platform
...
Installation finished. No error reported.

Now, dear reader, this is the part where you do not see the hours I spent debugging why dracut would not find my root partition (hint: see Fedora wiki for dracut debugging steps). The tl;dr is that the mdadm array wasn’t being assembled, for reasons still unknown. To solve this we need to add our array UUID as an additional kernel parameter for grub:

bash-4.2# MD_UUID=$(mdadm -D /dev/md0 | grep UUID | awk '{print $3}')
bash-4.2# grubby --update-kernel=/boot/vmlinuz-3.10.0-123.20.1.el7.x86_64 \
--args="rd_MD_UUID=$MD_UUID"
bash-4.2# grub2-mkconfig -o /boot/grub2/grub.cfg

Absolutely verify in /boot/grub2/grub.cfg that the correct rd_MD_UUID was appended to linuxefi, or like me, you may be left wondering why your system won’t boot.

Check with efibootmgr that a menu entry was created:

bash-4.2# efibootmgr -v
...
Boot0014* grub  HD(1,800,32000,SUPER-LONG-UUID)File(\EFI\grub\grubx64.efi)

Don’t forget to set a root password:

bash-4.2# passwd
Changing password for user root.
New password:
Retype new password:
passwd: all authentication tokens updated successfully

Configuring the hostname (using hostnamectl, or editing /etc/sysconfig/network and /etc/hostname), udev rules for eth* interface names, and static network configuration in /etc/sysconfig/network-scripts/ifcfg-eth* is left as an exercise for the reader.

After rebooting

If when you reboot you find that you cannot login as root using the password you specified, it’s probably SELinux. Normally I hate disabling SELinux, but in this case I was so tired of spending a day and a half debugging booting issues, I just disabled it and went on with the setup.

Despite what the wonderful CentOS installer tells you, you do not require a separate /boot partition. Right now this is what the partition layout looks like:

Disk /dev/sda: 128.0 GB, 128035676160 bytes, 250069680 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disk label type: gpt


#         Start          End    Size  Type            Name
 1         2048       206847    100M  EFI System      EFI System
 2       206848    250069646  119.1G  Linux RAID      Linux RAID

Because the contents of /boot/efi is static, and cannot be mdraid, remember to copy the contents of /dev/sda1 to /dev/sdb1 so that if your first drive ever dies, you will still have the required EFI components to boot off the second drive.

Overall, I can’t say this was a lot of fun. But I did learn a lot more about dracut and the CentOS booting process, and I can still say with confidence that I hate the CentOS 7 installer.

2 thoughts on “Installing CentOS 7 with a chroot

  1. Lidia Clases

    I can agree with you on 1 thing, CentOS 7 installer really sucks on big! :))

    Reply
  2. Alexei Colin

    Two notes:
    1. Debian (and likely on Ubuntu, too) the rpm package configuration differs from the default configuration in the database path. Debian package sets rpm’s _dbpath macro to point to ~/.rpmdb instead of the system path /var/lib/rpm. This results in a broken roots chroot in a very non-obvious way, where the surface symptom is that yum update fails, and ultimate symptom is that centos-release is not actually seen as installed within chroot, because rpm within the chroot looks for the db at /var/lib/rpm and finds it as empty (silent, no error, too!), while rpm –root from outside chroot reports packages as installed because it’s looking at chroot/root/.rpmdb created during installation. Tne fix is to revert _dbpath macro to the system path before creating the chroot: create or edit the /etc/rpm/macros with contents “%_dbpath /var/lib/rpm”.

    2. For creating the CentOS chroot itself, I found that it is not necessary to manually download and install .rpm packages. it’s sufficient to just do:
    sudo yum –installroot=$PWD install -y @core

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *