如何构建 Netboot 服务器,第 3 部分

如何构建 Netboot 服务器,第 1 部分文章提供了一个最小的 iPXE 您的网络启动映像的启动脚本。 除了 netboot 映像之外,许多用户可能还有一个他们想要使用的本地操作系统。 但是使用典型工作站的 BIOS 切换引导加载程序可能很麻烦。 本系列的这一部分展示了如何设置一些更复杂的 iPXE 配置。 这些允许最终用户轻松选择他们想要启动的操作系统。 它们还允许系统管理员从中央服务器管理引导菜单。

交互式 iPXE 启动菜单

以下命令将 netboot 映像的 boot.cfg 重新定义为具有 5 秒倒计时的交互式 iPXE 启动菜单:

$ MY_FVER=29
$ MY_KRNL=$(ls -c /fc$MY_FVER/lib/modules | head -n 1)
$ MY_DNS1=192.0.2.91
$ MY_DNS2=192.0.2.92
$ MY_NAME=server-01.example.edu
$ MY_EMAN=$(echo $MY_NAME | tr '.' "n" | tac | tr "n" '.' | cut -b -${#MY_NAME})
$ MY_ADDR=$(host -t A $MY_NAME | awk '{print $4}')
$ cat << END > $HOME/esp/linux/boot.cfg
#!ipxe

set timeout 5000

:menu
menu iPXE Boot Menu
item --key 1 lcl 1. Microsoft Windows 10
item --key 2 f$MY_FVER 2. RedHat Fedora $MY_FVER
choose --timeout ${timeout} --default lcl selected || goto shell
set timeout 0
goto ${selected}

:failed
echo boot failed, dropping to shell...
goto shell

:shell
echo type 'exit' to get the back to the menu
set timeout 0
shell
goto menu

:lcl
exit

:f$MY_FVER
kernel --name kernel.efi ${prefix}/vmlinuz-$MY_KRNL initrd=initrd.img ro ip=dhcp rd.peerdns=0 nameserver=$MY_DNS1 nameserver=$MY_DNS2 root=/dev/disk/by-path/ip-$MY_ADDR:3260-iscsi-iqn.$MY_EMAN:fc$MY_FVER-lun-1 netroot=iscsi:$MY_ADDR::::iqn.$MY_EMAN:fc$MY_FVER console=tty0 console=ttyS0,115200n8 audit=0 selinux=0 quiet
initrd --name initrd.img ${prefix}/initramfs-$MY_KRNL.img
boot || goto failed
END

上面的菜单有五个部分:

  • 菜单 定义将在屏幕上显示的实际菜单。
  • 失败的 通知用户出现问题并将用户放到 shell 中,以便他们解决问题。
  • 贝壳 提供交互式命令提示符。 您可以通过按 Esc键 键,或者如果“boot”命令返回失败代码。
  • 拼箱 包含一个命令,告诉 iPXE 退出并将控制权返回给 BIOS。 默认情况下要启动的任何内容(例如工作站的本地硬盘) 必须 在工作站的 BIOS 中被列为 iPXE 之后的下一个引导项。
  • f29 包含之前使用的相同网络引导代码,但最终退出替换为 goto failed。

将更新后的 boot.cfg 从 $HOME/esp/linux 目录复制到所有客户端系统的 ESP。 如果一切顺利,您应该会看到类似于下图的结果:

服务器托管的启动菜单

您可以添加到网络引导服务器的另一项功能是能够从一个中央位置管理所有客户端引导菜单。 在推出新版本的操作系统时,此功能特别有用。 它可以让你执行一种 原子事务 在将新内核和 initramfs 复制到所有客户端的 ESP 后,将所有客户端切换到新操作系统。

安装 Mojolicious:

$ sudo -i
# dnf install -y perl-Mojolicious

定义“bootmenu”应用程序:

# mkdir /opt/bootmenu
# cat << END > /opt/bootmenu/bootmenu.pl
#!/usr/bin/env perl
use Mojolicious::Lite;
use Mojolicious::Plugins;

plugin 'Config';

get '/menu';

app->start;
END
# chmod 755 /opt/bootmenu/bootmenu.pl

定义引导菜单应用程序的配置文件:

# cat << END > /opt/bootmenu/bootmenu.conf
{
  hypnotoad => {
    listen => ['https://*:80'],
    pid_file => '/run/bootmenu/bootmenu.pid',
  }
}
END

这是一个非常简单的 Mojolicious 应用程序,它侦听端口 80 并且只响应 /menu 请求。 如果您想快速了解 Mojolicious 的功能,请运行 man Mojolicious::Guides::Growing 以查看手册。 使用 键退出手册。

将 boot.cfg 作为名为 menu.html.ep 的模板移至我们的 netboot 应用程序:

# mkdir /opt/bootmenu/templates
# mv $HOME/esp/linux/boot.cfg /opt/bootmenu/templates/menu.html.ep

定义一个 systemd 服务来管理 bootmenu 应用程序:

# cat << END > /etc/systemd/system/bootmenu.service
[Unit]
Description=Serves iPXE Menus over HTTP
After=network-online.target

[Service]
Type=forking
DynamicUser=true
RuntimeDirectory=bootmenu
PIDFile=/run/bootmenu/bootmenu.pid
ExecStart=/usr/bin/hypnotoad /opt/bootmenu/bootmenu.pl
ExecReload=/usr/bin/hypnotoad /opt/bootmenu/bootmenu.pl
AmbientCapabilities=CAP_NET_BIND_SERVICE
KillMode=process

[Install]
WantedBy=multi-user.target
END

将 HTTP 服务的例外添加到本地防火墙并启动 bootmenu 服务:

# firewall-cmd --add-service http
# firewall-cmd --runtime-to-permanent
# systemctl enable bootmenu.service
# systemctl start bootmenu.service

用 wget 测试它:

$ sudo dnf install -y wget
$ MY_BOOTMENU_SERVER=server-01.example.edu
$ wget -q -O - https://$MY_BOOTMENU_SERVER/menu

上述命令应输出类似于以下内容:

#!ipxe

set timeout 5000

:menu
menu iPXE Boot Menu
item --key 1 lcl 1. Microsoft Windows 10
item --key 2 f29 2. RedHat Fedora 29
choose --timeout ${timeout} --default lcl selected || goto shell
set timeout 0
goto ${selected}

:failed
echo boot failed, dropping to shell...
goto shell

:shell
echo type 'exit' to get the back to the menu
set timeout 0
shell
goto menu

:lcl
exit

:f29
kernel --name kernel.efi ${prefix}/vmlinuz-4.19.4-300.fc29.x86_64 initrd=initrd.img ro ip=dhcp rd.peerdns=0 nameserver=192.0.2.91 nameserver=192.0.2.92 root=/dev/disk/by-path/ip-192.0.2.158:3260-iscsi-iqn.edu.example.server-01:fc29-lun-1 netroot=iscsi:192.0.2.158::::iqn.edu.example.server-01:fc29 console=tty0 console=ttyS0,115200n8 audit=0 selinux=0 quiet
initrd --name initrd.img ${prefix}/initramfs-4.19.4-300.fc29.x86_64.img
boot || goto failed

现在启动菜单服务器正在工作,使用指向它的初始化脚本重建 ipxe.efi 引导加载程序。

首先,更新在本系列的第一部分中创建的 init.ipxe 脚本:

$ MY_BOOTMENU_SERVER=server-01.example.edu
$ cat << END > $HOME/ipxe/init.ipxe
#!ipxe

dhcp || exit
set prefix file:///linux
chain https://$MY_BOOTMENU_SERVER/menu || exit
END

现在,重建引导加载程序:

$ cd $HOME/ipxe/src
$ make clean
$ make bin-x86_64-efi/ipxe.efi EMBED=../init.ipxe

将更新的引导加载程序复制到您的 ESP:

$ cp $HOME/ipxe/src/bin-x86_64-efi/ipxe.efi $HOME/esp/efi/boot/bootx64.efi

将更新后的引导加载程序复制到所有客户端后,您只需编辑 /opt/bootmenu/templates/menu.html.ep 并运行即可对引导菜单进行未来更新:

$ sudo systemctl restart bootmenu.service

做出进一步的改变

如果引导菜单服务器工作正常,您将不再需要客户端系统上的 boot.cfg 文件。

为了 example重新添加 Fedora 28 图像到启动菜单:

$ sudo -i
# MY_FVER=28
# MY_KRNL=$(ls -c /fc$MY_FVER/lib/modules | head -n 1)
# MY_DNS1=192.0.2.91
# MY_DNS2=192.0.2.92
# MY_NAME=$(</etc/hostname)
# MY_EMAN=$(echo $MY_NAME | tr '.' "n" | tac | tr "n" '.' | cut -b -${#MY_NAME})
# MY_ADDR=$(host -t A $MY_NAME | awk '{print $4}')
# cat << END >> /opt/bootmenu/templates/menu.html.ep

:f$MY_FVER
kernel --name kernel.efi ${prefix}/vmlinuz-$MY_KRNL initrd=initrd.img ro ip=dhcp rd.peerdns=0 nameserver=$MY_DNS1 nameserver=$MY_DNS2 root=/dev/disk/by-path/ip-$MY_ADDR:3260-iscsi-iqn.$MY_EMAN:fc$MY_FVER-lun-1 netroot=iscsi:$MY_ADDR::::iqn.$MY_EMAN:fc$MY_FVER console=tty0 console=ttyS0,115200n8 audit=0 selinux=0 quiet
initrd --name initrd.img ${prefix}/initramfs-$MY_KRNL.img
boot || goto failed
END
# sed -i "/item --key 2/a item --key 3 f$MY_FVER 3. RedHat Fedora $MY_FVER" /opt/bootmenu/templates/menu.html.ep
# systemctl restart bootmenu.service

如果一切顺利,您的客户端在下次启动时应该会看到类似于下图的结果: