📌 转载说明:本文首发于微信公众号《闫工的算力工具箱》 原文链接:https://mp.weixin.qq.com/s/1LodZCcde80ABg-kOEzyhA
【闫工的工具箱 · 第 28 弹|算力交付】#
上个月到货 8 台 DGX H 系列,交付流程我最怕的不是插线,是初始化。
说直白点,机器上架之后、交给客户之前,每台都得过一遍初始化。核固件版本、改 BIOS 参数、调启动顺序到 PXE、装完系统再翻一遍 SEL 日志看有没有硬件告警——这些操作全在 BMC 网页里。BMC 是服务器主板上独立的一块小管理芯片,有自己网口和 IP,只要通电就能登,跟主机开没开机没关系。
一台登进去点一遍,二十分钟。8 台就是大半个工作日,一台台点下来的整批。
真正让我下决心换方法的还不是慢,是不一致。手点的时候人是会飘的:改到第 3 台,某个下拉框忘了翻;第 6 台,某个参数以为上一台已经改过。8 台跑完,我拿不出任何东西证明它们参数一样。
那天 8 台机器点完,我拿配置出来比对,发现两台 BIOS 的 SEL 处理策略没改到。那一刻我就知道,这套活不能再手点了。那之后我决定换成 Redfish API。第一次试的时候,光搞清楚"资源树从哪进、端点怎么找"就花了半天。 这篇就把整个替换过程和踩的坑写清楚。

图:一个入口五棵子树,想干什么先定位到哪棵
适用机型:DGX H 系列(BMC 与 SBIOS 出厂即启用 Redfish) 本文覆盖:资源树定位、主机与 BMC 的内网通路、五类交付动作的 curl 实现、etag 与翻页处理、踩坑清单
— — —
先说清楚:Redfish 凭什么能替掉网页#
第 11 弹我用 IPMI 批量改过 100 台 BMC 的密码100 台 BMC 改密码,从半天缩到 20 分钟:一个脚本搞定批量改密 + 建监控账号,但留言区好几个大佬提醒我:带外管理还有一条更宽的路——Redfish,IPMI 碰不到的东西它都能碰。这次到货 8 台 DGX,正好上手试了试。
Redfish 是 DMTF 定的标准,走 HTTPS + JSON,交付阶段真正要改的 BIOS 参数、启动顺序、固件,它全能碰。两者一比就清楚了:
| IPMI / ipmitool | Redfish | |
|---|---|---|
| 协议 | RMCP+ 二进制 | HTTPS + JSON |
| 能干的活 | 电源、传感器、SEL、账号 | 上述全部,外加 BIOS 设置、启动顺序、固件更新、虚拟媒体、配置备份、遥测、功率封顶 |
| 输出可读性 | 命令行参数,得记 | JSON,看一眼就懂 |
| 自动化 | 可以,但每个厂商参数不一致 | 标准端点,curl 直接调 |
关键差别就一行:交付要动的 BIOS、启动顺序、固件,只有 Redfish 能碰。
而且端点是标准的:所有请求都从 https://<bmc-ip>/redfish/v1 往下找,想知道"改启动顺序该打哪个地址",看一眼资源树就行。
还有一条对交付场景很实用:主机和 BMC 之间有一条内部网络通路。你可以在主机上配一个 169.254.0.0/16 网段的接口(BMC 侧预置 169.254.0.17),这样脚本不用绕到管理网、也不用把 BMC 暴露在业务网里:
sudo ifconfig <主机侧网卡> 169.254.0.18 netmask 255.255.0.0配完之后 curl、nvfwupd 直接打 169.254.0.17 就行:
nvfwupd -t ip=169.254.0.17 username=<bmc-user> password=<password> show_version— — —
交付要做的五件事,逐个 curl 化#

