我们如何将核心单元启动时间从数小时缩短至数分钟

TL;DR · AI 摘要
Cloudflare 通过识别 UEFI 固件中网络启动接口的线性搜索缺陷,将核心服务器重启时间从 4 小时缩短至几分钟,涉及近 2000 台 Gen12 服务器,关键优化在于跳过无效的 IPv4 HTTPS 和 iPXE 尝试,直接使用有效的 IPv6 HTTPS 启动。
核心要点
- Cloudflare 的 Gen12 服务器在固件更新后因 UEFI 线性搜索所有网络启动接口导致单次重启耗时 20 分钟,累计达 4 小时。
- 问题根源是系统依次尝试 IPv4 HTTPS、IPv4 iPXE 等失败接口,每次超时约 5 分钟,直到最终成功使用 IPv6 HTTPS 启动。
- 解决方案是修改启动顺序配置,优先启用 IPv6 HTTPS 启动,避免无效尝试,使整体升级时间从小时级降至分钟级。
结构提纲
按章节快速跳转。
- §问题背景
Cloudflare 核心服务器在固件更新后重启耗时从分钟级延长至 4 小时,影响近 2000 台 Gen12 服务器的自动化升级流程。
服务器通过 PXE 或 UEFI HTTPS 进行网络启动,Cloudflare 使用开源 iPXE 实现灵活、可编程的启动流程。
UEFI 固件在启动时按顺序尝试所有网络接口,每次失败超时约 5 分钟,导致总延迟累积达 20 分钟。
系统依次尝试 IPv4 HTTPS、IPv4 iPXE、再次 IPv4 HTTPS 和 iPXE,最后才成功使用 IPv6 HTTPS 启动。
通过串口控制台实时监控发现,故障并非固件回归,而是启动顺序配置不当导致无效接口被优先尝试。
调整 UEFI 启动优先级,跳过无效接口,直接使用 IPv6 HTTPS,使单次重启时间从 20 分钟降至 2-3 分钟。
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- Core Server Boot Optimization
- Problem: 4-hour boot after firmware update
- Root cause: Linear search of network interfaces
- Network Boot Interfaces
- IPv4 HTTPS (failed)
- IPv4 iPXE (failed)
- IPv6 HTTPS (successful)
- Solution: Reorder UEFI boot sequence
- Prioritize IPv6 HTTPS
- Skip invalid attempts
金句 / Highlights
值得收藏与分享的关键句。
固件更新后,部分核心服务器重启耗时长达 4 小时,而非之前的几分钟。
每次失败的网络启动尝试会消耗约 5 分钟等待超时响应。
系统依次尝试 IPv4 HTTPS、IPv4 iPXE 并超时,最后才成功使用 IPv6 HTTPS 启动。
通过优先设置 IPv6 HTTPS 启动顺序,我们将总启动和升级时间从小时级缩短至分钟级。
2026-06-01
7 分钟阅读

