【芯月同辉】用 PXE + 无盘系统启动一个 RISC-V 计算集群!

引言

大家好!提前祝大家中秋节、国庆节快乐:tada:

我们是浙江大学超算队,从今年暑假开始,我们与进迭时空达成了合作,计划借助 MUSE Pi Pro 开发版,推出一系列 RISC-V 相关的课程实验与比赛赛题,宣传并推广 RISC-V 开源生态。

先讲一讲我们的背景:

从 2021 年开始,我们在浙江大学校内开设了「HPC 101 高性能计算」短学期课程。HPC 101 课程主要面向大一同学,同学们会利用暑假的两周时间集中学习 HPC 相关知识,并在暑假中完成相应的实验。

在今年之前,我们的全部实验都围绕着 x86-64 和 CUDA 生态设计,而今年我们一次性引入了 RISC-V 和 ARM 两个生态,并设计了对应的 CPU 向量化相关的优化实验。为了支撑总计 100 多位同学顺利完成实验,把 MUSE Pi Pro 开发版接入我们的集群就成为了一件必须要做的事情。

我们的集群架构 (简化版) 如下:

我们集群的基础设施基于存算分离的思路进行设计,主要基于两个技术来搭建:PXE 网络启动和 NFS 无盘系统,所有计算节点在启动时通过 iPXE 获得启动参数,加载内核、initramfs 和启动参数,实现高效、可控的系统管理;计算节点启动后通过 NFS 挂载相同的 overlayfs rootfs,实现环境的跨节点统一,而用户的家目录等则通过 NFS 挂载存储节点导出的目录。这样一来,一旦有新的 x86-64 计算节点上架,只需要简单设置 PXE 配置下发,就可以无缝接入我们的集群架构。

我们的目标是在 RISC-V 架构的计算节点上也实现具有相同灵活性的启动方式。在今年暑假期间,我们探索了在 MUSE Pi Pro 上实现相同流程的方法。我们摸索出的这套软硬件系统,在今年暑假中成功地接受了 100 多位同学的检验,其中 RISC-V 队列上有 600 余次作业申请,还算比较 Robust。因此,我们决定在这篇技术博客中分享给大家。

PXE 启动

PXE(Preboot Execution Environment)启动是一种通过网络启动计算机的技术,允许计算机在没有本地硬盘或操作系统的情况下从网络服务器加载操作系统。它基于客户端-服务器模型,其中计算机(客户端)通过网络与PXE服务器进行通信,从中获取启动所需的操作系统镜像和配置文件。

无论选用怎样的启动模式,在 MUSE Pi Pro 上启动一个 Linux 系统无非就是 3 个元素:Linux 内核,initramfs 和 Device Tree。PXE 启动只是把这些内容的加载变成了基于以太网的自动化流程而已,PXE 启动过程通常分为几个步骤:

  1. DHCP 请求与响应:当计算机开机时,它会通过网络广播一个 DHCP 请求,询问是否有可用的网络启动服务。DHCP 服务器回应客户端,并提供网络配置参数,如 IP 地址、网关、子网掩码以及 PXE 服务器的地址。

  2. TFTP 请求与响应:客户端收到 PXE 服务器地址后,会通过 TFTP(Trivial File Transfer Protocol)协议向 PXE 服务器请求引导程序文件(如 pxelinux.0grub 等),该文件通常是一个小型的引导加载器。

  3. 系统加载:引导程序在客户端计算机上加载后,进一步加载操作系统的内核、initramfs 和设备树,最终完成操作系统的启动。

iPXE

在 x86-64 和鲲鹏 ARM 计算节点上,我们均采用了 iPXE 进行 PXE 启动。iPXE 是一个 UEFI 程序,可以自动解析 DHCP 请求中配置的 PXE 服务器地址,并且完成后面的启动流程。

既然 MUSE Pi Pro 支持 UEFI 启动以及 UEFI Shell,那么 iPXE 就成为了我们的首选项。很幸运的是,iPXE 也可以编译成 RISC-V UEFI 程序,并官方支持 RISC-V 架构。

