Our predominant exposure to GRUB has been as the common boot loader used with Linux, installed on the Master Boot Record (MBR) of the hard drive. This, however, is not the only way we can utilize GRUB.
This document will explore setting up GRUB2 (1.99) to be used via DHCP and PXE for use in network-based operation.
I discovered that information on successfully setting up GRUB with PXE was horrendously scant, lacking, and even unclear due to lack of differentiation between GRUB1 and GRUB2 details.
After far too much time scouring the internets and getting stuff figured out, I figured the world needed at least one document that can plainly walk someone through the process from start to finish, functionally lowering the bar of entry to PXE GRUB utilization.
Because GRUB is the common boot loader used on Linux systems, and since we'll be compiling a fresh copy, I wanted to avoid screwing up the system-installed GRUB.. so I deployed a virtual machine to be host to the compiling, installing, and netdir creation.. this way, if anything went wrong, all I'd be out is a virtual machine (that can be easily discarded and recreated) versus potentially hosing a production server.
Even though it seems that these instructions and resulting operations do not interfere with the system installed grub, I figure it is better to be safe than sorry.
The first order of business, I realized, was the need to compile GRUB from source, due to the apparent omission of the grub-mknetdir script and who knows what else (note that in retrospect, this whole thing can probably be done with the default GRUB deployment on a Linux system).
First off, I grabbed the source:
machine:~$ wget ftp://ftp.gnu.org/gnu/grub/grub-1.99.tar.gz --2011-08-11 10:23:04-- ftp://ftp.gnu.org/gnu/grub/grub-1.99.tar.gz => "grub-1.99.tar.gz" Resolving ftp.gnu.org... 140.186.70.20 Connecting to ftp.gnu.org|140.186.70.20|:21... connected. Logging in as anonymous ... Logged in! ==> SYST ... done. ==> PWD ... done. ==> TYPE I ... done. ==> CWD (1) /gnu/grub ... done. ==> SIZE grub-1.99.tar.gz ... 4652619 ==> PASV ... done. ==> RETR grub-1.99.tar.gz ... done. Length: 4652619 (4.4M) (unauthoritative) 100%[================================================>] 4,652,619 477K/s in 10s 2011-08-11 10:23:15 (445 KB/s) - "grub-1.99.tar.gz" saved [4652619] machine:~$
Extract the source:
machine:~$ tar -zxvf grub-1.99.tar.gz grub-1.99/ grub-1.99/ABOUT-NLS grub-1.99/AUTHORS grub-1.99/COPYING grub-1.99/INSTALL grub-1.99/README grub-1.99/THANKS grub-1.99/TODO grub-1.99/acinclude.m4 grub-1.99/build-aux/ grub-1.99/build-aux/arg-nonnull.h grub-1.99/build-aux/c++defs.h ... grub-1.99/stamp-h.in grub-1.99/autom4te.cache/ grub-1.99/autom4te.cache/requests grub-1.99/autom4te.cache/traces.0 grub-1.99/autom4te.cache/output.0 grub-1.99/autom4te.cache/traces.1 grub-1.99/autom4te.cache/output.1 grub-1.99/config-util.h.in machine:~$
And change into the newly created grub-1.99 directory:
machine:~$ cd grub-1.99 machine:~/grub-1.99$
We will now proceed to compile a fresh copy of our newly downloaded GRUB source.
The grub-1.99/INSTALL file indicates the need for certain packages/libraries to be installed.
machine:~/grub-1.99$ sudo aptitude install build-essential gawk libfreetype6-dev libfreetype6 libdevmapper libdevmapper-dev make ... machine:~/grub-1.99$
GRUB2 has made improvements where, for what I wanted (PXE GRUB), no changes in options were needed when running configure, so just run it:
machine:~/grub-1.99$ ./configure ... machine:~/grub-1.99$
Assuming all has gone according to plan, we simply type 'make' to commence the build process:
machine:~/grub-1.99$ make ... machine:~/grub-1.99$
Note that there is a make check option that can be invoked, which performs various tests on our GRUB build; when I did this, 20 out of 25 tests failed, but the end result still worked.
Assuming the original make successfully built grub, let us go ahead and install it (it should install into /usr/local, with the bulk of the binaries in /usr/local/bin and /usr/local/sbin):
machine:~/grub-1.99$ sudo make install ... machine:~/grub-1.99$
To avoid complications with any existing GRUB installation, I opted to have it do its magic in a special /pxe directory, so that any confusion over differing versions or files would be minimized.
Using the grub-mknetdir script, do the following:
machine:~$ sudo /usr/local/sbin/grub-mknetdir --net-directory=/pxe Netboot directory for i386-pc created. Configure your DHCP server to point to /boot/grub/i386-pc/core.0 machine:~$
If you look, a /pxe directory has been created and contains goodies within (/pxe/boot/grub/i386-pc)
Because the fileserver acts as the TFTP server for netboots, place the contents of /pxe (that would be /boot and descendants) in the root of the tftpboot directory– in our case, that would be /export/tftpboot.
I accomplished this via tarring it, scp'ing it, and extracting it on the fileserver under /export/tftpboot, so in the end the following path exists: /export/tftpboot/boot/grub/i386-pc
By default, the newly deployed /export/tftpboot/boot/grub/i386-pc directory has a config file called grub.cfg that contains the following:
source /boot/grub/grub.cfg
So, to roll out our config, we'll simply create a /export/tftpboot/boot/grub/grub.cfg file. A sample follows:
menuentry "KNOPPIX 6.7.0 (64-bit)" { loopback loop /distros/knoppix/knoppix.iso linux (loop)/boot/isolinux/linux64 iso-scan/filename=/KNOPPIX/KNOPPIX toram initrd (loop)/boot/isolinux/minirt.gz } menuentry "Ubuntu Live 11.04 64bit" { loopback loop /boot/iso/ubuntu-11.04-desktop-amd64.iso linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=/boot/iso/ubuntu-11.04-desktop-amd64.iso noeject noprompt -- initrd (loop)/casper/initrd.lz } menuentry "Ubuntu Live 9.10 32bit" { loopback loop /boot/iso/ubuntu-9.10-desktop-i386.iso linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=/boot/iso/ubuntu-9.10-desktop-i386.iso noeject noprompt -- initrd (loop)/casper/initrd.lz } menuentry "Ubuntu Live 9.10 64bit" { loopback loop /boot/iso/ubuntu-9.10-desktop-amd64.iso linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=/boot/iso/ubuntu-9.10-desktop-amd64.iso noeject noprompt -- initrd (loop)/casper/initrd.lz }
Obviously one needs to ensure that the referenced files are in the appropriate locations (root relative to /export/tftpboot!!) GRUB is very versatile, and has a lot of functionality… consulting a HOWTO on its configuration and usage would likely be a useful thing to get even more out of this resource.
On the DHCP Server, change the config as follows:
#filename "pxelinux.0"; filename "/boot/grub/i386-pc/core.0";
I basically just commented out the pxelinux.0 and had it instead serve up core.0 (if core.0 was specifically placed in /export/tftpboot, the path would not be needed).
Note that this “absolute” path is rooted in /export/tftpboot.
Restart the DHCP daemon and attempt to netboot! You should get the GRUB menu.
NOTE: This still needs to be verified!
Instead of completely disabling PXELINUX, you could also try adding grub as an option to an existing PXE config, as follows (this was added to /export/tftpboot/boot-screens/menu.cfg on the tftp/file server):
label grub menu label ^GRUB kernel boot/grub/i386-pc/core.0 menu end
This way, no changes will need to be made on the DHCP server, and we are merely adding an additional option to the existing PXELINUX menu. Likely a more preferred approach, especially if running on an existing PXELINUX configuration.
Obviously, make sure if you've made the previous change on the DHCP server, you remember to revert it:
filename "pxelinux.0"; #filename "/boot/grub/i386-pc/core.0";
And restart the DHCP daemon.
The following links were utilized in creating this document: