UEFI引导与RAID1
昨天我花了一些时间构建一台UEFI服务器,该服务器的系统驱动器没有板载硬件RAID。在这种情况下,我总是使用Linux的md RAID1作为根文件系统(和/或/boot)。这在BIOS引导时工作良好,因为BIOS只是盲目地将控制权转移到它看到的任何磁盘的MBR(模数找到“可引导分区”标志等)。这意味着BIOS并不真正关心驱动器上的内容,它会将控制权交给MBR中的GRUB代码。
在UEFI中,引导固件实际上会检查GPT分区表,寻找标记为“EFI系统分区”(ESP)UUID的分区。然后它在那里寻找FAT32文件系统,并执行更多操作,如查看NVRAM引导条目,或直接从FAT32运行BOOT/EFI/BOOTX64.EFI。在Linux下,这个.EFI代码要么是GRUB本身,要么是加载GRUB的Shim。
所以,如果我想为根文件系统使用RAID1,那没问题(GRUB可以读取md、LVM等),但我如何处理/boot/efi(UEFI ESP)?在我找到的所有回答这个问题的资料中,答案都是“哦,只需在每个RAID驱动器上手动创建一个ESP并复制文件,为每个驱动器添加一个单独的NVRAM条目(使用efibootmgr),你就没问题了!”我一点也不喜欢这个方案,因为它意味着副本之间可能会失去同步等。
Linux md RAID的当前实现将元数据放在分区的前面。这解决的问题比它创造的要多,但它意味着RAID对于不了解元数据的东西不是“不可见的”。事实上,mdadm对此发出了相当响亮的警告:
|
|
从mdadm手册页阅读:
|
|
首先我们在RAID上放置一个FAT32(mkfs.fat -F32 /dev/md0),查看结果,前4K全是零,file命令看不到文件系统:
|
|
所以,我们改用–metadata 1.0将RAID元数据放在末尾:
|
|
现在我们在ESP上有一个可见的FAT32文件系统。UEFI应该能够引导任何没有故障的磁盘,并且grub-install将写入挂载在/boot/efi的RAID。
然而,我们面临一个新问题:在(至少)Debian和Ubuntu上,grub-install尝试运行efibootmgr来记录UEFI应该从哪个磁盘引导。但这失败了,因为它期望单个磁盘,而不是RAID集。事实上,它什么也不返回,并尝试用空的-d参数运行efibootmgr:
|
|
幸运的是,我的UEFI在没有NVRAM条目的情况下可以引导,我可以通过运行时的“Update NVRAM variables to automatically boot into Debian?” debconf提示禁用NVRAM写入:dpkg-reconfigure -p low grub-efi-amd64
所以,现在我的系统将在两个或任一驱动器存在时引导,并且从Linux到/boot/efi的更新在引导时在所有RAID成员上可见。然而,这种设置有一个讨厌的风险:如果UEFI向其中一个驱动器写入任何内容(该固件在写出“引导变量缓存”文件时这样做了),一旦Linux挂载RAID,可能会导致损坏的结果(因为成员驱动器将不再具有FAT32的相同块级副本)。
为了处理这种“外部写入”情况,我看到一些解决方案:
- 在不在Linux下时使分区只读。(我认为这不是一件事。)
- 创建对根文件系统RAID配置的更高级了解,需要手动同步一组文件系统,而不是进行块级RAID。(似乎需要很多工作,并且需要将/boot/efi重新设计为类似/boot/efi/booted、/boot/efi/spare1、/boot/efi/spare2等)
- 偏好一个RAID成员的/boot/efi副本,并在每次引导时重建RAID。如果没有外部写入,就没有问题。(但选择偏好的副本的真正正确方法是什么?)
由于mdadm有“–update=resync”组装选项,我实际上可以做后者。这需要更新/etc/mdadm/mdadm.conf,在RAID的ARRAY行添加
|
|
(由于被忽略,我选择了/dev/md100用于下面的手动组装。)然后我在/etc/fstab中的/boot/efi条目添加了noauto选项:
|
|
最后,我添加了一个systemd oneshot服务,该服务用resync组装RAID并挂载它:
|
|
(并且不要忘记运行“update-initramfs -u”,以便initramfs有更新的/dev/mdadm/mdadm.conf副本。)
如果mdadm.conf支持ARRAY行的“update=”选项,这将变得微不足道。但查看源代码,这种更改看起来并不容易。我可以梦想!
如果我想保留一个UEFI无法更新的“原始”版本的/boot/efi,我可以更戏剧性地重新安排事情,将主RAID成员作为根文件系统中文件上的环回设备(例如/boot/efi.img)。这将使真实ESP中的所有外部更改在重新同步后消失。类似这样:
|
|
并在引导时仅从/dev/loop0重建它,尽管我不确定如何“偏好”那个分区……
注意:我用于捣鼓grub-install的命令:
|
|
更新:Ubuntu Focal 20.04现在提供了一种方式,让GRUB可以安装到任意设备集合,因此不需要RAID。哇。
© 2018 – 2020, Kees Cook。本作品根据知识共享署名-相同方式共享4.0国际许可协议授权。
评论(5)
5条评论
使用非RAID的引导分区。容易得多。 评论者:odi — 2018年4月19日 @ 9:55 pm
但那没有处理我的任何要求。:P 评论者:kees — 2018年4月19日 @ 11:21 pm
看起来UEFI智囊团完全回避了ESP冗余的问题,并将其全部留给了硬件供应商。这 predictably 导致了供应商特定(通常是脑死亡的)解决方案,这是不幸的。 评论者:yaneti — 2018年4月19日 @ 11:44 pm
在UEFI中,引导固件实际上会检查GPT分区表,寻找标记为“EFI系统分区”(ESP)UUID的分区。 UEFI规范不要求这样做。 相反,UEFI固件完全可以跳过这一步,只需寻找具有FAT文件系统的GPT分区,该文件系统在正确的位置有正确的文件。 我目前使用的所有UEFI固件都不关心GPT ESP UUID(即Super micro、Lenovo、Open Virtual Machine Firmware – OVMF)。 因此,您也可以将ESP的GPT分区类型设置为“Linux RAID”UUID – 从而明确表示FS是RAID的一部分。 也许这甚至有助于防止固件写入“引导变量缓存文件”(尽管我即使在非RAID设置中也没有注意到这样的缓存文件)。 顺便说一句,当在Fedora安装程序中选择镜像配置时,它也会在RAID-1上创建ESP(使用超级块格式1.0),并将GPT分区类型UUID设置为“Linux RAID”。Fedora的efibootmgr在这种配置下工作良好。 评论者:Georg Sauthoff — 2018年4月20日 @ 12:18 am
从字里行间看,我的UEFI固件内置了Intel Matrix Raid驱动程序,因此它可以从RAID读取ESP。所以我们所需要的只是一个干净室的BSD-2实现的mdadm驱动程序用于OVMF/EDKII,并在OEM固件中提供,不是吗?它可以非常基本,跳过这么多头并从偏移量开始读取。也许不写入任何东西,或者如果必须的话写入所有驱动器。 评论者:DImitri John Ledkov — 2018年6月12日 @ 2:12 am