FreeBSD/Firecracker平台正式发布:虚拟机监控器的跨系统兼容突破

本文宣布FreeBSD正式支持Firecracker虚拟机监控器,详细介绍了PVH启动机制、Virtio设备适配、串口控制修复等核心技术实现,以及如何构建和运行FreeBSD/Firecracker环境。

宣布FreeBSD/Firecracker平台

Firecracker虚拟机监控器由亚马逊云服务(AWS)开发,是构建AWS Lambda和AWS Fargate等服务的基础组件。尽管有多种启动和管理虚拟机的方式,但Firecracker以其极简主义脱颖而出——这对安全性(更少的设备意味着更小的攻击面)和减少启动时间都非常重要,尤其是在按需启动虚拟机以响应传入HTTP请求时。

Firecracker首次发布时,仅支持Linux操作系统;六个月后,Waldek Kozaczuk将OSv unikernel移植到Firecracker上运行。而就在几分钟前,有了第三个选择:FreeBSD现在可以在Firecracker上运行了。

我于6月20日开始这项工作,主要是出于好奇:我听说Firecracker支持PVH启动(这实际上是错误的!),并且我知道FreeBSD可以从Xen以PVH模式启动,所以我想知道让FreeBSD启动和运行有多难。结果证明,虽然并非不可能,但比我希望的要复杂一些。

我得到了其他FreeBSD开发者的很多帮助,特别要感谢Bryan、Ed、Jessica、John、Kyle、Mark、Roger和Warner,他们为我解释代码、帮助审查我的补丁,甚至编写了我需要的新代码。以下是实现FreeBSD/Firecracker平台运行的一些关键更改:

  • PVH启动机制使用ELF Note告诉加载器PVH内核入口点的位置;FreeBSD使用SHT_NOTE,而Firecracker(或更准确地说,linux-loader)只寻找PT_NOTEs。一旦我追踪到这个问题,Ed和Roger很快就修复了它。
  • 在PVH启动时,加载器提供请求的镜像(内核、可能的内核模块和ramdisk),以及一个包含启动过程所需元数据的“start info”结构。在Xen中,内核和模块首先加载到内存中,start info结构紧随其后;Firecracker则将start info页面放在首位,然后加载其余部分。在启动过程的早期,FreeBSD需要一个页面的临时空间——它原本使用start info页面之后的页面。Mark和Roger重写了FreeBSD PVH启动代码,使用PVH加载器提供的所有数据之后的第一个页面——不覆盖重要数据确实有区别。
  • Firecracker不提供ACPI,而是通过历史Intel多处理器规范中定义的MPTable接口提供关于CPU和中断控制器的信息。FreeBSD GENERIC内核不支持这一点——没关系,我本来就要提供一个定制的“FIRECRACKER”内核配置——但Firecracker的实现有两个错误:它将MPTable放在错误的位置(在系统内存广告顶部之上,而不是在最后1kB),并将包含表条目数的字段设置为零而不是适当的计数。在这两种情况下,Linux都接受了这种错误行为;因此,我在FreeBSD MPTable代码中添加了一个“bug for bug兼容性”选项。
  • 进入用户空间后,FreeBSD串行控制台在打印16个字符后停止。这个错误我认识,因为我在EC2上遇到过:UART在传输FIFO上丢失了一个中断。幸运的是,FreeBSD内核仍然有一个解决方法,设置hw.broken_txfifo="1"修复了这个问题。
  • 串行控制台也无法读取输入——实际上,Firecracker不会读取输入,任何按键都会留在终端缓冲区中,直到Firecracker退出。这原来是Firecracker的UART模拟中的一个错误——或者我应该说缺少的功能:Firecracker不模拟FCR(FIFO控制寄存器),而FreeBSD使用它来刷新FIFO。我添加了代码来检查通过FCR刷新FIFO是否成功,如果不成功,则切换到(较慢的)读取字节并丢弃它们的方法。(为什么需要刷新FIFO?当UART首次附加时,我们向其中写入数据以查看缓冲区的大小,然后丢弃虚拟数据。)
  • Firecracker使用Virtio向客户操作系统呈现虚拟设备;没问题,FreeBSD支持Virtio。除了……FreeBSD通过ACPI发现Virtio设备,而Firecracker上没有ACPI。相反,Firecracker通过内核命令行暴露设备参数(内存映射I/O地址和中断号)。这需要大量的管道处理——尤其是因为FreeBSD将内核命令行解释为环境变量,而Virtio MMIO规范要求设备作为一系列virtio_mmio.device=...参数暴露——即,每个都有相同的“变量名”。FreeBSD内核现在通过附加后缀来处理这种重复的环境变量,因此我们最终得到virtio_mmio.devicevirtio_mmio.device_1virtio_mmio.device_2等,Virtio驱动程序查找这些环境变量来创建设备实例。
  • 大多数Virtio主机处理由多个数据段组成的磁盘I/O;例如,QEMU处理128个段。Firecracker更极简:它拒绝具有多个段的I/O。这对FreeBSD处理来自用户空间的对齐I/O造成问题,因为虚拟地址空间中连续的缓冲区可能在物理地址空间中跨越不连续的页面。我修改了FreeBSD的virtio块设备驱动程序,以利用busdma系统,该系统根据需要“反弹”(即通过缓冲区复制)数据以符合对齐(和其他)要求。现在,当Virtio块设备仅支持单段I/O时,如果我们收到未对齐的请求,我们会反弹数据。

既然FreeBSD支持Firecracker,还有一件事要做:让Firecracker支持FreeBSD。我早些时候提到,我错误地认为Firecracker支持PVH启动;结果,Alejandro Jimenez两年前贡献了补丁,但它们从未被合并。他的一些代码最终进入了linux-loader项目(Firecracker使用);但我花了几个星期梳理他的数千行更改,以找出哪些进入了linux-loader,哪些仍然适用于Firecracker,哪些我必须从头重写——这个任务更加困难,因为Firecracker是用Rust编写的,而我以前从未使用过Rust!尽管如此,我最终成功了,并打开了一个PR,带有更新的补丁,我希望在接下来的几周内看到它们合并到主线Firecracker中。

如何尝试FreeBSD/Firecracker

要尝试在Firecracker上运行FreeBSD,您需要构建一个FreeBSD amd64 FIRECRACKER内核,并使用我的补丁构建Firecracker:

构建FreeBSD内核(在FreeBSD系统上):

1
2
# git clone https://git.freebsd.org/src.git /usr/src
# cd /usr/src && make buildkernel TARGET=amd64 KERNCONF=FIRECRACKER

构建的内核将位于/usr/obj/usr/src/amd64.amd64/sys/FIRECRACKER/kernel

构建支持PVH启动的Firecracker(在Linux系统上):

1
# git clone -b pvh-v3 https://github.com/cperciva/firecracker.git

并按照入门文档中的说明从源代码构建。

您可能还想构建一个磁盘镜像,以便FreeBSD有东西可以启动;将vfs.root.mountfrom=ufs:/dev/vtbd0放入Firecracker的boot_args中,告诉FreeBSD使用您附加的磁盘(即第一个Virtio块设备)作为根磁盘。

如果社区对尝试FreeBSD/Firecracker有显著兴趣,我将提供一个预构建的FreeBSD内核、FreeBSD根磁盘和Firecracker二进制文件,以便人们可以跳过自己构建这些的过程。

玩得开心!

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计