systemd的新时代网络启动
通过systemd 257以后提供的systemd-import-generator组件,我们现在终于可以完全依赖UEFI HTTP Boot协议实现完整的新时代网络启动了。
什么是UEFI HTTP Boot?
UEFI HTTP Boot是一种网络启动技术,它支持通过两种方式:
- 固件配置:在固件的
HTTP Boot Configuration页面直接配置地址。 - DHCP服务器响应:将Option 60配置为
"HTTPClient",然后通过Option 67配置文件URL。
使主机的UEFI固件在引导时从一个指定的HTTP URL下载:
- 一个UEFI PE程序并加载。
- 一个Raw Disk镜像并加载其中的回退引导项
/EFI/BOOT/BOOTX64.EFI。
systemd组很欣赏这一网络启动技术,并试图将其集成到用户空间以实现更加精简的,全链路的网络启动。
这是怎么做到的?
这是通过以下几个技术实现的:
- EDK2的UEFI HTTP Boot:实现从UEFI固件层从HTTP服务器下载并加载EFI PE二进制程序。
systemd-boot:经过更新后,SDBoot现在支持在Type 1引导项中设置uki-url配置项,这一配置项会使SDBoot利用UEFI HTTP Boot的协议栈,从HTTP服务器拉取链式加载的UKI。systemd-import-generator:systemd 257的新组件,其功能是可以根据内核命令行参数的配置,从HTTP服务器拉取各种DDI(Discoverable Disk Image,主要是根文件系统镜像和USR文件系统镜像)镜像。mkosi:用于轻松地构建所有这些镜像(NetESP镜像、根文件系统镜像等等)。
由此可见,藉由这些组件的搭配组合,我们的网络启动链路是一个高度可自定义化的过程,我们可以自由决定我们的启动过程在哪种程度上是“网络化”的。
主要的思考点有:

- 是否使用UEFI HTTP Boot?
a. 是。
b. 否,使用本地ESP。 - UEFI HTTP Boot提供什么?
a. 提供一个只有ESP的磁盘镜像,ESP中包含systemd-boot及其引导项。
b. 提供PE程序,PE程序是UKI。
c. 提供PE程序,PE程序是USI,启动。 - (接2a)systemd-boot做什么?
a. 通过uki-url拉取远程UKI。
b. 通过uki-url拉取USI,启动。
c. 加载(磁盘镜像或本地)的ESP中的UKI。 - (接2b,或接3a,或接3c)UKI中做什么?
a. 通过ip=dhcp rd.systemd.pull=XXX root=XXX从远程拉取根文件系统镜像,启动。
b. 通过ip=dhcp rd.systemd.pull=XXX mount.usr=XXX从远程拉取USR镜像,释放根文件系统,启动。
实践
以标准的1a -> 2a -> 3a -> 4a为例。
构建镜像
你可以从这里快速获取我的镜像模板:MoltenArmor/mkosi-template。
镜像构建配置项的含义可以从这里参考:mkosi/mkosi/resources/man/mkosi.1.md。
NetESP
mkosi.conf:
1 | [Distribution] |
mkosi.extra/efi/loader/loader.conf:
1 | timeout 5 |
mkosi.extra/efi/loader/entries/10-netboot.conf:
1 | title NetBoot |
随后执行构建:
1 | mkosi build |
Rootfs
根文件系统怎样都好,只需要确保构建产品格式为Disk,按照你的个性化构建即可,如果你实在懒得客制化,使用这条命令:
1 | mkosi --include mkosi-vm --distribution debian --format disk build |
UEFI HTTP Boot
如前所述,UEFI HTTP Boot有两种配置方式:
- 在UEFI固件配置中手动配置选择。
- 使用DHCP服务器传递特定报文。
第一种自不必说,在自动化场景中可能显得不够实用,我们主要讨论第二种情况。我们选择systemd-networkd作为DHCP服务器实现,如果是内网唯一的DHCP服务器,那么配置如下:
1 | [Match] |
如果不是内网唯一的DHCP服务器,那么:
1 | [Match] |
systemd-boot
通过HTTP服务器提供的Raw Disk镜像只能包含一个ESP分区(其他分区就算包含也会被忽略),ESP中必须在/EFI/BOOT/BOOTX64.EFI位置安装systemd-boot,并在/loader/entries/目录下配置它的引导项,指定提供UKI的服务器:
1 | title NetBoot |
HTTP服务器可以选择Busybox、Python或任意一个实现。
内核命令行参数与systemd-import-generator
我们需要进行配置,以使得UKI的Initrd中的systemd-importd会自动拉取Rootfs,并将其加载到内存中,用于之后的启动过程。因此内核命令行参数需要配置systemd-importd的行为:
1 | ip=dhcp rd.systemd.pull=raw,machine,verify=no,blockdev:rootdisk:http://XXX/rootfs.raw root=dissect rd.systemd.mask=systemd-repart.service systemd.mask=systemd-repart.service |
ip=dhcp:触发Initrd中的systemd-networkd配置网络。rd.systemd.pull=:触发Initrd中的saystemd-importd拉取镜像。root=dissect:自动解析镜像中的根文件系统并进行挂载。rd.systemd.mask=systemd-repart.service systemd.mask=systemd-repart.service:镜像是不可变的,systemd-repart无力进行重新分区,如果不Mask掉会启动失败。
这样在启动时,systemd-importd就会导入rootfs.raw,并使systemd从rootfs.raw启动。
启动!
接前文,如果希望手动配置UEFI固件,进入EFI固件后,进入Device Manager -> Network Device List,然后选择HTTP Boot Configuration,选择HTTP服务器提供的文件的URL。


接着就可以在EFI Boot Manager找到引导项了。

随后系统启动,会拉取镜像并启动systemd-boot,选择网络启动的引导项,即可启动系统:
