HOWTO make NFS-root without kernel compilation

A common practice is to recompile custom kernel when using root over NFS. An NIC driver, IP stack, NFS support and root on NFS support should be compiled into custom kernel. This procedure described at Documentation/filesystems/nfsroot.txt comes with every kernel distribution. [It is in kernel-doc-XXX.rpm for FC/RH].

It is impossible to recompile a run-time kernel supplied by RH/FC project. Recompiling from .src.rpm is not so simple as it can may looks. Every time I have to download closest vanilla kernel from kernel.org, read .config file from current distribution, change desired options and rebuild kernel.

The idea is to customize initrd, that can do the same using original, unmodified kernel

Prepare NFS volume

Create somewhere NFS volume and export it to some IP/hostname. At time of writing, my volume was resides on NetApp. Scheduled snapshots were taken, that solve for me backup problem.

/vol/ovws_nfs/fc10      -sec=sys,ro,rw=ovws,root=ovws
/vol/ovws_nfs/fc11      -sec=sys,ro,rw=ovws,root=ovws
/vol/ovws_nfs/home      -sec=sys,ro,rw=ovws,root=ovws

I'd choose to separate /home for more flexible migration between FC versions

Populate NFS volume. You can do this at least in two ways. Install FC somewhere on HD and copy content to NFS, or mount NFS on existing Linux machine and install directly to NFS using rpm --root=/mnt/nfsvolume options. A practice shows that easiest way is to find spare HD (even external USB), connect it to target workstation, install FC on it and copy content to NFS volume after installation.

Modification to NFS root content

Because of fact that network is already started, some changes required to root content. Modify /etc/fstab record about / as:

/dev/root       /       auto    defaults        0 0

A network should be enabled:

# chkonfig --add network && chkonfig network on

A hostname would setted at very early boot state, so it should not be overwritten by network scripts. Eliminate this truncating /etc/sysconfig/network file:

# echo "NETWORKING=yes" > /etc/sysconfig/network

An interface configuration file (/etc/sysconfig/network-scripts/ifcfg-eth0) may be missing, or should have ONBOOT=no entry in it.

Running mkinitrd

An FC/RH mkinitrd script is not a flexible tool. It is too hard to make it do what you want. The easy way is to change its result by yourself. First of all, lets create new initrd taking maximum that mkinitrd can gave:

# mkinitrd --omit-scsi-modules --omit-raid-modules --omit-lvm-modules --omit-dmraid --preload tg3 --preload nfs \
  /boot/initrd-nfs 2.6.29.4-167.fc11.i686.PAE

Once using NFS as root, other SCSI/LVM/DM modules can be loaded from mounted / file system later. Modules for NFS support and NIC driver should be included in initrd. Use pcnet32 instead of my tg3 when testing on VmWare. As a result, an /boot/initrd-nfs file was created.

Lets open it and prepare for changes:

# mkdir /boot/initrd.open && \
  cd /boot/initrd.open && \
  gzip -dc ../initrd-nfs | cpio -i
11502 blocks

An initrd is open now and can be expected. The most interesting file is init what is nash script. You have to read man for nash to uderstand this script.

Modifying initrd

Lets copy some executables we will need:

# cp -av /bin/{gawk,bash,mount} /sbin/{dhclient,ip,mount.nfs} bin/ && \
  ln -s gawk bin/awk && \
  ln -s bash bin/sh

Create simpliest dhclient-script script:

# cat bin/dhclient-script
#!/bin/bash

case $reason in
"PREINIT") /sbin/ip link set $interface down
        echo "setting link up"
        /sbin/ip link set $interface up
        ;;
"BOUND")
        /sbin/ip addr add $new_ip_address/$new_subnet_mask dev $interface
        /sbin/ip route add default via $new_routers
        ;;
*)      ;;
esac

The next script will mount NFS volume according to bootline:

cat bin/nfsmount
#!/bin/bash

# Get info from kernel command line:
HOSTNAME=$(awk '{for(i=1;i<=NF;i++){ if($i~"hostname="){print $i} }}' /proc/cmdline | awk -F"=" '{print $2}')
NFSROOT=$(awk '{for(i=1;i<=NF;i++){ if($i~"nfsroot="){print $i} }}' /proc/cmdline | awk -F"=" '{print $2}' | awk -F"," '{print $1}')
NFSOPT=$(awk '{for(i=1;i<=NF;i++){ if($i~"nfsroot="){print $i} }}' /proc/cmdline | awk -F"," '{for (i=2;i<=NF;i++){printf "%s,",$i}} END {print "rw"}')

export HOSTNAME NFSROOT NFSOPT
echo $HOSTNAME > /proc/sys/kernel/hostname

# DHCP
/sbin/dhclient -H $HOSTNAME eth0

# mount /sysroot
/bin/mount -t nfs -o $NFSOPT $NFSROOT /sysroot

Make both scripts executable:

# chmod +x bin/{nfsmount,dhclient-script}

Modify init script as follows:

....
# echo Mounting root filesystem.
# mount /sysroot
echo "DHCP query and Mounting root filesystem."
/bin/nfsmount
....

Make bootable media

Pack initrd back using following command:

# find . | cpio -H newc --quiet -o | gzip -9 > ../initrd.ird

I prefer to use syslinux boot loader to make bootable CD, USB stick or network (PXE) boot. Follow to its instruction how to make things work. Copy our initrd.ird and original kernel as kernel.krl. An sys/pxe/isolinux.cfg should include the following lines:

LABEL nfsroot
 KERNEL kernel.krl
 APPEND initrd=initrd.ird nfsroot=IP.ADD.RE.SS:/vol/ovws_nfs/fc11,nolock,rsize=8192,wsize=8192,tcp,nfsvers=3 hostname=ovws
Where nfsroot= option is exactly as it used at classic NFS-root solution. An option hostname= used by DHCP client in initrd to set correct name at early boot time and register itself at DDNS environment. This allow NFS volume to be exported to hostname without fixed IP.



By Oleg Volkov ( Last update on: Thu Oct 15 18:56:37 IST 2009 ) More documentations here