How to create a single CD for fast and easy customized installation.
Installing Linux is a relatively easy task. However, I was faced with the task of installing it on multiple machines repeatedly, which is time consuming and prone to errors. This problem affected our whole group and other groups that relied on us. So I started using Red Hat Kickstart to automate the installs. This helped, but there still was room for improvement. What I ultimately wanted was an automated installation that would fit on one CD, dynamically partition the hard drives and contain all of the updated packages. I wanted to be able to start an installation then walk away from the machine, returning only when it was done. To accomplish this, I supplemented Kickstart with a customized version of the Red Hat installation program, Anaconda.
Although not officially supported, Red Hat has made available several tools and documentation to assist in customizing an installation. I describe a few of the possible ways to do this here, which should give the reader enough information to get started.
The following topics are covered in this article: replacing packages with updates, reducing the installation size to fit on one CD, utilizing Kickstart in the custom installation and creating a custom message screen.
The reader should have a good understanding of Linux installations in general. I also assume that no esoteric hardware is being used, as other customizations may be needed to accommodate such hardware.
The first step is to prepare the build computer. Because the installer tools are specific to a particular release, the build computer needs to have the same Red Hat release as the one used on the target(s). For our example, Red Hat 8.0 is being used. There are some differences between Red Hat 8.0 and previous versions, and you need to investigate them if you use a previous version.
Once the build computer has the correct release installed, the Anaconda packages need to be installed. These usually are not installed by default, so they need to be manually installed. They are located on the second CD of the standard Red Hat distribution and are named anaconda, anaconda-runtime and anaconda-help (optional).
The next step is to create a directory structure where the installation files will be located. The partition should have adequate space available, approximately 3GB. The actual location is based on your preference; for this article, the base directory is located at /RH80. Under this directory, we create directories for each of the CDs:
mkdir -p /RH80/CD{1,2,3}
We are not concerned with the source packages, so CD4 and CD5 are not included.
We make an additional directory where we can create the custom installation:
mkdir /RH80/ONE_CD
Now we can copy the contents of the CDs to the respective directories. Mount the first CD, then issue this command:
cp -a /mnt/cdrom/* /RH80/CD1/Repeat this step for CD2 and CD3.
Copy the contents of the CD directories to the ONE_CD directory, but hard link them instead of actually copying the files. This saves space and is quicker:
cd /RH80 cp -al CD1/* ONE_CD/ cp -al CD{2,3}/RedHat/RPMS/* ONE_CD/RedHat/RPMS/
You'll get an overwrite TRANS.TBL message; you can answer no.
Next we trim down the contents of the ONE_CD directory so it fits on one CD. I assume the CD size to be 700MB. I will not go into detail on how to do this, as the list of files to remove from the distribution is different from one installation to another. However, here are some tips for trimming down the distribution:
Don't include the source RPMs.
Remove the dosutils directory, as these will be automated installs.
Remove any unnecessary packages. This can be complicated, because you need to make sure that the dependencies are still intact.
You should keep a record of the files that were removed. You can use this list in case you need to back out, and you will need it later if you edit the comps.xml file.
For the package selection, I logged to a file all of the base and core group packages with their dependencies (according to the comps.xml file). In order to find this information, I used the script getGroupPkgs.py (see Resources):
cd /RH80/ONE_CD/RedHat/base getGroupPkgs.py comps.xml > /RH80/pkglist
Any additional package names can be appended to the end of this file. After my list was complete, I removed the packages not on the list by using the syncRpms.py script (see Resources). The arguments are the package directory and the list of packages is generated from getGroupPkgs.py. This script removes the packages not listed in the package list and prints out the package names. We redirect that to a file so we have a log:
cd /RH80 syncRpms.py ONE_CD/RedHat/RPMS/ pkglist > pkgs_remWe can monitor the installation size by using the du command. The -h option displays the result in human readable format, and the -s option gives a summary of the whole directory tree:
du -hs /RH80/ONE_CDThe hdlist files actually decrease in size after they are regenerated (see below), because we removed many of the packages. This in turn reduces the size of the CD image.
The tricky part about removing packages is they may break dependencies. Even though getGroupPkgs.py resolved the dependencies base on the comps.xml file, they are not guaranteed to be accurate. Adding additional packages may break dependencies as well. One way to verify their accuracy is to create a temporary RPM database, and then do a test install on that database with the packages you have selected:
cd /RH80/ONE_CD/RedHat/RPMS mkdir /tmp/testdb rpm --initdb --dbpath /tmp/testdb rpm --test --dbpath /tmp/testdb -Uvh *.rpm
Look for any error messages regarding failed dependencies. If any appear, resolve the dependencies by either adding or removing files that caused the discrepancy, and repeat the above test.
Once the package dependencies have been resolved, you can download the package updates pertinent for your installation. Put these files under a separate directory:
mkdir -p /RH80/updates/RPMS/
Remove the old files from the build directory and then link the updated files to the build directory. Do this for each updated package (where old_rpmfile is the previous version of the package):
cd /RH80/ONE_CD/RedHat/RPMS/ rm # ... remove each old rpm cd /RH80/updates/RPMS/ cp -l # ... do this for each rpmYou should keep a record of the updated packages, in case you need to back them out. It's also a good idea to check the dependencies and size one more time, in case they changed after the packages were updated.
Next, we check the internal checksum of each package with the -K option to rpm. First we need to import the key:
cd /RH80/ONE_CD/RedHat/RPMS rpm --import /usr/share/rhn/RPM-GPG-KEY rpm -K *.rpm | grep "NOT *OK"
This isn't strictly necessary, but because we downloaded package updates, this verifies they are valid.
Once all of the packages have been updated, we need to regenerate the hdlist files. They contain only the headers of the packages, which allows Anaconda to retrieve the header information more quickly. Because we updated packages, we need to regenerate these files with the genhdlist tool, which is part of the anaconda-runtime package:
/usr/lib/anaconda-runtime/genhdlist /RH80/ONE_CD/
Next we need to handle the comps.xml file. This file defines package groups and package dependencies (although they are not guaranteed). The file structure was changed in Red Hat 8.0 to be XML-based; in previous releases it was only a simple text file with some obscure tags. We need to ensure that packages defined within groups are included in our installation. We need to be concerned only with groups we are installing. If packages are missing (or if packages were added), we need to edit the comps.xml file (see Resources). Because we chose all of the packages in the Core and Base groups, however, we don't need to edit this file. We simply need to specify those groups under the %packages directive in the Kickstart file. See Listing 1 for an excerpt from the Kickstart file.
Listing 1. Excerpt from the Kickstart File
Technically, we can leave out the @Core and @Base groups, as they are installed by default. They are included here for illustrative purposes.
We also want to create a custom message screen to give the user special instructions. The message screens are kept in the boot.img file (for CD-ROM installs) under the images directory. This is a DOS filesystem, so we can mount it to get to the contents:
cd /RH80/ONE_CD/images mount -o loop -t msdos boot.img /mnt/boot
Looking in /mnt/boot, you see six message files: boot.msg, options.msg, general.msg, param.msg, rescue.msg and snake.msg. We create our own message file and call it custom.msg, an arbitrary name. Because snake.msg isn't really used, we replace that entry within syslinux.cfg with custom.msg. Edit syslinux.cfg in /mnt/boot and replace F7 snake.msg with F7 custom.msg.
A few other modifications were made to the syslinux.cfg file; refer to Listing 2. The default entry was changed from linux to ks. If the timeout occurs or if the user presses Enter at the boot prompt, then the ks label is used. Additionally, the timeout value was decreased from 600 to 60, so the installation can start sooner if there is no input from the user. The display entry was changed as well. Instead of boot.msg being the initial message screen, we wanted our custom message to be displayed. For the append line under the ks label, we added two things. The first is the keyword text, to enable text-based installs. Then we changed the keyword ks to ks=cdrom:/ks.cfg. This hard codes the Kickstart location so the user doesn't have to specify it at the boot prompt.
Listing 2. Modified syslinux.cfd File
Next we create our custom.msg file. Listing 3 shows our custom.msg. The contents of the file can be marked up, such as adding color around words. For example, ^O09Custom^O02 changes the color of Custom to blue, and ^O02 changes it back. See the syslinux reference in the Resources section for more information.
Once we have composed the custom message, we need to umount the boot image:
umount /mnt/boot
Before actually creating the CD, you may want to test it by doing a network install. See the Red Hat Kickstart documentation on how to do this.
We want this custom installation to be automated, so we put the Kickstart file on the CD itself. You can create the Kickstart file with any text editor, or you can use the GUI tool called Kickstart Configurator.
In the %pre section, add a shell script to probe the hard drives and dynamically create the partition information based on the number of drives. We take advantage of the fact that Kickstart executes the %pre section and then re-reads the Kickstart file. When it reads it for the second time, it includes the /tmp/partinfo file where the %include directive is located (see Listing 1). The /tmp/partinfo file is the output from the script. We use the list-harddrives command, which lists the available hard drives and their sizes. Having the partition created dynamically frees us from having to create multiple Kickstart files that hard code the partition information.
Once the Kickstart file is created, name it ks.cfg and place it in the root directory of our installation tree (/RH80/ONE_CD/). It is possible to create more than one Kickstart file and place all of them on the CD. Different Kickstart files might address different hardware configurations.
We can now create the ISO image. From our previous steps, the distribution should be small enough to fit on one CD and contain all of the updated packages. The mkisofs program creates the image, and then we can copy the image to the CD. The command to create the ISO image is:
cd /RH80/ONE_CD mkisofs -r -T -J \ -V "My Custom Installation CD" \ -b images/boot.img \ -c images/boot.cat \ -o /RH80/mydist.iso \ /RH80/ONE_CD
Refer to Table 1 for a description of the options.
Table 1. Options Used for mkisofs
The last parameter to mkisofs is the source directory of the contents that need to be included in the image file (e.g., our custom installation directory). Several other parameters are available that you may want to use. For example, the -A, -P and -p options add additional labeling information to the image. The -m and -x options also allow you to exclude certain directories and file patterns from the image. See the mkisofs man page for additional information.
Next, add a checksum to the ISO image. This is not strictly necessary, but it provides a way for end users to check the integrity of the CD. The tool to add a checksum to the ISO image is called implantisomd5. To add a checksum to the ISO image, use the following command:
implantisomd5 /RH80/mydist.iso
A companion tool, checkisomd5, can be used to verify the checksum for you:
checkisomd5 /RH80/mydist.isoThe CD also can be verified during the installation. After booting from the CD, the user can issue this command:
linux mediacheckNow we can burn the image to the CD. I assume the CD writer is already set up on your system. We use cdrecord below, but you can use other programs as well. The command is invoked as:
cdrecord -v speed=4 dev=0,0,0 /RH80/mydist.isoThe speed and dev options depend on your system. The device for the dev argument can be determined by using the -scanbus option to cdrecord:
cdrecord -scanbus
Once the image is burned onto the CD, insert the CD into the target machine and boot the machine. You should get the custom message that you created earlier. At this point, you can either press Enter at the boot prompt or let it timeout. When it times out it uses the default label, which we specified as ks (Kickstart).
If we did everything right, the installation should proceed without user interaction. In my experience, the installation takes approximately ten minutes. This may differ depending on your exact configuration.
With a combination of Kickstart and a customized Anaconda, a powerful and flexible installation can be created. This installation greatly improved cycle time and reduced errors for my project. I was able to install multiple machines, multiple times almost effortlessly. In this article, I touched on only a few ways to take advantage of Kickstart and Anaconda, but there are many other possibilities. I encourage those interested to read the documentation in the Resources section and to join the Kickstart and Anaconda mailing lists for further information.