图:五步都能脚本化,人只管换线和上电
后面的命令里,<bmc-user>、<password>、<bmc-ip> 三个占位符按现场替换。-k 是跳过证书校验,BMC 用的自签证书,不加会直接失败。
一、核固件清单#
交付第一步是核固件,不是刷固件。 机型 SOP 上写死了各组件该是什么版本,先对表,对不上的才动手。一上来就刷,刷错版本反而添乱。
curl -k -u <bmc-user>:<password> --request GET \
'https://<bmc-ip>/redfish/v1/UpdateService/FirmwareInventory'返回的是一个集合,DGX H 系列上一眼能看到 Members@odata.count 是 66 上下。每一项是 CPLDMB_0、HostBMC_0、PSU_0 这类组件名,版本号在 Oem.Ami.FirmwareInventory 里成对给出。
需要升级时,按组件分两类目标:
| 要更新的部分 | target 怎么写 | 固件包 |
|---|---|---|
| GPU 托盘(整套 GPU 托盘组件) | HGX_0 | nvfw_HGX_DGXH100-H200x8_xxxxxx.x.x.fwpkg |
| 主板托盘(BMC / BIOS / CPLD / PSU / PCIe Switch 等) | HostBMC_0 、HostBIOS_0、CPLDMB_0 等 | nvfw_DGX_xxxxxx.x.x.fwpkg |
上传和刷写:
echo "{\"Targets\":[\"/redfish/v1/UpdateService/FirmwareInventory/HostBMC_0\"]}" > parameters.json
curl -k -u <bmc-user>:<password> -H 'Expect:' --location --request POST \
https://<bmc-ip>/redfish/v1/UpdateService/upload \
-F 'UpdateParameters=@parameters.json;type=application/json' \
-F UpdateFile=@<fw_bundle>那个 -H 'Expect:' 不是可有可无的。不加的时候 curl 会先发一个 Expect: 100-continue 探路,部分 BMC 固件对这个处理得不好,表现是卡住不动、也不报错。我第一次刷固件在那儿等了十分钟,以为是包太大。
另外固件默认只升不降(版本必须高于当前值才写)。要强行覆盖,先改这个开关:
curl -k -u <bmc-user>:<password> --request PATCH 'https://<bmc-ip>/redfish/v1/UpdateService' \
--header 'If-Match: *' --header 'Content-Type: application/json' \
--data-raw '{"HttpPushUriOptions" : {"ForceUpdate": true}}'改动成功返回 204;如果你设的值和当前值一样,会返回 400。以前核固件要一台台登进去对,现在一条 curl 把 66 个组件的版本全拉下来。
二、改 BIOS 设置#
BIOS 参数不是想改就能改。属性名写错一个字母,BMC 不一定报错,可能就静静忽略了。 所以第一步不是改,是先把这台机器支持的属性清单拉下来。三跳:
# 1) 列出所有注册表,找到 BIOS 属性注册表
curl -k -u <bmc-user>:<password> --location --request GET 'https://<bmc-ip>/redfish/v1/Registries'
# 2) 拿注册表 URI(名字形如 BiosAttributeRegistry106.1.0.6)
curl -k -u <bmc-user>:<password> --location --request GET \
'https://<bmc-ip>/redfish/v1/Registries/BiosAttributeRegistry106.1.0.6/'
# 3) 把属性定义 JSON 拉下来
curl -k -u <bmc-user>:<password> --location --request GET \
'https://<bmc-ip>/redfish/v1/Registries/BiosAttributeRegistry106.en-US.1.0.6.json' \
--output BiosAttributeRegistry106.en-US.1.0.6.json这份 JSON 是属性字典:每个属性名对应默认值、显示名、帮助文本、是否只读、是否需要重启才生效。改参数前先在这儿确认属性名拼写,写错了 BMC 不一定报错,可能就静静忽略了。
看当前生效值:
curl -k -u <bmc-user>:<password> --location --request GET 'https://<bmc-ip>/redfish/v1/Systems/DGX/Bios'写"下次启动生效"的值,PATCH 到 Bios/SD:
curl -k -u <bmc-user>:<password> --location --request PATCH \
'https://<bmc-ip>/redfish/v1/Systems/DGX/Bios/SD' \
-H 'Content-Type: application/json' -H 'If-Match:*' \
--data-raw '{"Attributes" : {"IPMI002":"IPMI002DoNothing", "IPMI201":"IPMI201Donotloganymore"}}'上面这两个属性是 SEL 日志满的时候系统怎么办。IPMI002 是写满之后的动作,IPMI201 是满了还继续记录与否。交付时按客户要求选,不选的话默认行为可能是停止记录,之后出问题就没日志可查了。一次 PATCH 可以带多个属性。
⚠️ BIOS 属性的改动,全部需要一次断电再上电(power cycle)才生效。 而且如果你改完属性紧接着又刷了 BIOS 固件,还得再来一次 power cycle。这个坑我踩过——改完直接重启,进系统一看参数没变,回头怀疑了半天是不是权限问题。
以前改参数靠记忆,现在属性名从注册表里查,错了也有记录。
三、改启动顺序#
启动顺序不对,PXE 装机流程接不上,机器就躺在机柜里等。这一步必做,也最容易写错,因为它要先读再写。
先读当前顺序:
curl -k -u <bmc-user>:<password> \
https://<bmc-ip>/redfish/v1/Systems/DGX/SD \
-H "content-type:application/json" -X GET -s | jq .Boot.BootOrder拿到一串 Boot0000、Boot000F 这样的编号。光看编号不知道谁是谁,得再查一遍:
curl -k -u <bmc-user>:<password> \
https://<bmc-ip>/redfish/v1/Systems/DGX/BootOptions/00{0,1}{0,4,5,6,7,8,9,A,F} \
-H "content-type:application/json" -X GET -s | jq | grep -e "UefiDevicePath\|Name"这里用了个 shell 的大括号展开,一次把 Boot0000、Boot0001、Boot0004 一直到 Boot0010 全查了,不用写循环。
返回里三个字段有用:
DisplayName 是设备的可读名字,比如 UEFI: PXE IPv4 Nvidia Network Adapter - 00:AA:BB:00:00:01、DGX OSName 是对应的启动项编号UefiDevicePath 里的 MAC(<地址>,0x1) 就是这块网口的 MAC,多个口名字长得一样的时候,靠这个区分
确认好顺序之后写回去:
curl -k -u <bmc-user>:<password> \
https://<bmc-ip>/redfish/v1/Systems/DGX/SD \
-H "content-type:application/json" \
-H 'If-Match: "<etag>"' \
--data '{"Boot":{"BootOrder": ["Boot0004","Boot0000","Boot0005","Boot0006","Boot0007","Boot0008","Boot0009","Boot000A","Boot000F","Boot0010"]}}' \
-X PATCH数组是全量覆盖,不是增量。没写进数组的启动项不会自动保留在末尾,所以平时习惯的做法是:把读回来的完整列表拿出来,只把要提前的那一项挪到最前面,其余原样跟上去。
写完再读一遍确认:
curl -k -u <bmc-user>:<password> \
https://<bmc-ip>/redfish/v1/Systems/DGX/SD \
-H "content-type:application/json" -X GET -s | jq .Boot.BootOrder这个顺序会一直保留到下次被改写,或者 SBIOS 被重置。以前要登网页翻菜单,现在读出来、改、写回去,一套动作三行命令。
四、电源循环#
改完 BIOS、改完启动顺序,别急着看结果——那些值都还只是"下次启动生效",没断电重上电,根本不落地。你进系统一看还是老样子,会怀疑自己白改了半天。
Redfish 的电源操作全在同一组端点上,差别只在 ResetType:
| 想要的效果 | ResetType |
|---|---|
| 开机 | On |
| 直接断电 | ForceOff |
| 软关机 | GracefulShutdown |
| 直接重启 | ForceRestart |
| 软重启 | GracefulRestart |
| 断电再上电 | PowerCycle |
curl -k -u <bmc-user>:<password> --request POST \
--location 'https://<bmc-ip>/redfish/v1/Systems/DGX/Actions/ComputerSystem.Reset' \
--header 'Content-Type: application/json' \
--data '{ "ResetType": "PowerCycle" }'⚠️ 交付场景里我要的是 PowerCycle 不是 ForceRestart。改 BIOS 参数这类操作,重启(restart)不算数,要的是断电再上电——有些参数只在完整的上电初始化流程里才被读进去。
另外注意:管理板(HMC)上不支持 ForceRestart、GracefulRestart、GracefulShutdown 这三种,别拿同一套脚本无脑刷。这一步是让前面所有改动真正落地,不做,前面全是白改。
五、收口核查#
前面四步都在写,最后一步得扫一遍,确认没写坏什么。机器交付出去才发现 SEL 里躺着一条 DIMM 告警,那才叫丢人。
curl -k -u <bmc-user>:<password> --location --request GET \
'https://<bmc-ip>/redfish/v1/Managers/BMC/LogServices/SEL/Entries'curl -k -u <bmc-user>:<password> --location --request GET \
'https://<bmc-ip>/redfish/v1/Chassis/DGX/Sensors'这两个端点都要翻页,而且不会提醒你。 SEL 和 Chassis/DGX/Sensors 每次都只返回 75 个成员,剩下的靠 Members@odata.nextLink 给地址。我第一次拉 SEL,看到一页就觉得"日志挺干净",后来加上翻页才看到后面还有一条 DIMM 相关的告警。
翻页就是顺着 nextLink 里的 $skip 往下走:
curl -k -u <bmc-user>:<password> --request GET \
'https://<bmc-ip>/redfish/v1/Managers/BMC/LogServices/SEL/Entries?$skip=75'批量交付的时候,这几步串起来就是一个循环:读清单文件里的 BMC IP,对每台依次跑上面五步,把每台的返回按 IP 存成独立文件。哪台失败、失败在哪一步,都在文件名和日志里。以前靠人眼翻网页,现在一条 curl 扫完,有告警一目了然——这才扛得住交付的体量。
— — —
我踩过的坑(按踩到的时间顺序)#
1. 第一次上手,资源树从哪进都不知道。 所有请求都从 https://<bmc-ip>/redfish/v1 往下找,但机型路径里的 DGX 是固定的,不是变量——写成 Systems/1 之类会直接 404。第一回我在这儿绕了半天,以为是自己拼错了 URL。
2. 改完 BIOS 不 power cycle,等于没改。 改完就重启,进系统一看参数没变,我回头怀疑了半天是不是权限问题。判断标准很简单:Bios 端点返回当前值,Bios/SD 返回"下次启动生效"的值,两个对不上就说明还没落地。
3. -H 'Expect:' 刷固件时必须加。 不加就卡住,而且不报错。第一次刷固件我卡了十分钟,以为是固件包太大,其实是 curl 那个 Expect: 100-continue 探路把部分 BMC 卡住了。这类"静默失败"比报错难查得多。
4. SEL 和 Sensors 会静默截断。 75 条一页,不翻页就以为看全了。我第一次拉 SEL,看到一页就觉得"日志挺干净",后来加翻页才看到后面还有一条 DIMM 告警。判断依据是返回值里有没有 nextLink:有就继续拉。
5. 写操作尽量带 etag。 8 台并行跑脚本的时候才暴露出来:PATCH 带 If-Match(有的固件文档里写成 if-None-Match,以实际返回为准),etag 对不上会返回 412。这不是麻烦,是保护——防止两个进程同时改同一台。
6. 别把写操作并发化。 BMC 只有一颗,8 个 PATCH 同时打过去,轻则返回 500,重则把 BMC 搞到要重启,差点挂掉。我的做法是读可以并发、写串行,一台改完确认了再下一台。
— — —
用了这套东西,省了哪些事?#
- 参数可对账:每台机器的固件版本、BIOS 当前值、启动顺序都能拉成文本,交付记录里附一份,比"我点过了"有说服力
- 一致性有保障:同一段脚本跑 8 次,参数不会因为人飘而漂移
- 不用登网页:机器还没上架、还在机柜走线阶段就能操作,只要 BMC 通了电和网
- 可回滚:配置文件写前先 GET 存档,改坏了能按原值写回,出了岔子也兜得下
- 能接进交付流水线:跟 PXE 装机、配置下发串成一个脚本,一条命令批量下发
— — —
结尾#
你们交付 GPU 服务器的时候,BIOS 参数和启动顺序是怎么改的?还在登 BMC 网页点,还是也脚本化了?如果已经脚本化,是用 Redfish 还是厂商自己的工具?评论区聊聊。