
机房交付前一晚最容易翻车的从来不是业务跑不起来而是固件版本对不上。我见过太多场景机器在工厂预装时 BIOS 是三个月前的版本BMC 还停在上一个大版本网卡固件跟交换机那边协商出来的链路参数对不上结果整批机器在客户现场被判定不通过回厂重刷。而真正能在产线、实验室、交付前快速把固件拉齐的手段恰恰不是外挂一个带外管理平台排队刷而是在操作系统里直接调厂商工具做更新。这篇就把服务器测试之系统下更新固件这件事掰开揉碎讲一遍一台机器里到底有多少块固件、每块固件用什么命令读版本、什么工具刷、刷完怎么验证、顺序怎么排、以及那些只有踩过才知道的坑。内容适合三类人看做服务器整机测试和交付验收的工程师、负责机房批量运维的同学、以及刚接触固件这条线、只会跟着文档点按钮的新人。全文围绕服务器、测试、系统、更新固件这四个关键词展开命令都是可以直接抄去用的但更新前的风险判断和顺序编排比命令本身重要得多。1. 为什么在系统下更新固件必须单独当一件事来做1.1 带内更新和带外更新各管一段路很多团队一提刷固件脑子里第一反应就是登录 BMC 网页点上传。这属于**带外out-of-band**路径走的是 BMC 自己的网络栈和 HPM.1 之类的固件更新协议好处是跟操作系统无关机器只要能上电就能刷坏处也很明显传输速度受 BMC 那颗低功耗处理器限制一个几十兆的 BIOS 包传十几分钟很正常批量场景下排队等得人抓狂而且 BMC 自己的固件挂了这条路就直接断掉。**带内in-band**就是在操作系统里跑厂商的更新工具。它的优势是快、能脚本化、能塞进自动化测试流水线里一次批次几百台机器并行跑日志还能统一收集。代价是对系统环境有要求得有 root 权限、得装对内核模块、/tmp或/var得有足够空间、SELinux 或系统加固策略可能把工具的行为拦掉。提示这两条路是互备关系不是替代关系。带内刷 BMC 失败、刷砖了最后一根救命稻草还是带外强制恢复。所以凡是产线测试流程两条路都得跑一遍验证别只测一条。1.2 一台机箱里到底躺着多少块有固件的器件这是新人最容易低估的地方。以为更新固件就是更新 BIOS实际上机箱里能独立升级固件的器件随便一数就十几个。器件类别典型固件生效方式常见更新通道主板BIOS/UEFI需重启部分需 AC 断电带内 DUP / 带外 Redfish芯片组ME / CSME / PSP随 BIOS 一起需 AC 断电跟 BIOS 包绑定管理BMC 固件BMC 自身重启带内厂商工具 / 带外逻辑CPLD / FPGA部分需整机断电厂商专用工具网卡NIC FW NVM部分热生效部分需重启ethtool / 原厂工具存储控制RAID / HBA 固件需重启storcli / ssacli硬盘NVMe / SAS / SATA FWNVMe 可热激活SAS 常需断电nvme-cli / 厂商工具供电PSU 固件需断电重上电通常走 BMC背板Expander / 背板 CPLD需断电走 BMC 或专用工具图形GPU VBIOS需重启厂商工具安全TPM 固件需特殊流程原厂工具散热风扇板 / 调速固件随 BMC跟 BMC 包绑定把这张表列出来的意义是固件更新测试的覆盖面取决于你有没有把这些器件全部识别出来。只刷 BIOS 就交付等于把风险留给客户现场。1.3 刷上去了和刷对了完全是两码事我审过不少测试报告里面写着固件更新测试通过翻到细节只有一句执行更新工具无报错。这根本不叫通过。真正的验证至少包含三层版本读回是否与目标一致、更新过程日志和硬件告警日志SEL里有没有异常条目、更新之后整机功能有没有回归。再往下还有第四层断电、复位、重复升级这些异常路径有没有覆盖。这套东西的逻辑跟业务功能测试是一样的——能跑通不代表稳定跑通一百次不掉链子才叫稳定。固件层面的稳定性尤其如此因为它一旦出错代价往往是把主板刷成砖。2. 更新前的版本盘点先拉一张准清单再谈动作2.1 用系统自带命令先把底子摸出来在 Linux 下哪些信息不装厂商工具也能拿到这是基础功。下面这些命令我在任何一台新机器上都会先跑一遍形成一个基线快照。# BIOS 版本、发布日期、厂商 dmidecode -t 0 dmidecode -t bios # 整机型号、序列号用来对固件包型号 dmidecode -t 1 # BMC 固件版本IPMI 通道 ipmitool mc info ipmitool fru print # 网卡固件版本、驱动版本、PCI 位置 ethtool -i eth0 lspci -vvv | grep -i -A2 Ethernet # NVMe 盘固件版本fr 字段 nvme list nvme id-ctrl /dev/nvme0 | grep -E ^fr|^mn|^sn # RAID 卡固件不同厂商命令不同两个常见分支 storcli /c0 show all | grep -i -E fw|version ssacli ctrl all show detail # 全量 PCI 设备清单用来对照有没有漏掉的器件 lspci -nn这套命令的价值在于它不依赖任何厂商包是系统自带的。装系统、验收、交付前都能跑跑完把输出存成一个文本快照后面更新完再跑一次做 diff就是最朴素的更新前后对比。注意ethtool -i读到的firmware-version有时候跟 BMC 里显示的网卡固件版本不是同一个东西。网卡通常有固件FW和NVM/配置镜像两套版本号有些工具只更新其中一套。做测试的时候必须两个都读出来分别比对否则会出现工具说成功了但 BMC 侧仍显示旧版本的诡异现象。2.2 厂商工具自带的盘点命令更权威通用命令的局限在于它只能看到操作系统枚举出来的东西看不到 BMC、CPLD、PSU、背板这些幕后器件。这时候就得用厂商的管理工具。某国际大厂机型racadm getversion、racadm swinventory能一次性吐出 BIOS、BMC、CPLD、网卡、RAID 卡、PSU 的完整版本表Redfish 接口/redfish/v1/UpdateService/FirmwareInventory也是同一份数据。另一家ilorest serverinfo、ilorest ls配合 SPP 包做版本比对。国产和其他品牌类似基本都有OneCLI、ipmitool扩展或者自研 CLI。通用跨厂商方案是fwupdfwupdmgr get-devices、fwupdmgr get-updates。它的好处是接口统一坏处是覆盖的器件范围取决于厂商有没有把更新包提交到那个生态里服务器侧覆盖度参差不齐别把它当成唯一手段。我的习惯是两套都跑交叉比对。如果厂商工具列出的器件数量明显多于lspcidmidecode能看到的说明有带外侧器件需要单独处理如果反过来说明系统侧有器件厂商工具没管得手写脚本兜底。2.3 版本字串里最容易踩的三个坑第一坑同型号不同批次版本字符串格式不一样。比如同是某型号网卡早期批次固件号是纯数字后期批次带了字母后缀和构建号。做自动比对的时候如果用字符串完全匹配会把其实就是同一个版本误判成不一致。稳妥做法是先做规范化处理去掉前缀、统一大小写、只比对主次版本段。第二坑双镜像器件读回的是当前活动镜像。BIOS、BMC、CPLD 这类器件很多有 A/B 双镜像或者 golden image 机制。你读回版本读到的是当前 running 的那一份另一份备份镜像可能还是旧的。如果更新过程中途失败自动回滚了你读到的就还是旧版本看起来更新没成功实际是保护机制生效了。所以判断更新是否成功必须同时看更新工具的返回码和 SEL 里的固件更新事件记录。第三坑OEM 版本号和原厂版本号不是一套编号体系。主板整机厂商会自己套一层版本命名芯片原厂又是另一套。写成版本对照表的时候最好把两套编号都记上并在旁注里写明对应关系否则跨团队沟通非常容易鸡同鸭讲。3. 分器件的更新命令与验证动作3.1 BIOS / UEFI 与 ME必须选对复位类型BIOS 更新是所有器件里最重的一类因为它牵涉到启动流程本身。Linux 下的通用做法是运行厂商提供的 Linux 版更新包典型形态是一个可执行文件加上静默和强制参数chmod x BIOS_XXXXXX.BIN ./BIOS_XXXXXX.BIN -q -f # -q 静默-f 强制跳过版本比较包含 ME/CSME 或 PSP 的机器BIOS 包通常会把这块固件一并带进去。这里有个关键认知BIOS 更新写进 flash 之后并不代表 ME 那部分就生效了。ME 运行在芯片组的独立微控制器上只有整机彻底掉电AC cycle / 冷启动才会重新加载。很多测试报告里写的BIOS 更新成功、版本已变其实只验证了 BIOS 那一半。复位类型我用一张表来区分这个表我在团队里要求所有人都背下来复位类型动作影响范围适用器件系统重启rebootOS 正常重启只重启 OS少数网卡固件热复位warm reset复位 CPU/内存不断电大部分芯片保留状态部分设备激活冷复位cold reset重新上电初始化BMC、部分 PCIe 设备BMC、CPLDAC 断电AC cycle彻底断电 30 秒以上再上电全机全器件BIOS/ME、PSU、背板实操心得做 BIOS 更新的时候我会把 AC 断电做成硬性步骤并且严格要求断电时长。某些 PSU 的保持电容放电需要时间断电 3 秒和断电 30 秒结果完全不同——断得太短ME 状态可能没清干净读回版本会出现有时新有时旧的间歇性现象这种问题查起来极其折磨人。更新后的验证动作固定三件dmidecode -t 0读回 BIOS 版本和日期、BMC 侧再读一遍版本表做交叉确认、SEL 里检查有没有更新相关的告警或者校验失败记录。3.2 BMC 与 CPLD更新中断链是正常现象BMC 更新的特殊性在于——它会把自己的管理通道刷断。更新过程中BMC 重启带外连接断开风扇控制可能短暂进入默认全速电源策略也可能临时切换。如果你的测试脚本同时在跑带外监控这时候必然会刷出一堆连接失败的误报。做测试的时候必须把这段时间窗排除掉否则告警统计完全失真。CPLD 更麻烦因为它管的是上电时序和板级逻辑。CPLD 更新失败不会立刻表现为开不了机而是表现为某些槽位偶发识别不到设备风扇转速曲线不对这类非常难定位的问题。所以 CPLD 更新一定要在整机断电后重新上电验证并且把 PCIe 设备枚举结果、风扇转速、温度读值全部对一遍。# 带外通道更新 BMC 的典型路径示意具体参数以厂商文档为准 ipmitool hpm upgrade bmc_fw.img activate # Redfish 简化更新HTTP/HTTPS 拉包 curl -k -u user:pass -X POST \ -H Content-Type: application/json \ -d {ImageURI:http://192.168.1.10/bmc_fw.bin} \ https://bmc-ip/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate3.3 网卡固件链路会断bonding 会切网卡固件更新的核心风险是链路中断带来的业务抖动。在做了 bond 或者多路径的环境里更新某张网卡时链路会 down 掉再恢复触发主备切换。如果对端没有正确配置会出现短暂丢包甚至路由震荡。不同厂商的工具链差异很大通用读取ethtool -i eth0、lspci -vvv。某家用得很广的高速网卡方案mlxfwmanager --query、mlxfwmanager --update或者mstflint系列。另一家的万兆方案原厂nvmupdate64e或者niccli一类的工具。通用开源方案fwupdmgr但要看厂商有没有把包提交上去。验证动作除了版本读回还要看链路协商结果ethtool eth0里的 Speed、Duplex、Link detected以及ethtool -S eth0里的错误计数有没有异常增长。曾经遇到过一次更新后链路速率从 25G 掉到 10G版本号没错、链路也是 up 的就是协商速率不对——只看up/down是发现不了的。3.4 存储侧RAID 卡、HBA 和硬盘各不相同RAID 卡的更新相对成熟以storcli为例storcli /c0 show all | grep -i fw # 先读当前版本 storcli /c0 download fileraid_fw.rom # 下载固件 # 之后按提示重启或者执行在线激活需要注意的是RAID 卡的固件和它自带的 Option ROM供 BIOS 阶段初始化用的那部分经常是成对更新的。只更新固件不更新 ROM会出现OS 里识别正常、但 BIOS 阶段看不到启动盘的怪问题。HBA 卡直通模式同理固件和 BIOS 部分要匹配。NVMe 硬盘的更新是流程最清晰的一类nvme-cli直接支持nvme id-ctrl /dev/nvme0 | grep ^fr # 读当前固件版本 nvme fw-download /dev/nvme0 --fwfw.bin # 下载到设备缓冲区 nvme fw-activate /dev/nvme0 --slot1 --action1 # action: 1立即激活 2下次复位激活 3立即激活并复位action的选择直接决定生效时机选 1 需要设备支持热激活选 2 要等下次重启选 3 会立刻复位设备。生产环境里我一般选 2 或者 3 并安排维护窗口避免动作瞬间的 I/O 悬空。SAS/SATA 盘的固件更新通常需要整机断电才能生效而且很多厂商工具是按背板槽位寻址的。混插不同品牌硬盘的机器要特别小心批量工具极可能扫不全出现刷了 10 块盘实际重上电后只有 7 块版本变了的情况。我现在的做法是更新前先把lsblk、nvme list、RAID 卡侧的物理盘清单全部导出来更新后逐块对缺一块都不算通过。3.5 电源、背板和其他边缘器件PSU 固件一般只能通过 BMC 通道更新因为操作系统管不到它。这里有条铁律更新 PSU 固件前必须确认双电源都在线且来自不同配电回路。如果机器只有一个电源在工作更新过程中一旦出错断电机器直接黑掉而且 PSU 刷砖没什么好办法只能换。背板 Expander、风扇板、GPU VBIOS 这几类属于不常更新但一出事就是大事的器件。它们的共同特点是更新工具少、文档差、生效条件苛刻。我的处理原则是非必要不更新只有在厂商明确发布了修复特定问题的固件、且这个问题确实影响交付时才纳入更新范围并且必须先在单台机器上完整走一遍全流程再放量。4. 依赖顺序和组合升级编排错了会白刷4.1 为什么顺序错了会刷不进去固件之间是有依赖的。举个最典型的例子新版 BIOS 可能依赖某个最低版本的 BMC 才能正确上报传感器数据或者新版 BMC 依赖某个 CPLD 版本提供的上电时序逻辑。你先把 BIOS 刷了BMC 还没跟上就会出现BIOS 版本对了但风扇狂转、温度读数异常这种半吊子状态。还有一类更隐蔽的同一器件的不同分区有先后要求。比如某 RAID 卡要求先更新 NVRAM 部分再更新主固件反过来做会导致参数区被覆盖、阵列配置丢失。这类要求通常写在厂商的 release notes 里而 release notes 恰恰是大多数人不会去读的东西。我总结的经验顺序是这样的注意这只是通用参考具体以厂商依赖矩阵为准先确认当前所有器件版本形成基线快照。CPLD / FPGA 优先它管上电时序必须先到位。BMC 第二它是后续所有带外操作的通道。BIOS 及随附的 ME/PSP。存储控制器RAID/HBA固件与 Option ROM。硬盘固件NVMe 可以先做因为能热激活。网卡固件。PSU、背板、GPU 等边缘器件。每完成一阶段做一次 AC 断电然后读回验证。4.2 编排脚本的骨架长什么样手工一台台点是不现实的尤其是几百台的批量场景。脚本骨架大概是这个形态#!/bin/bash set -euo pipefail FW_ROOT/opt/fw LOG_DIR/var/log/fwupdate/$(date %F_%H%M%S) mkdir -p $LOG_DIR log() { echo [$(date %T)] $* | tee -a $LOG_DIR/main.log; } # 阶段一盘点输出当前版本快照 inventory() { dmidecode -t 0 $LOG_DIR/bios_before.txt dmidecode -t 1 $LOG_DIR/sysinfo.txt ipmitool mc info $LOG_DIR/bmc_before.txt || true ethtool -i eth0 $LOG_DIR/nic_before.txt || true nvme list $LOG_DIR/nvme_before.txt || true } # 阶段二前置检查任何一项不满足直接退出 precheck() { [ $(id -u) -eq 0 ] || { log 需要 root; exit 1; } df -h /tmp | awk NR2 {exit ($40 512000) ? 0 : 1} || { log /tmp 空间不足; exit 1; } # 双电源在线检查示例字段名以实际机型为准 systemctl is-active kdump /dev/null 21 log 注意kdump 已启用占用内存策略需确认 } # 阶段三分器件更新每个器件一个函数便于单独重跑 stage_bios() { log 开始更新 BIOS $FW_ROOT/bios/BIOS_XXXX.BIN -q -f $LOG_DIR/bios_update.log 21 } # 阶段四读回比对 verify() { dmidecode -t 0 $LOG_DIR/bios_after.txt diff (grep -i version $LOG_DIR/bios_before.txt) \ (grep -i version $LOG_DIR/bios_after.txt) || log BIOS 版本已变化 }这个骨架的关键设计是每个器件独立成一个函数。批量刷机最怕的就是中途失败要全流程重来拆成函数之后哪一步挂了单独重跑那一步就行日志也是按器件分开存的事后排查方便。4.3 批量放量的节奏和回滚点批量场景下我坚持三档放量第一档一台跑完全流程包括 AC 断电验证第二档一个机架大约 10 到 20 台观察 24 小时第三档才全量。这个节奏看起来慢但比起全量刷完发现某个固件版本有兼容问题再回滚省下的时间多得多。回滚这件事要提前想清楚大部分服务器固件不支持降级或者降级需要额外的开关参数、专用工具。所以放量之前必须记录每一台、每一个器件的原始版本形成一张回滚对照表。真出问题时至少知道退回到哪个版本是可行目标而不是一团乱麻。5. 刷完之后怎么证明真的成功5.1 三层校验缺一层都不算数第一层是版本读回。这一层的坑在于读的地方不对——操作系统侧读到的和 BMC 侧读到的可能不一致尤其是网卡、RAID 卡这类器件。我的做法是两侧都读不一致就当成异常处理。第二层是日志校验。要看三份更新工具自己的日志、BMC 的系统事件日志SEL、操作系统的内核日志dmesg。任何一条跟固件校验、镜像切换、复位相关的告警都值得停下来看一眼。特别是 SEL 里如果有固件更新失败、已回滚这类条目而工具返回码是 0那就说明工具根本没检测到回滚。第三层是功能回归。这一层最容易被省掉但恰恰最重要。网卡要看链路速率和错误计数存储要看 RAID 状态和重建进度散热要看风扇转速曲线和温度是否在正常区间电源要看双路输入是否都正常上报。我一般会固化一组脚本更新前后各跑一次做 diff。5.2 异常路径比正常路径更值得测更新固件测试真正的价值在于异常场景。我固定会做这几个更新中拔电在更新进度 50% 左右直接断掉 AC然后上电看能否正常回滚到旧版本或者进入恢复模式。这个测试有条件就在实验室做别在交付现场做。重复升级同一版本连刷三次看工具是否正确识别已经是目标版本并跳过还是傻乎乎地重复写 flash。重复写有损耗风险。跨大版本升级跳过中间版本直接升到最新看是否存在依赖缺失。双电源先后断电更新完成后先断一路电稳定运行再恢复再断另一路观察是否触发任何异常告警。连续 AC cycle更新后连续做 5 到 10 次 AC 断电上电看版本是否稳定保持、有没有偶发回退。5.3 万一刷砖了恢复路径在哪这是必须提前准备的。常见的几条路一是双镜像/golden image 自动回退。大部分 BMC 和部分 BIOS 有主备两份镜像主镜像校验失败会自动从备份启动。做测试时要确认这个机制真的能触发而不是只在文档里写着。二是带外强制恢复。BMC 挂了但主板上的管理芯片可能还活着通过 Redfish 或者厂商专用恢复流程可以强制刷回。三是物理接触恢复。有些器件刷砖后只能靠跳线、串口或者专用的恢复接口这需要现场有人能接触到机器。批量部署的场景里这一条往往是最不现实的所以宁可前期多测也不要指望后期恢复。6. 那些只有踩过才知道的坑6.1 复位类型选错版本看着更新了其实没生效这个坑我踩过不止一次。更新工具返回成功读回版本也是新的但整机功能表现还是旧版本的行为。原因就是 ME 或者某块随附固件没真正重新加载。后来我把流程改成更新完成后必须做一次完整 AC 断电断电时长不少于 30 秒然后重新读回全部版本这类问题才彻底消失。判断方法很简单把 AC 断电前后的版本快照都留档如果断电后某个器件版本又变回去了说明那次更新根本没落盘。6.2 工具版本太老认不出新固件包固件包和更新工具是有代际关系的。老版本的更新工具遇到新格式的包表现往往是模糊的——可能报unsupported image也可能静默跳过什么也不做。排查的时候先把工具升到厂商当前推荐版本再重试很多时候问题就没了。跨大版本升级的时候尤其要注意有时候需要先把工具和中间版本固件升到某个基线才能跳到最新版本。厂商的升级路径文档里通常有一张箭头图认真看一遍能省掉很多来回。6.3 权限、内核模块、加固策略把工具挡住了这一类问题在安全加固过的系统上特别常见。表现是工具报错信息含糊比如无法访问设备。实际原因可能是SELinux 处于 enforcing 状态拦住了工具对/dev下设备节点的写操作。缺少某个内核模块工具依赖的字符设备节点根本不存在。/tmp被挂成了noexec更新包解压出来的临时可执行文件跑不起来。/tmp空间不足几十兆的固件写入中途失败。排查顺序我一般是从dmesg和工具自己的 verbose 模式切入把报错原文拿到再判断。测试环境如果是按安全基线加固过的最好在流程里单列一条更新固件前临时调整安全策略更新后恢复的步骤并且留下审计记录。6.4 时间基准不一致事后对不上时间线这个坑在多人协作排查时特别致命。更新工具日志用的是本地时间BMC 的 SEL 用的是 UTC操作系统日志又可能是另一个时区。三份日志放一起时间线条目完全错位根本判断不出到底是先失败再回滚还是先回滚再失败。解决办法是在流程开始前先对齐时间确认 NTP 同步状态、把待收集的日志时区统一标注出来、最好在脚本里对每条日志行显式打上 UTC 时间戳。这个动作花不了几分钟但排查的时候能救命。6.5 并发更新抢资源出现莫名其妙的中断批量脚本很容易写成所有器件并行刷结果就是多个更新工具同时抢 flash 控制器或者 BMC 通道出现莫名其妙的超时。我的做法是把同一台机器上的更新严格串行机器之间并行。这样既保证了速度又避免了单机内部的资源冲突。最后分享一个我自己一直在用的小技巧每次更新前先把所有器件的版本快照和一个简单的整机健康检查输出打成一个归档包命名带上机型和序列号。更新完再打一个包。这两个包放在一起就是这台机器这台次更新最完整的证据链。真出了争议翻这两个包比什么都管用。