不过当我们进入 UEFI 并选中 iPXE 程序并执行时,我们遇到了一个困难——机器无法通过 DHCP 获取 IP,进而后面的网络启动都无法进行。通过 iPXE 程序内的交互式指令,我们发现 iPXE 并不能识别到 MUSE Pi Pro 的网卡。而这归根结底,是因为 MUSE Pi Pro 的 UEFI 并没有实现 SNP (Simple Network Protocol)。因此 UEFI 内的 iPXE 无法操作网卡,进而也无法连接网络。

我在论坛中发了帖子 (https://forum.spacemit.com/t/topic/602) 询问这个问题,但是没有得到相关的回复 (不过确实看到了对应的文档更新,这点要点赞)。给 MUSE Pi Pro 的 UEFI 添加 SNP 的支持就超出了我们的能力范围了,因此 iPXE 启动的计划宣告失败。

Uboot PXE 启动

随后,我们便把目光转向了 uboot 自带的 PXE 启动。经过简单的尝试,在 uboot 的环境下,MUSE Pi Pro 是能够正常 DHCP 获取 IP 并且连接网络的,这给了我们很大的希望。我们在集群的主路由上部署了 tftp 服务器,并且把 Bianbu OS 的内核、initramfs 和 rootfs 提取出来作为测试环境,在 TFTP 上编写了 uboot PXE 启动需要的 pxelinux.cfg 后,便可以通过下面的指令成功实现 PXE 启动:

dhcp;
set serverip=<PXE server ip>;
pxe get;
pxe boot;

接下来的问题就是如何将这套配置快速下发到所有的设备上(我们有 10 台 MUSE Pi Pro 开发版,上架 8 台,备用 2 台)。通过阅读文档和探索进迭开放的 uboot 源码,我们发现可以把刚才的 PXE 启动设置固化到 uboot 的环境变量中,这样一来,只要烧写新的 uboot 和环境变量分区,就可以自动进行 PXE 启动了。

当时我们还没有摸索出如何打包成开发版量产包的格式,所以选择了手动通过 fastboot 来烧写。具体而言,我们先在 SD 卡上烧写一个能够进入 uboot 的系统镜像,然后逐个给 MUSE Pi Pro 插上这个 SD 卡,进入 uboot fastboot 模式,然后在主机上通过 fastboot 更新 mtd 上的 uboot 相关分区。因为我们的设备不算多,所以这个过程并没有耗费太多时间。

Debian rootfs 制作

在上一步中,我们使用了 BianbuOS 镜像中提取的内核与文件系统进行了测试。不过,我们的 x86-64 节点全部使用当时最新的稳定版本 Debian 12。为了实现在 MUSE Pi Pro 上获得相对纯净且统一的系统环境,我们给 MUSE Pi Pro 构建更纯净一些的 Debian 13,因为 Debian 从 Trixie (13) 才开始正式支持 RISC-V 架构,且当时 Trixie 即将成为新的 Stable 版本。

之前我们提到所有的计算节点共享相同的不可变 rootfs,这是通过 Linux 的 overlayfs 实现的,对上层的修改不会影响底层的文件。为了实现系统软件和配置的更新,我们有一套 rootfs 构建脚本,能够自动构建 Debian、Ubuntu 等发行版的稳定分支的环境。

那么,现在所需要做的工作就是给这套 rootfs 构建脚本添加 RISC-V 目标的支持,并且使其能够成功在 MUSE Pi Pro 上启动。对于构建脚本来说,主要有两个阶段:

  1. 第一阶段:Bootstrap

    这个过程是使用 debootstrap 生成一个基础的 Debian rootfs。对于适配 MUSE Pi Pro 来说,我们需要阻止 debootstrap 使用默认的系统内核 (6.12 主线),因为该内核并不支持进迭时空基于 K1 的相关产品,并且需要自行构建进迭提供的厂商 Linux 内核,安装到文件系统中。好在进迭的厂商内核仓库中提供了把内核、内核模块、设备树等打包 deb 包的脚本,我们只需要配置好交叉编译环境,设置好编译选项(比如编译一些必要的内核模块),便可以编译并打包内核包了。随后,只需要删除已有的系统内核,替换为进迭的厂商内核即可。

  2. 第二阶段:chroot 进行进一步配置

    生成好 rootfs 之后,我们还需要 chroot 进去进行进一步的调整,比如安装一些自定义的软件包,修改一些系统配置之类的。与构建 x86-64 系统不同的是,这一步我们需要使用 qemu 来模拟 RISC-V 架构,才能执行 RISC-V 的二进制程序。

    我们在构建脚本里添加了一个选项,判断如果当前的构建目标是 RISC-V,就跳过一部分设置,比如安装 zfs,安装 CUDA 驱动等等。不过能感受到的是大部分常用的、开源的软件都是支持 RISC-V 的,这也得益于开源社区对 RISC-V 生态的大力建设。

    除了安装软件外,这一阶段还有一件非常重要的事情要做:构建 initramfs。

    我们选用了 dracut 来构建 initramfs,因为它的 overlay 模块可以在系统启动阶段将系统根目录挂载成只读,并且通过 overlayfs 来允许上层的修改。同时,还需要注意把厂商固件一并打入 initramfs,例如如果没有 esos.elf 这个固件,系统就无法正常启动。

经过不断的尝试和调整,我们最终成功地构建出了一版 RISC-V 的 Debian Trixie rootfs,并且成功在 MUSE Pi Pro 上进行 PXE 启动。

Slurm 异构集群

灵活的作业调度和管理对于迎接 HPC 101 这样选课人数超过 100 人的课程是必选项。在超算领域,最为广泛使用的资源调度软件便是 Slurm 了,我们的集群也使用 Slurm 进行管理。

Slurm 是一款大型开源软件,一开始我们觉得可能会遇到很多的麻烦事。不过经过调研,我们惊喜地发现 slurm 对 RISC-V 的支持还算不错,尤其是看到爱丁堡大学有一个兴趣小组已经用 Slurm 连接起了一个 RISC-V 集群 https://riscv.epcc.ed.ac.uk/status/ ,给了我们很强的信心。经过漫长的编译(无论是基于 qemu 模拟还是在 MUSE Pi Pro 本地编译),我们成功地在 RISC-V Debian 上构建并安装了 Slurm,并且能够正常连接 x86-64 的管理节点,并上报机器状态。

同样地,申请任务、执行任务也都正常:

这样一来,我们的 8 块 RISC-V 开发版就成功接入了现有的 x86-64 计算集群,为科学计算、深度学习、人工智能等工作负载注入了新的异构算力。

因为我们的用户家目录是通过 NFS 跨节点、跨架构挂载的,所以对于同学们来说,只要编译的时候配置好交叉编译工具链,那么提交一份作业到 RISC-V 节点的感受和提交到 x86-64 节点的感受几乎没有任何差异。在实践中,整个 HPC 101 课程期间,同学们都是在 x86-64 的节点上完成全部实验的,同学们只需要交叉编译出 RISC-V 的二进制,再用 slurm 提交就可以直接运行。

实拍图

最后给大家看看我们 RISC-V 计算集群的实拍图吧:

思考

值得一提的是,除了 RISC-V 的开发版,我们还引入了鲲鹏 ARM 计算节点。鲲鹏服务器虽然是 ARM 架构,但是实现了 UEFI + ACPI,且内核驱动支持完整,启动流程和 x86-64 节点几乎别无二致,也可以直接启动 Debian 12 的 ARM 服务器镜像。而目前 RISC-V 生态的 ACPI 相关的规范刚刚标准化,驱动的主线化也有很长的路要走,但这是 RISC-V 走向数据中心、服务器平台的必经之路,希望进迭时空未来也能跟进。

最后,感谢进迭时空官方和超算队指导老师对我们的支持与帮助,也期待未来能看到更多高性能 RISC-V 计算产品涌现 :tada:

1 个赞