服务器网络启动方式探索Part2:UEFI启动篇

上一篇服务器网络启动方式探索Part1:Legacy启动篇里,总结了一些在Legacy启动模式下的一些网络启动方案,那么这一篇,很自然的就需要介绍一下在纯UEFI模式下的网络启动了。

相比Legacy启动直接读取MBR启动分区的第一个扇区作为引导的逻辑,UEFI启动变得强大了很多,在UEFI模式下,固件直接具有的读取FAT文件系统的能力,并且直接通过运行EFI可执行文件的方式进行引导。
因为这个显而易见的变化,导致对应到PXE相关的实现上,也会有相应的区别。不过相比于Legacy启动的那些方案,区别不是那么大,依然是可以做到功能上一一对应的,同样的,我们从最简单的情况开始看起。

UEFI Boot + PXE

这个方案依然是配置最简单的方案,和Legacy+PXE一样,也是需要ftp server和DHCP,只是DHCP服务器的配置有些不一样:

option domain-name-servers 10.1.1.1;
option routers 10.1.1.1;
default-lease-time 14400;
ddns-update-style none;
subnet 10.1.1.0 netmask 255.255.255.0 {
    range dynamic-bootp 10.1.1.100 10.1.1.120;
    default-lease-time 14400;
    max-lease-time 172800;
    next-server 10.1.1.10;   #关键配置!用于指明tftp server的地址
    filename "grubx64.efi";   #关键配置!用于指明bootloader的名字
}

可以发现其他的都没变化,只是把filename的配置换成了grubx64.efi,这意味着在UEFI启动里,放弃了使用pxelinux,转而使用了grub,当然其实SYSLINX也是有efi的,文件名syslinux.efi,但是因为确实用的比较少,所以在这个环境里就换成了grub作为Bootloader。

接下来是grubx64.efi和配置文件的准备,以rhel举例,如果本身机器就是用的UEFI启动,那直接可以从/boot/efi/EFI/redhat/grubx64.efi拷贝,如果系统本身还是MBR安装,那可以可以参考红帽的文档

然后是配置文件,和PXELINUX一样,grub也是支持根据客户端的不同加载不同的配置文件的。具体的可以参考一下grub的文档:8 Booting GRUB from the network
不过我们也不需要多配置文件了,只需要配置一个grub.cfg就行,把这个配置文件放在和grubx64.efi同目录下:

set timeout=5
menuentry 'RHEL' {
  linuxefi images/vmlinuz
  initrdefi images/initrd.img
}

至此一个最简单的UEFI的PXE启动方式就配置完成了。

UEFI Boot + iPXE

接下来就是iPXE了,相比于PXE来说,iPXE的变化要小的多,因为自带了bootloader,所以只需要把filename换成ipxe.efi就行了:

option domain-name-servers 10.1.1.1;
option routers 10.1.1.1;
default-lease-time 14400;
ddns-update-style none;
subnet 10.1.1.0 netmask 255.255.255.0 {
    range dynamic-bootp 10.1.1.100 10.1.1.120;
    default-lease-time 14400;
    max-lease-time 172800;
    next-server 10.1.1.10;   #关键配置!用于指明tftp server的地址
    if exists user-class and option user-class = "iPXE" { #根据user-class字段来判断客户端类型
      filename "http://10.1.1.10/boot.script";
    } else {
      filename "ipxe.efi";
    }
}

同样的,ipxe.efi可以从http://boot.ipxe.org直接下载。剩下的关于boot.script配置,和上一篇Legacy里的配置是一致的。这里就不用赘述了。

UEFI HTTP Boot

对于iPXE来说,支持通过HTTP/FTP等等基于TCP传输的协议来获取kernel和initrd文件,速度相比于之前的PXE的tftp要快很多倍,但是如果稍微有那么一点点强迫症的话,依然会觉得整个iPXE方案里始终存在一个不太和谐的点。
是的,虽然kernel和initrd等文件可以通过HTTP获取,但是对于iPXE本身,无论是Legacy模式还是UEFI模式下,都依然需要用到tftp,虽然iPXE文件本身很小,只有KB级别,不会影响启动的速度了,但是tftp始终是一个依赖,这对于一个想Keep it Simple、Stupid的启动方式来说,实在是有那么些不舒服,如果能有什么办法(当然,直接把网卡刷成iPXE的方案不算)解决掉tftp的依赖,那显然是极好的。

