PXE & Kickstart

I’ll need a repeatable VM build process for an upcoming project so I’ve setup a PXE boot environment and Kickstart scripts.    I’m using my AD domain controllers, FreeNAS filer,  and Citrix XenServer host.  The goal is automate building CentOS 7 VMs.

DHCP Policy Condition

DHCP comes first.  I’ve added a policy to the DHCP scope where the VMs will be connected.  The policy has a single condition; Vendor Class, Equals, PXEClient*.  It adds Option 066 Boot Server Host Name set to tftp.dugas.lan and Option 067 Bootfile Name set to pxelinux.0.  The hostname is a CNAME alias for my FreeNAS machine.

The NAS comes next.  I’ve enabled the TFTP server and set it up to serve up a new dataset; /mnt/tank/tftpboot.  I’ve also shared the dataset read-only via NFS.  I copied /usr/share/syslinx/pxelinux.0 and /usr/share/syslinux/menu.c32 into that folder from an existing machine with the syslinx package installed.  I then created a pxelinux.cfg/ subdirectory and a plain text file named default in there containing the lines below.  When a PXE machine boots, we’ll get a menu offering the boot from the local drive by default or install CentOS 7.

DEFAULT menu.c32

MENU TITLE Network Boot Menu
 MENU AUTOBOOT Booting from local drive in # seconds

 MENU LABEL ^0: Boot from local drive

  MENU LABEL ^1: Install CentOS 7
  KERNEL centos7/vmlinuz
  APPEND initrd=centos7/initrd.img text ks=nfs:nas.dugas.lan:/mnt/tank/tftpboot/centos7/centos7.ks

Note that the APPEND line may wrap here in your browser but it should be all one line in that file.

Next we setup the centos7/ directory referenced by the KERNEL and APPEND lines in the boot menu.  We’ll use a sync script to pull the majority of the directory’s content from CentOS mirrors.   I keep script there in the directory so I can re-run it to pull down updates later.  The /mnt/tank/tftpboot/centos7/sync script is below.  I’m using the mirror at GaTech here in town – adjust as you need.  Make it executable, run it, and go get some coffee while it pulls down the installation packages then copies the kernel and boot image files.

rsync -avH $MIRROR/os $LOCALDIR
cp -f $LOCALDIR/os/x86_64/isolinux/vmlinuz $LOCALDIR
cp -f $LOCALDIR/os/x86_64/isolinux/initrd.img $LOCALDIR

Next step is the Kickstart script – /mnt/tank/tftpboot/centos7/centos7.ks.  This is another plain text file – mine is below.  Hit Redhat’s Kickstart documentation online for details on what’s possible here.  There are a myriad of options.  A couple items that took me a bit to figure out are the lines that add the EPEL and IUS repositories and some additional packages.  These are for installing a newer version of PHP than what ships with CentOS7.  The line in that same %post block that runs yum update is also pretty handy.  The last %post block is installing the XE tools packages.

# Install, not upgrading
logging --level=info

# System Auth
auth --enableshadow --passalgo=sha512
rootpw --iscrypted encrypted-password-goes-here

# Install source
nfs --server=nas.dugas.lan --dir=/mnt/tank/tftpboot/centos7/os/x86_64/

# Network
network --bootproto=dhcp --ipv6=auto
firewall --enabled --ssh --http

# Misc...
irstboot --disabled
lang en_US.UTF-8
keyboard --vckeymap=us --xlayouts='us'
timezone America/New_York --isUtc --ntpservers=ntp
selinux --enforcing

# Partitioning
bootloader --append=" crashkernel=auto" --location=mbr --boot-drive=xvda 
clearpart --all --drives=xvda --initlabel
part /boot --size=500
part swap --recommended
part pv.01 --size=1 --grow
volgroup vg01 pv.01
logvol / --vgname=vg01 --size=5000 --name=root
logvol /tmp --vgname=vg01 --size=1000 --name=tmp
logvol /var --vgname=vg01 --size=2000 --name=var
logvol /var/log --vgname=vg01 --size=2000 --name=varlog
logvol /home --vgname=vg01 --size=1 --grow --name=home

# Packages
%packages --excludedocs

# Services
services --enabled httpd

# Enable crash dumps
%addon com_redhat_kdump --enable --reserve-mb='auto'

# Install Repos & Updates
%post --log=/root/ks-post-yum.log
echo "Adding EPEL repository."
yum -y install epel-release
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7
echo "Adding IUS repository."
rpm -U https://centos7.iuscommunity.org/ius-release.rpm
rpm --import /etc/pki/rpm-gpg/IUS-COMMUNITY-GPG-KEY
echo "Installing updates."
yum -y update
echo "Installing PHP."
yum -y install php56u php56u-ldap php56u-mcrypt php56u-mbstring php56u-soap php56u-opcache

# Install Citrix XenServer VM Tools (if CD is there)
%post --log=/root/ks-post-vmtools.log
mkdir -p /mnt/cdrom
if mount -t iso9660 -o ro /dev/cdrom /mnt/cdrom; then
   echo "Installing VM Tools."
   /mnt/cdrom/Linux/install.sh -n
   umount /mnt/cdrom
   eject /dev/cdrom
   echo "Failed to mount CDROM. VM Tools not installed!"
mdir /mnt/cdrom

# Done

The last step is to create the VM host on the XenServer machine.  We use the CentOS 7 template and select “Boot from network“.  The rest of the options are typical of manually created VMs.  Make sure to set the size of the virtual disk to work with the partitioning you’ve specified.  Boot the VM and switch to the VM’s console in XenCenter to choose the “Install CentOS 7” option in the boot menu.  Hit enter and standby.  Your VM should be ready in 5-10 minutes.

By default, VMs built using the CentOS 7 template on XenServer boot first from the network, their hard disk, or a CDROM in that order.  Once it’s built, I recommend you disable the network-boot option so the VM starts faster and isn’t wiped and reinstalled by accident.

One other useful tidbit…  If you press TAB in the PXE boot menu, you can edit the boot parameters.  You can change the name of the kickstart file if you have a custom one.  Add ip=... to override the default network settings.  Use ip=::::hostname::dhcp  if you just want to override the hostname and leave it using DHCP.  Lots of options here.  See Redhat’s docs for details.