Cloudflare 的核心数据中心是运行我们控制平面、计费和分析系统的集中式数据中心,与处理用户流量的全球分布式边缘节点不同。核心服务器采用裸金属架构,一旦重启过程中出现问题,后果可能迅速蔓延。
它们的启动序列由 UEFI(现代固件标准)协调,该标准负责初始化硬件并将控制权移交给操作系统。在这一交接过程中微小的异常,也可能带来巨大的影响。
在一次例行固件更新后,我们部分核心服务器的上线时间从原本的几分钟延长到了四个小时。本应一天内完成的全集群升级,变成了持续数天的漫长过程。新节点在首次启动时就不得不经历完整的超时“关卡”。维护窗口不断膨胀,工程团队不得不手动监控那些本应无人值守的升级流程。
此问题影响了整个 Gen12 服务器集群——近 2000 台设备。每次升级中途出现意外故障,都意味着必须重新开始整个流程,而新增的计算资源只能闲置等待超时“关卡”结束。
本文将讲述我们如何定位到问题根源:一个固件的小瑕疵,以及系统对所有可用网络启动接口进行过度积极的线性搜索;并介绍我们如何将总启动和升级时间从数小时缩短回几分钟。同时,我们也会分享在 UEFI 内部机制、厂商特定特性以及最终解决问题的自动化策略方面的经验。
网络启动接口
网络启动接口允许服务器通过网络而非本地存储来加载操作系统。这对于实现集中化、自动化和可扩展的机器启动控制至关重要,尤其适用于在全球分布、承载不同工作负载的服务器集群中。由于我们的服务器部署在不同环境中、承担不同任务,因此对特定网络启动接口有不同的需求。主要的两种接口是 预启动执行环境(PXE) 和基于统一可扩展固件接口(UEFI)的 HTTPS 启动。
在我们的重启流程中,服务器通常会经过 PXE 阶段,以支持各种自动化操作。在 Cloudflare,我们使用开源项目 iPXE ——一种支持 HTTP 和 HTTPS 等现代协议的开源网络启动固件。它使计算机可以直接从 Web 服务器、云平台或企业存储网络加载操作系统,速度更快、可靠性更高。
对于组织而言,iPXE 将启动过程转变为可编程的工作流。它提供高级脚本功能,使 IT 团队能够自动化复杂的部署任务,例如根据特定硬件配置部署服务器,或管理安全的无盘工作站。
部分硬件支持基于 HTTPS 的 UEFI 网络启动,允许主板固件原生安全下载操作系统文件。
线性搜索
我们的故事始于那次关键的固件更新。更新后,内部渠道陆续收到报告:服务器无法正常上线。监控仪表板显示,多台机器长时间卡在操作系统加载前的状态。我们最初怀疑是固件回归:也许更新本身引入了导致启动卡死的 bug。
为了排除这种可能性,我们连接了一台受影响服务器的串口控制台,实时观察其启动过程。固件自检(POST)顺利完成,硬件初始化也一切正常。但随后,服务器并未快速进入网络启动阶段并下载操作系统镜像,而是陷入了漫长的等待状态——并且持续等待。
控制台输出揭示了真相:系统尝试通过 IPv4 HTTPS 进行网络启动,在数分钟后超时;接着尝试 IPv4 iPXE,再次超时;然后重复这两个步骤——直到最后才轮到真正能成功的 IPv6 HTTPS 启动接口。
每一次失败的网络启动尝试都会消耗大约五分钟的超时等待时间。由于在找到正确接口前需要尝试四次,单次启动周期就浪费了约二十分钟。对于常规重启来说,这已经令人痛苦不堪;而对于需要多次顺序重启的固件升级自动化流程(每个组件一次重启),这些二十分钟的惩罚叠加起来,每台服务器的等待时间接近四个小时。

不玩猜谜游戏:直接声明我的启动接口
在追踪启动序列并隔离出超时模式后,根本原因变得清晰:服务器正盲目地逐个遍历所有可用的网络启动接口,在每个接口失败后才继续下一个。解决方案很简单——完全消除猜测:提前明确声明正确的启动接口,让系统不再浪费时间在永远不会响应的接口上。
然而,实际落地却远非易事。如接下来所述,我们遇到了多个障碍:启动自动化工作流的顺序、一个我们无法修改的设置,以及来自不同网卡厂商的字符串格式差异。
我们的启动自动化工作流
我们的启动自动化流程分为三个主要阶段:固件初始化、预启动(pre-boot)和内核启动。上电后,UEFI 固件首先完成硬件及外设的初始化,随后进入 PXE 预启动环境。预启动阶段会配置网卡,并执行一个名为 bootloader 的小型程序,从而启动内核。在 PXE 阶段,系统会探测各个网络接口,以确定正确的启动接口。首次启动时,固件升级也包含在我们的启动自动化流程中。
由于每次固件升级都需要重启(以及随之而来的网络启动尝试序列),这导致总启动时间接近四小时。

通过重构自动化流程,在每个硬件/使用场景的预启动 PXE 阶段早期就声明网络启动接口顺序,我们成功将总时间缩短了约一小时,因为启动过程不再需要为每次固件升级花费 20 分钟进行探测。

