Linux installation can be performed totally unattended using Kickstart file, which contains configuration, required setup and post installation tasks to fully automate installation process without being prompted for any input details. Kickstart file can be placed in the remote repository or can be included in ISO image in order to be read by Anaconda installer during system installation.
In this tutorial we present some practical solutions that can be placed in kickstart file to automate CentOS 7 / Red Hat 7 installation tasks.
At the very beginning let’s take a look at this kickstart file: ks.cfg, which is a complete and working set of commands and functions that let us perform hardware detection, unattended disk partitioning, additional RPMs installation, kernel parameters configuration, etc…
This file can be used as it is, without any modifications and this will do the whole automated installation job, but let’s break it down into pieces to describe particular use cases:
To skip X Window Server installation, place the following in kickstart’s header section:
skipx
To add new user to the installed system, use the following option in kickstart’s header section:
user --name=tuxoper --password=$1$qrBNJr0C$8J4stpPzjQZywX33wnMn7. --iscrypted
Additional RPM packages to install can be placed in %packages section right below @base package:
%packages
@base
policycoreutils-python
libseccomp
PyYAML-3.10-11.el7
python-jinja2
python-pyasn1
nfs-utils
lksctp-tools
pexpect
screen
nmap-ncat
telnet
lftp
lshw
tmux
cairo
numactl
perf
ltrace
dstat
iotop
iptraf-ng
net-snmp-utils
lldpad
%end
If you want to avoid accidental reinstallation of already installed operating system, you can check whether there is any existing partition table on the disk using kpartx or partx:
%pre --erroronfail --log=/tmp/ks-pre.log
part_table=$(kpartx -r /dev/sda)
if [ -n "$part_table" ]; then
whiptail --fb --title "WARNING" --yesno --defaultno "This system contains partition table. Installation will DELETE EXISTING DATA on this system. Continue?" 12 50 > /dev/console
if [ $? -ne 0 ]; then
reboot
fi
fi
The below example shows, how to detect hardware type, verify hard disk capacity and apply different partitioning scheme based on detected hardware type.
For hardware detection we are using here virt-what command, which checks the type of detected hardware (bare metal, Virtualbox, VMware). virt-what, unlike other similar commands (i.e. dmidecode), displays already parsed output, so it’s pretty convenient in such use cases.
Partitioning for bare metal depends also on the detected tux_node_type parameter, which is being read from kernel command line parameters included in GRUB 2 menu entries. For virtual hardware detected (VirtualBox, VMware), auto partitioning is applied. Partitioning instructions for each case are redirected to /tmp/part-include file, which is included in header section of kickstart file:
# === include partitioning scheme generated in pre ===
%include /tmp/part-include
# ====================================================
Kickstart also detects if minimum hard disk capacity requirements are met by comparing hard disk capacity parameter with bm_storage_min_size for bare metal and vm_storage_min_size for virtual hardware detected. capacity parameter is parsed from fdisk command.
For VMware hardware detected, open-vm-tools information is redirected to /tmp/virtual file, which can be included in %packages section to install open-vm-tools RPM package for VMware only:
# === open-vm-tools installation for vmware only ===
echo "open-vm-tools" > /tmp/virtual
The above solution requires placing dummy replacement of /tmp/virtual files for bare metal and VirtualBox to avoid installation failure, since open-vm-tools are not supposed to be installed in these two cases:
# === open-vm-tools dummy replacement for bare metal ===
echo "" > /tmp/virtual
# === open-vm-tools dummy replacement for virtualbox ===
echo "" > /tmp/virtual
The whole hardware detection and hard disk capacity verification code is placed in %pre section of kickstart file:
# === storage minimum capacity settings ===
bm_storage_min_size=400
vm_storage_min_size=16
# =========================================
sleep 20
hw=$(virt-what | head -1)
sleep 10
# === read tux_node type from grub menuentry parameter ===
tux_node_type=$(cat /proc/cmdline | cut -f 3 -d '=' | cut -f 1 -d ' ')
if [ -z "$hw" ]; then
whiptail --infobox --title "HARDWARE DETECTION" "Detected hardware: Bare Metal" 10 40 > /dev/console
sleep 10
capacity=$(fdisk -l | grep /dev/sda | cut -f 3 -d " " | cut -f 1 -d ".")
sleep 1
if [ "$capacity" -lt "$bm_storage_min_size" ]; then
whiptail --infobox --title "HARD DISK REQUIREMENTS" "ERROR: Minimum hard disk capacity for Bare Metal installation is $bm_storage_min_size GB. Will reboot now." 10 50 > /dev/console
sleep 15
reboot --eject
else
# === open-vm-tools dummy replacement for bare metal ===
echo "" > /tmp/virtual
if [ "$tux_node_type" == "manager" ]; then
# =================== generate partitioning scheme for bare metal ==========================
# partitioning intentionally removed to simplify analysis
# ===========================================================================================
elif [ "$tux_node_type" == "collector" ]; then
# =================== generate partitioning scheme for bare metal ==========================
# partitioning intentionally removed to simplify analysis
# ===========================================================================================
fi
fi
elif [ "$hw" == "vmware" ]; then
whiptail --infobox --title "HARDWARE DETECTION" "Detected hardware: VMWare" 10 40 > /dev/console
sleep 10
capacity=$(fdisk -l | grep /dev/sda | cut -f 3 -d " " | cut -f 1 -d ".")
sleep 1
if [ "$capacity" -lt "$vm_storage_min_size" ]; then
whiptail --infobox --title "HARD DISK REQUIREMENTS" "ERROR: Minimum hard disk capacity for VMWare installation is $vm_storage_min_size GB. Will reboot now." 10 50 > /dev/console
sleep 15
reboot
else
# === open-vm-tools installation for vmware only ===
echo "open-vm-tools" > /tmp/virtual
# ============== generate partitioning scheme for vmware ====================================
echo "autopart" > /tmp/part-include
# ===========================================================================================
fi
elif [ "$hw" == "virtualbox" ]; then
whiptail --infobox --title "HARDWARE DETECTION" "Detected hardware: VirtualBox" 10 40 > /dev/console
sleep 10
capacity=$(fdisk -l | grep /dev/sda | cut -f 3 -d " " | cut -f 1 -d ".")
sleep 1
if [ "$capacity" -lt "$vm_storage_min_size" ]; then
whiptail --infobox --title "HARD DISK REQUIREMENTS" "ERROR: Minimum hard disk capacity for VirtualBox installation is $vm_storage_min_size GB. Will reboot now." 10 50 > /dev/console
sleep 15
reboot
else
# === open-vm-tools dummy replacement for virtualbox ===
echo "" > /tmp/virtual
# ============== generate partitioning scheme for virtualbox ================================
echo "autopart" > /tmp/part-include
# ===========================================================================================
fi
else
whiptail --infobox --title "HARDWARE DETECTION" "WARNING: This Operating System is not released for recognized hardware: $hw. Will reboot now." 10 40 > /dev/console
sleep 10
reboot
fi
The below partitioning layout is designed for UEFI boot based servers, since it contains EFI / UEFI system partition. EFI / UEFI partition should be VFAT type partition with the minimum capacity of 200 MB.
The next one is a primary /boot partition with the capacity of 512 MB, this partition can’t be included in LVM volume groups.
Next the physical volumes pv.00 and pv.01 are created to provide physical extents for accordingly vg_root and vg_app volume groups. Capacity of pv.00 is set to fixed size: 150 GB, pv.01 from the other hand starts from 1 MB, but the grow parameter makes it fill up the whole remaining disk space.
Volume groups: vg_root and vg_app are created accordingly on pv.00 and pv.01 physical volumes.
Logical volumes (swap, /var, /home, etc…) are created on both volume groups with fixed sizes and file system options. Additionally /var mount point capacity is set to 1 MB and has parameter grow assigned, that means, /var directory will fill up the whole remaining volume group space.
Each line of the below block is placed in /tmp/part-include file which is being read in kickstart file header:
# === include partitioning scheme generated in pre ===
%include /tmp/part-include
# ====================================================
If you have many partitions in your configuration (like the below), consider also putting everything in HERE or EOF block of code.
Here is our example partitioning scheme:
# =================== generate partitioning scheme for bare metal ==========================
echo "part /boot/efi --size=200 --ondisk=sda --fstype=vfat --label=EFIBOOT" > /tmp/part-include
echo "part /boot --size=512 --ondisk=sda --asprimary --fstype=ext4 --label=boot --fsoptions=acl,user_xattr,errors=remount-ro,nodev,noexec,nosuid" >> /tmp/part-include
echo "part pv.00 --size=150000 --asprimary --ondisk=sda" >> /tmp/part-include
echo "part pv.01 --size=1 --grow --asprimary --ondisk=sda" >> /tmp/part-include
echo "volgroup vg_root pv.00" >> /tmp/part-include
echo "volgroup vg_app pv.01" >> /tmp/part-include
echo "logvol swap --name=swap --vgname=vg_root --size=32000" >> /tmp/part-include
echo "logvol / --fstype=ext4 --fsoptions=acl,user_xattr,errors=remount-ro --size=30000 --name=root --vgname=vg_root" >> /tmp/part-include
echo "logvol /var --fstype=ext4 --fsoptions=acl,user_xattr,errors=remount-ro --size=1 --grow --vgname=vg_root" >> /tmp/part-include
echo "logvol /var/log --fstype=ext4 --fsoptions=acl,user_xattr,errors=remount-ro --size=20000 --vgname=vg_root" >> /tmp/part-include
echo "logvol /var/log/audit --fstype=ext4 --fsoptions=acl,user_xattr,errors=remount-ro --size=20000 --vgname=vg_root" >> /tmp/part-include
echo "logvol /var/crash --fstype=ext4 --fsoptions=acl,user_xattr,errors=remount-ro --size=24000 --vgname=vg_root" >> /tmp/part-include
echo "logvol /home --fstype=ext4 --fsoptions=acl,user_xattr,errors=remount-ro --size=2000 --vgname=vg_root" >> /tmp/part-include
echo "logvol /var/docker-volumes --fstype=ext4 --fsoptions=acl,user_xattr,errors=remount-ro --size=100000 --vgname=vg_app" >> /tmp/part-include
echo "logvol /var/esdata --fstype=ext4 --fsoptions=acl,user_xattr,errors=remount-ro --size=50000 --vgname=vg_app" >> /tmp/part-include
echo "logvol /var/lib/docker --fstype=ext4 --fsoptions=acl,user_xattr,errors=remount-ro --size=16000 --vgname=vg_app" >> /tmp/part-include
# ===========================================================================================
If you need to install extra packages not included in standard RHEL 7 / CentOS 7 ISO image, you can place them in the custom directory on ISO image, i.e.: mounted_iso_main_dir/soft/… and invoke their installation in %post nochroot section of kickstart file.
Note: this part of package installation must be executed in nochroot environment, since we need to have access to installation media.
cd /run/install/repo/soft/docker
rpm -ivh --root /mnt/sysimage *.rpm
cp docker-compose /mnt/sysimage/usr/local/bin/
chmod 755 /mnt/sysimage/usr/local/bin/docker-compose
cd /run/install/repo/soft/ansible
rpm -ivh --root /mnt/sysimage *.rpm
cd /run/install/repo/soft/utils
rpm -ivh --root /mnt/sysimage python-GnuPGInterface*.rpm
rpm -ivh --root /mnt/sysimage python-lockfile*.rpm
rpm -ivh --root /mnt/sysimage ncftp*.rpm
rpm -ivh --root /mnt/sysimage python2-pyasn1*.rpm
rpm -ivh --root /mnt/sysimage python2-rsa*.rpm
rpm -ivh --root /mnt/sysimage python2-boto*.rpm
rpm -ivh --root /mnt/sysimage ngrep*.rpm
rpm -ivh --root /mnt/sysimage librsync*.rpm
rpm -ivh --root /mnt/sysimage duplicity*.rpm
rpm -ivh --root /mnt/sysimage duply*.rpm
cd /run/install/repo/soft/performance
rpm -ivh --root /mnt/sysimage *.rpm
Services can be easily managed in kickstart file to be executed right after installation by placing them in %post chroot section, since all the mentioned commands are executed in newly installed system (outside installation media):
# === enable docker service ===
systemctl enable docker.service
# === disable RHN unit services
systemctl disable rhnsd.service rhsmcertd.service
# === disable postfix unit service ===
systemctl disable postfix.service
Regular user can be added to the wheel group for elevated administrative privileges and become a sudoer by redirecting apropriate strings to /etc/sudoers file:
# === add tuxoper to sudoers ===
echo "# Allow tuxoper to run any commands anywhere" >> /etc/sudoers
echo "tuxoper ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
To generate SSH keys via kickstart file for a user during system installation, execute SSH key creation command on behalf of the destination user. The below command presents SSH key creation without password for tuxoper user:
# === set ssh keys for tuxoper ===
su - tuxoper -c "ssh-keygen -t rsa -f /home/tuxoper/.ssh/id_rsa -N ''"
Kernel runtime parameters can be configured in kickstart file to be enabled right after installation by placing particular parameter lines in /etc/sysctl.conf file:
echo "kernel.exec-shield = 1" >> /etc/sysctl.conf
echo "kernel.randomize_va_space = 2" >> /etc/sysctl.conf
echo "net.ipv4.ip_forward = 0" >> /etc/sysctl.conf
echo "net.ipv4.conf.all.send_redirects = 0" >> /etc/sysctl.conf
echo "net.ipv4.conf.default.send_redirects = 0" >> /etc/sysctl.conf
Nice article and it helped me lot, One observation “–erroronfail” not working for pre section
%pre –erroronfail –log=/tmp/ks-pre.log
it gives error
ImportError: No module named updates_disk_hook
As soon as i removed it it start working
Below works fine
%pre –log=/tmp/ks-pre.log
Hi Imran
Thanx for remark, it has been a while since I wrote this article, so maybe something has changed in the distro since then, anyway I will take a look if I have a moment.
how you downloaded docker rpms? i did steps as mentioned in below URL but docker package installation is not successful.
https://unix.stackexchange.com/questions/516605/install-docker-package-on-centos-with-kickstart-and-offline-rpms
I downloaded them on some working CentOS7/RHEL7 system using yum with option downloadonly, then placed the rpms on the iso in ../soft/ directory, during kickstart installation I install all the packages from ../soft/.. subdirectoties using wildcard *.rpm
What is the disk size of the target machine? –size=150000 is ambiguous. Also, how do you get away with not specifying physical extents? I seem to need to use –pesize for the volgroup.
We often need to “yum update” after installation so all security and other packages get update, How we can do through kickstart (with condition that we are in disconnected environment)? Can we do offline installation of yum update packages and install with kickstart?
My ks.cfg file is not loading with custom iso installed!
append initrd=initrd.img inst.stage2=hd:LABEL=CentOS\x207\x20x86_64 inst.ks=hd:/ks.cfg quiet