对于这个问题呢,设计固件的聪明人们自然也想到了,于是就在UEFI 2.5的SPEC里,加上了HTTP Boot的功能,直接让UEFI可以从HTTP URL获取启动文件,并通过这个启动文件启动系统,对于这个方案,目前已知的情况是,Intel提供的UEFI标准实现edk2是支持的,文档可以参考Getting Started with UEFI HTTPS Boot on EDK II ,除此之外,Dell联想HPE等等国际大厂以及虚拟机也是明确有相关文档支持的,,而对于国内的一些厂商,目前我知道的情况是大部分都支持,不过因为确实国内用的比较少,很多厂商并没有非常仔细的测试。

我找了一台Dell的服务器,目前测试是没问题的,对于HTTP Boot来说,依然是通过DHCP获取启动配置,和iPXE类似,会带上一个特殊的标记,来区分普通PXE和HTTP Boot的区别,在iPXE文档UEFI HTTP chainloading里,也明确表示了iPXE支持这种启动方式。下面是个可以参考的DHCP服务器的配置:

option domain-name-servers 10.1.1.1;
option routers 10.1.1.1;
default-lease-time 14400;
ddns-update-style none;
subnet 10.1.1.0 netmask 255.255.255.0 {
    range dynamic-bootp 10.1.1.100 10.1.1.120;
    default-lease-time 14400;
    max-lease-time 172800;
    next-server 10.1.1.10;
    if exists user-class and option user-class = "iPXE" { #根据user-class字段来判断客户端类型
      filename "http://10.1.1.10/boot.script";
    } elsif substring (option vendor-class-identifier, 0, 10) = "HTTPClient" { # UEFI HTTP BOOT
      filename "http://10.1.1.10/ipxe.efi";
    } else {
      filename "ipxe.efi";
    }
}

通过判断vendor-class-identifier字段是否是HTTPClient,来确认是否是UEFI HTTP BOOT,如果是HTTP Boot,那就把iPXE启动文件的URL返回,这样UEFI固件就可以通过HTTP协议获取iPXE启动文件,接下来的过程就和iPXE没有区别了。如此一来,如果UFEI支持HTTP Boot并开启,那么就完全不需要tftp,只需要一个HTTP服务器就可以完成所有的启动过程了。真正意义上去除了对tftp的依赖。
当然,如果想基于grub或者其他bootloader启动的话,也是可以的,基本原理也差不多,感兴趣的话,可以试试。

Secure boot

对于UEFI来说,还有一个非常重要的特性:Secure boot(安全启动),开启Secure boot之后,UEFI会利用数字签名来确认EFI驱动程序或者应用程序是否是受信任的,这其中就包括了从网络下载的bootloader,一般来说,大多数常见的OS发行版都会对Secure boot进行支持,具体支持的原理,这里就不作过多的介绍了,具体的可以参考一下红帽的文档:What is UEFI Secure Boot and how it works?
需要说明的事,这篇Blog以及上一篇Blog所讨论的方法,都不涉及Secure boot,或者说这些配置都无法在Secure boot开启的情况下正常工作。但是也需要稍微进行一些修改,就可以顺利的在Secure boot环境下启动。大致的思路就是把bootloader换成一个受信任的shim,具体的实践,还需要大家自己去测试。

真正的总结

最近确实花了很多时间去研究服务器的网络启动方案,从PXE开始,到HTTP Boot结束,几乎把所有相关的可能性都测试了一遍,想找到一个依赖少、稳定、兼容性好的网络启动方案。然而实际情况是,面对的OEM厂商实在是太多了,每个厂商开发固件时的侧重点也不尽相同,导致在我个人内心中最完美的HTTP Boot方案,几乎没办法真正意义上在线上环境跑通。相对的,反而是最原始功能最少的方案,跨厂商的兼容性最好。

其实一开始的目的不仅于此,如果要深究的话,其实在UEFI这种模式下,甚至有可能去除掉DHCP服务器这个依赖,理论上在UEFI模式下,固件是有自己的一套协议栈的,在PXE之前,是可以手动给网卡配置静态的IP地址,网关,DNS等等网络配置,同样的bootloader的位置也是可以静态指定的,除此之外,还有很多tricky的手段,不过这些方案总归兼容性堪忧,特别是国内的很多服务器厂商,大多对于这些高级功能缺乏测试。感兴趣的同学可以花点时间时间研究研究,还是挺有意思的😄。同样也是希望国内厂商能给力起来,像国际大厂们看齐,不断完善好固件等等各个方面。