尝试声明网络启动接口顺序引入了两个具体限制:
- 旧版支持问题:较老版本的 UEFI 不支持启动顺序设置
- 持久性问题:配置设置在 UEFI 固件升级后通常会被重置
为解决这些边缘情况,我们实现了一个状态验证步骤。固件自动化现在会在配置更改后验证设置:如果检测到设置已被修改,则重新应用配置并触发重启。
虽然首次启动可能稍长,但这一变更大幅减少了后续所有启动所需的时间——从大约 20 分钟缩短至每次启动不到一分钟。
厂商禁用的启动顺序设置
网络启动设置的内部数据结构是一个 EFI_IFR_REF3 数据结构,该结构采用延迟加载(lazy loaded)方式,意味着数据只有在通过 GUI 回调显式访问时才会实例化:
typedef struct _EFI_IFR_REF3 {
EFI_IFR_OP_HEADER Header;
EFI_IFR_QUESTION_HEADER Question;
EFI_QUESTION_ID QuestionId;
EFI_GUID FormSetId;
} EFI_IFR_REF3;尽管这是行业标准做法,用于加速 BIOS 启动时间,但它使“网络启动接口”对我们的程序化扫描不可见。由于该结构尚未“加载”,我们的自动化无法发现其优先级设置。
我们与供应商合作,在固定的“启动顺序模块”中启用特定令牌,从而强制在启动过程中发现网络启动接口,无需手动 GUI 操作。
我们设备制造商提供的 UEFI 中有一个不可变设置 Force Priority Httpv4 Httpv6 Pxev4 Pxev6,阻止我们更改启动顺序。
这要求我们获取供应商的新 BIOS 版本,并在设置启动顺序时进行调试会话。
不同网卡厂商的字符串差异
根据网络接口卡(NIC)厂商的不同,字符串也会不同,导致通过 iPXE 配置启动顺序时出现不匹配。
示例:
UEFI: HTTPS IPv4 Ethernet Network Adapter XXX-XXX-Y for OCP 3.0 P1
UEFI: HTTPS IPv4 Network Adapter - 50:00:E6:8F:4F:32 P1为解决此问题,我们必须为 CfHIIConfig_App 工具增加一项新功能,使其能够在没有完整字符串的情况下设置配置:
.*HTTP.*IPv4.*P1
配置将与接受的配置字符串进行匹配,并选择正确的启动顺序。目前我们正在与 UEFI 供应商合作,标准化网络接口字符串,仅保留相关信息(如协议、传输类型、端口号和物理插槽索引),并移除产品细节(如 MAC 地址)。如有需要,产品详情可从网卡嵌入的 Vital Product Detail(VPD)信息中读取。这样可以消除配置漂移,并避免使用通配符。
#### 无法通过 iPXE 检查配置
由于 iPXE 将该变量读作十六进制(HEX),它会将字符串输出解释为十六进体制。为了检查网络启动设置是否被修改,并减少启动时间(避免在设置前打印变量),我们实现了一个布尔标志 uefi-same-hex,用于指示配置是否已更改。
这使得我们可以只运行一次 set 命令,而不是先运行 show 进行比较,再在配置未达到期望状态时运行 set。
这使得我们可以只运行一次 set 命令,而不是先运行 show 进行比较,再在配置未达到期望状态时运行 set。
# 构建路径以读取更新变量
set buffer-var-guid 91468514-75bc-4bb5-8f33-91efff9e9b1f
set var-upd-path efivar/CfHIIVarUpd-${buffer-var-guid}
# 执行配置更改命令
imgexec <signed CF UEFI configuration App> set ${uefi-setting}=${uefi-value}
# 如果配置已更改,则将更新变量与预期值进行比较。
# 如果已更改,则设置本地变量以重启系统
iseq ${uefi-same-hex} ${${var-upd-path}} || set has-changed ${uefi-diff-hex}结果:更动态的系统
通过消除网络启动序列中的猜测环节,我们将原本长达四小时的繁琐流程恢复为仅需三分钟的高效过程。最终结果是一个动态系统,无需任何手动 BIOS 交互。单一的 BIOS 固件镜像适用于所有 SKU,配置更新可通过现有发布管道大规模部署,整个工作流完全由 iPXE 驱动。
| 指标 | 更改启动顺序前 | 更改启动顺序后 | | --- | --- | --- | | 固件升级自动化 | 接近 4 小时 | 3 分钟 | | 后续单次启动 | 约 20 分钟 | 不到 1 分钟 |
如果没有深入研究 UEFI 内部机制,与我们的 OEM 厂商紧密合作以解锁诸如程序化启动顺序控制等功能,并利用 iPXE 等开源工具构建可扩展的自动化方案,这一切都不可能实现。
随着每一天的推进,Cloudflare 的 OpenBMC 团队持续学习、实验并优化我们核心机群中的启动流程。如果你正在管理裸金属基础设施,并且正面临服务器启动缓慢的问题,希望本文为你提供了一个实用的框架,帮助你识别并消除自身网络启动序列中的不必要延迟。对于那些希望进一步了解 iPXE 和网络启动自动化的读者,欢迎访问 此处!