We will see how to generate a rootfs tarball for redhat based system. Since we are targetting redhat based system, I am going to use debian based system (Ubuntu 17.10 64-bit - latest at the time of writing this post) as my host. We will generate rootfs tarball for CentOS 7 64-bit.

The below mentioned steps are validated on Ubuntu 17.10 desktop installation.

Lets get started

As usual the first step, install the required dependecy packages which in our case is yum.

sudo apt install yum

Second, will prepare the required directory structure. In our example ~/work will be our working directory and we will install the CentOS 7 roofs in inside of work in a directory named centos7.

export CENTOS\_BASE\_DIR=~/work
export CENTOS\_ROOT\_DIR=${CENTOS\_BASE\_DIR}/centos7

mkdir -p ${CENTOS\_ROOT\_DIR}

cd ${CENTOS\_BASE\_DIR}

Now we need to generate the repo metadata file for CentOS 7 installation. Below code does the same.

cat > ${CHROOT_BASE_DIR}/chroot-centos7.repo << EOF

[centos7-chroot-base]
name=CentOS-7-Base
baseurl=http://mirror.centos.org/centos/7/os/x86_64
gpgcheck=0

[centos7-chroot-epel]
name=Extra Packages for Enterprise Linux 7
baseurl=http://dl.fedoraproject.org/pub/epel/7/x86_64
gpgcheck=0

EOF

Once we have our directory structure and the repo file prepared as mentioned above, we can bootstrap the CentOS 7 root by executing the below mentioned command. This creates a base rootfs with networking utilities and sudo command.

sudo yum -y -c chroot-centos7.repo --disablerepo=* \
    --enablerepo=centos7-chroot-base \
    --enablerepo=centos7-chroot-epel \
    --disableplugin=* \
    --installroot=${CHROOT_CENTOS7_BASE_DIR} install \
	bash \
	bash-completion \
	vim-minimal \
	yum \
	iproute \
	iputils \
	rootfiles \
	sudo

After the yum installation is complete we need to prepare our rootfs for customization. We will start by mounting the required mount points to use chroot.

sudo mount --bind /dev ${CHROOT_CENTOS7_BASE_DIR}/dev
sudo mount --bind /dev/pts ${CHROOT_CENTOS7_BASE_DIR}/dev/pts
sudo mount --bind /sys ${CHROOT_CENTOS7_BASE_DIR}/sys
sudo mount --bind /proc ${CHROOT_CENTOS7_BASE_DIR}/proc
sudo mount --bind /home ${CHROOT_CENTOS7_BASE_DIR}/home
sudo cp /etc/resolv.conf ${CHROOT_CENTOS7_BASE_DIR}/etc/

Now we can start using the rootfs using chroot command. We will do the following customizations

  1. Setup releasever and basearch yum variables. Unfortunately this is required because of the caveat mentioned in Appendix I.
  2. Remove unncessary files like yum cache, locale definitions, temporary files, etc.
  3. Generate proper yum.conf, locale definition, build timestamp file and machine-id file - these are pretty standard stuff required for storage optimization in case we decide to use this rootfs for docker images
sudo chroot ${CHROOT_CENTOS7_BASE_DIR}

# Step no 1

echo "7" > /etc/yum/vars/releasever
echo "x86_64" > /etc/yum/vars/basearch

# Step no 2
yum clean all
rm -rf /boot /var/cache/yum/* \
    /tmp/ks-script* \
    /var/log/* \
    /tmp/* \
    /etc/sysconfig/network-scripts/ifcfg-*

###Optional for making the rootfs lean to be used as base container
umount /run
systemd-tmpfiles --create --boot
rm -f /var/run/nologin

# Step no 3
echo 'container' > /etc/yum/vars/infra

awk '(NF==0&&!done){print "override_install_langs='$LANG'\ntsflags=nodocs";done=1}{print}' \
    < /etc/yum.conf > /etc/yum.conf.new
mv /etc/yum.conf.new /etc/yum.conf

rm -f /usr/lib/locale/locale-archive

#Setup locale properly
localedef -v -c -i en_US -f UTF-8 en_US.UTF-8
/bin/date +%Y%m%d_%H%M > /etc/BUILDTIME

:> /etc/machine-id
###End container

After the rootfs configuration we need to umount all the mount points we mounted earlier. The unmount order is important here.

sudo umount ${CHROOT_CENTOS7_BASE_DIR}/home
sudo umount ${CHROOT_CENTOS7_BASE_DIR}/proc
sudo umount ${CHROOT_CENTOS7_BASE_DIR}/sys
sudo umount ${CHROOT_CENTOS7_BASE_DIR}/dev/pts
sudo umount ${CHROOT_CENTOS7_BASE_DIR}/dev
sudo rm ${CHROOT_CENTOS7_BASE_DIR}/etc/resolv.conf

We do one last cleanup before we package the rootfs as a tarball.

sudo rm -rf ${CHROOT_CENTOS7_BASE_DIR}/boot
sudo rm -rf ${CHROOT_CENTOS7_BASE_DIR}/var/cache/yum/*
sudo rm -f ${CHROOT_CENTOS7_BASE_DIR}/tmp/ks-script*
sudo rm -rf ${CHROOT_CENTOS7_BASE_DIR}/var/log/*
sudo rm -rf ${CHROOT_CENTOS7_BASE_DIR}/tmp/*
sudo rm -rf ${CHROOT_CENTOS7_BASE_DIR}/etc/sysconfig/network-scripts/ifcfg-*

And the final piece, run the below command to generate the CentOS 7 rootfs tarball.

sudo tar --exclude=centos7/home \
    --exclude=centos7/var/cache/yum/*  \
    -Jcvf centos7.tar.xz centos7

References

  1. Customizing yum variables

Appendix I

While running yum command inside of the CentOS 7 chroot I ran into below error message. Strangely this happens on Ubuntu host but not on CentOS host. As a fix I have created yum variables releasever and basearch inside the CentOS rootfs. Please check Reference section for the manual describing how to customize yum variables.

$ yum search yum
Failed to set locale, defaulting to C
Loaded plugins: fastestmirror


 One of the configured repositories failed (Unknown),
 and yum doesn't have enough cached data to continue. At this point the only
 safe thing yum can do is fail. There are a few ways to work "fix" this:

     1. Contact the upstream for the repository and get them to fix the problem.

     2. Reconfigure the baseurl/etc. for the repository, to point to a working
        upstream. This is most often useful if you are using a newer
        distribution release than is supported by the repository (and the
        packages for the previous distribution release still work).

     3. Run the command with the repository temporarily disabled
            yum --disablerepo=<repoid> ...

     4. Disable the repository permanently, so yum won't use it by default. Yum
        will then just ignore the repository until you permanently enable it
        again or use --enablerepo for temporary usage:

            yum-config-manager --disable <repoid>
        or
            subscription-manager repos --disable=<repoid>

     5. Configure the failing repository to be skipped, if it is unavailable.
        Note that yum will try to contact the repo. when it runs most commands,
        so will have to try and fail each time (and thus. yum will be be much
        slower). If it is a very temporary problem though, this is often a nice
        compromise:

            yum-config-manager --save --setopt=<repoid>.skip_if_unavailable=true

Cannot find a valid baseurl for repo: base/$releasever/x86_64
$