简介:《BlueField DPU Solutions 202104.pdf》是一份面向数据中心架构师、云计算与网络工程师的NVIDIA BlueField-2 DPU技术资料,聚焦通过DPU卸载CPU基础设施负担、实现软件定义硬件加速的解决方案。内容围绕DPU架构、安全加速、存储加速、网络加速以及裸金属云与生态合作展开,涉及Arm核与加速引擎、IPsec与正则及视频等专用处理、NVMe-oF与vSAN场景,也讨论DOCA SDK与VMware Project Monterey等生态实践,适合用于方案调研、技术选型与架构设计参考。资源包共1个PDF文件,约4.7MB,便于集中查阅与归档。已有807人学习下载,读者可从中获得BlueField-2的模块化知识框架、典型应用场景与加速能力对比,对理解DPU在裸金属和虚拟化环境中的落地价值较有帮助。
1. 一块卡替掉 125 个 x86 核:BlueField-2 DPU 到底解决什么问题
把服务器上那块网卡拔下来,换成一块外观、功耗、插槽都一样,但自带 8 个 Arm 核、独立操作系统和一堆加速引擎的卡,然后把原本压在主机 CPU 上的防火墙、加解密、存储网关、虚拟交换统统挪上去——这就是 BlueField-2 DPU 最直白的用法。这份 2021 年 4 月的解决方案文档覆盖面比较全:硬件规格、安全加速、存储加速、网络加速、裸金属与虚拟化场景,以及围绕 DOCA 的软件栈,基本都给了参数和框图。它更像一份选型与落地参考,而不是入门教程。适合三类人看:正在评估智能网卡能不能吃掉一部分 x86 预算的基础设施工程师;要做裸金属云网络/存储卸载的云平台开发;以及手上已经有卡、却一直当普通 200G 网卡在用的人。
2. 拆开硬件账本:8 核 Arm、PCIe Gen4 交换与 200G 端口怎么读
文档里那张规格页信息密度很高,但很多人扫一眼只记住了「6.9B 晶体管」和「替代 125 个 x86 核」两个数字,然后就去下单了。真正决定这块卡能不能进你机房的,是另外几行:8 个 64 位 Arm A72 核的 Tile 结构、集成 PCIe Gen4 交换机的 bifurcation 能力、200G 端口的调制方式,以及 1GbE 带外管理口是否单独布线。这一章按「哪些是网卡本来就有的、哪些是 DPU 新增的、上车前怎么核对」三段拆。
2.1 从 ConnectX-6 Dx 到 DPU:先分清哪些能力不是白来的
BlueField-2 的高速通路部分是建立在 ConnectX-6 Dx 之上的,这意味着 200Gbps 以太网与 InfiniBand、RoCE、IPsec/RegEx 加速引擎这些东西,在纯网卡形态下就已经存在。DPU 额外加的是三样:Arm 子系统(能跑完整 Linux)、集成 PCIe switch(能当 Root Complex 也能当 End Point)、以及把这些能力用 DOCA 串起来的软件栈。
这个区分很重要,因为它直接决定选型逻辑。如果你的需求只是「IPsec 卸载 + 线速转发」,一块 ConnectX-6 Dx 就够,不必为 Arm 核和内存付溢价,也不必处理 Arm 侧的固件、内核、容器这套运维负担。反过来说,只要出现下面任意一条,就该考虑 DPU 形态:
- 需要在不改主机内核、不装主机驱动的前提下,把网络或存储功能做成「基础设施服务」;
- 需要一块与控制面隔离的执行环境(比如安全策略、微隔离代理),主机被攻破也不影响策略执行;
- 需要在裸金属服务器上提供本地盘幻觉,也就是把远端 NVMe-oF 存储伪装成本地盘给租户;
- 需要把主机 CPU 上的 vSwitch、NAT、负载均衡卸载掉,给业务腾出核。
文档里那组「2.5X / 8X / 15X / 30X / 50X / 150X」的等效核数对比,说的是同等负载下需要多少 x86 核才能顶上一块 DPU,其中云 overlay 网络那一栏是 150X,弹性块存储是 2.5X。这组数字的正确读法是「量级参考」,不是承诺值:不同报文大小、不同流表规模、不同协议组合下差距非常大。云 overlay 那一栏之所以夸张,是因为它比的是软件 vSwitch 在小包多流场景下的表现,属于 DPU 最擅长的区间;而弹性块存储只有 2.5X,是因为块存储路径本身还有网络和后端阵列的瓶颈,不是单靠卸载就能线性提升。看到这种表,第一反应应该是「他在什么条件下测的」,而不是「我能省 150 个核」。
2.2 八个 A72 核的 Tile 结构与内存层级:绑核策略从哪来
文档里写得很具体:8 个 Arm A72 核分布在 4 个 Tile 上,每 Tile 2 核,8MB L2、6MB L3,主频最高 2.5GHz。这句话拆开看有三个落地含义。
第一,核是按 Tile 分组的,同一 Tile 内的两个核共享 L2,跨 Tile 访问要走片上互联,延迟和带宽都不一样。所以凡是做 DPDK 轮询或者高包速率转发的场景,lcore 分配要尽量成对落在同一 Tile 内,不要把一个 flow 的收发包线程拆到两个 Tile 上。判断方法很土但有效:跑一遍lscpu -e看 topology,再跑一遍你自己的工作负载,把线程绑定前后各测一次 pps,差距超过 10% 就说明绑错了。
第二,2.5GHz 的 A72 单核性能大致相当于同年代 x86 的一小部分,所以别指望在 Arm 侧跑重计算的业务逻辑。它的定位是「控制面 + 轻量数据面」:流表管理、策略下发、健康检查、协议解析、少量状态的转发,这些合适;压缩、正则、加解密这种重活交给专用引擎(文档给的数字是 100Gbps IPsec、50Gbps RegEx、100Gbps 视频流、5M NVMe IOPS)。
第三,内存是硬约束。Arm 侧可用内存取决于具体 SKU 和固件配置,跑容器化服务时很容易被几个镜像撑满。我一般的做法是先在 Arm 侧跑free -m和df -h把真实可用量记下来,再倒推能放几个服务,而不是照着文档框图规划。
2.3 用 lspci 和 mlxconfig 把纸面参数核对成实际配置
纸面参数和到手实物之间经常有差距:可能是固件版本不同,可能是出厂配置是纯网卡模式,也可能是 PCIe 链路协商到了低带宽。上手第一件事不是装驱动,是核对。下面这段脚本按顺序采集硬件身份、固件、PCIe 链路和当前模式,输出一份可直接贴进工单的基线记录。
#!/usr/bin/env bash # 用途:BlueField-2 上电后第一轮硬件核对,只读,不改任何配置 set -euo pipefail # 1. 找到设备:BlueField 在 lspci 里通常显示为 Ethernet controller / Infiniband controller lspci -nnD | grep -i -E "mellanox|bluefield" || echo "未发现设备,先检查插槽与供电" # 2. 记录 PCIe 链路速率与宽度,确认是 Gen4 x16 而不是降速到 Gen3 x8 for bdf in $(lspci -D | grep -i mellanox | awk '{print $1}'); do echo "=== ${bdf} ===" lspci -vv -s "${bdf}" | grep -E "LnkCap:|LnkSta:" done # 3. 启用 mst 工具栈,便于后续用 mlxconfig / flint 操作 sudo mst start sudo mst status -v # 4. 查固件版本与设备型号,确认 SKU 与采购单一致 for dev in $(sudo mst status -v | awk '/\/dev\/mst/{print $2}'); do echo "--- ${dev} ---" sudo flint -d "${dev}" q | grep -E "Image type|FW Version|Product Version|PSID" done # 5. 查关键配置项:Arm 子系统模式、SR-IOV、SNAP 相关开关 # 注意:不同固件暴露的键名可能略有差异,先用 query 看全量再决定改什么 sudo mlxconfig -d /dev/mst/mt41692_pciconf0 query | \ grep -i -E "internal_cpu|sriov|num_of_vfs|snap|exp_rom" || true这段脚本的每一步都有明确的失败含义。第一步查不到设备,问题在物理层(插槽、供电、BIOS 里的 PCIe 枚举),不用往下走。第二步的LnkSta如果显示Speed 8GT/s Width x8,说明协商降级了,常见原因是主板 PCIe 插槽共享、转接卡质量差、或者 BIOS 里该插槽被限速,这时候测出来的 200G 会变成 100G 以内,后面所有性能数字都不可信。第三步的mst start是把设备挂到一个可管理的字符设备上,后面mlxconfig、flint、mlxfwreset都依赖它;如果这步报权限或内核模块问题,先确认mst内核模块与当前内核版本匹配。第四步查固件,是为了确认 Arm 侧能不能跑起来——老固件可能缺少 DPU 模式的完整支持。第五步只查不改:INTERNAL_CPU_MODEL这个键决定 Arm 侧上电方式以及主机侧能否看到这块卡,不同固件的取值含义不完全一致,务必先看query输出里的 help 列,再对照厂商文档改,改错的表现是主机直接看不到设备,需要回到带外管理口或者物理重刷。
顺带说一个容易忽略的点:文档里提到 1GbE 带外管理口。这个口不是可选项,在多卡部署里它是你唯一的后悔药。Arm 侧系统跑挂了、SSH 进不去、rshim 又没配好,就只能靠这个口或者串口。上线前把它接进独立的管理网段,配好固定 IP,成本极低。
3. DOCA 与三种运行模式:卡是网卡、是主机、还是旁挂加速器
拿到卡之后第一个岔路口是模式选择。同一个硬件,可以表现为一块高性能网卡,也可以表现为一台能被 SSH 登录的小型 Arm 服务器,还可以表现为跟主机分离、走独立管理通道的加速卡。模式选错了,后面所有实验都会在莫名其妙的地方卡住:装了驱动看不到设备、容器起不来、主机侧认不出存储盘。这一章把三种模式的边界、DOCA 各组件的分工,以及第一次在 Arm 侧跑起容器的完整路径讲清楚。
3.1 DPU 模式与网卡模式:Arm 侧到底上不上电
按常见的配置方式,BlueField-2 大致有三种工作形态:
| 模式 | Arm 侧状态 | 主机侧能否看到 | 典型用途 |
|---|---|---|---|
| DPU 模式 | 上电,独立运行 | 只看到网卡功能与指定 PF/VF | 安全卸载、存储网关、裸金属云 |
| 分离主机模式 | 上电,通过 rshim 由主机灌入镜像 | 看到网卡 + rshim 通道 | 开发调试、自研 Arm 侧服务 |
| 纯网卡模式 | 不上电 | 只看到标准网卡 | 当 ConnectX-6 Dx 用 |
选型的判断标准通常就一句话:Arm 侧要不要跑你自己的东西。要跑,就必须让它上电并解决怎么装系统的问题;不跑,纯网卡模式最省事,故障面最小。
模式切换靠mlxconfig改INTERNAL_CPU_MODEL,改完必须做一次固件级复位(mlxfwreset)才生效,而且这个操作有掉设备风险:
# 切换模式前先记下当前值,出问题好回滚 sudo mlxconfig -d /dev/mst/mt41692_pciconf0 query | grep -i internal_cpu # 示例:改成 Arm 侧上电、由主机通过 rshim 引导的模式 # 取值含义以 query 输出的 help 为准,不同固件可能不同 sudo mlxconfig -d /dev/mst/mt41692_pciconf0 set INTERNAL_CPU_MODEL=1 # 必须复位生效;-y 跳过交互,生产环境先用不带 -y 的版本确认提示内容 sudo mlxfwreset -d /dev/mst/mt41692_pciconf0 reset -ymlxfwreset会重置整张卡,主机侧对应的网口、VF、bond 全部闪断,跑在 Arm 侧的虚拟机也会一起掉。血泪经验是:这一步千万别在远程 SSH 里对着承载管理流量或存储流量的那张卡做,链路一断就再也连不回来。要做就在带外控制台或者串口里做,或者先把管理流量切到另一块卡上。
复位之后如果主机侧彻底看不到设备了,多数情况下不是卡坏了,而是模式值和你的预期不一致,需要在 BIOS 里确认 PCIe 枚举正常,然后通过带外管理口登录 Arm 侧,或者用mst重新扫描。这也是前面强调 1GbE 管理口的原因。
3.2 DOCA 各组件的分工:别把 DPDK 当成万能钥匙
文档里给出的软件栈是「DOCA SDK + 开源 API(DPDK、SPDK、P4)+ 参考应用」。落到实际选型,各组件负责的边界大致是这样:
- DPDK:用户态报文处理,做 vSwitch、NAT、负载均衡、简单 ACL 这类包级操作。适合小包多流、需要确定性延迟的场景。
- SPDK:用户态存储栈,配合 NVMe-oF target/initiator,把远端盘暴露出去或者接进来。
- P4:把转发面丢给硬件流水线,适合流表规模大、需要线速匹配的场景,但可编程范围和调试手段比 DPDK 窄。
- SNAP:文档里的重点之一,NVMe SNAP / virtio-blk SNAP,把远端存储模拟成本地 NVMe 盘或 virtio 盘给主机用。
- ASAP2 / Crypto / RegEx:专用加速引擎的接口层,做加解密、正则匹配、压缩去重。
- Management / Telemetry:带外管理、遥测、固件升级通道。
新手最容易犯的错是「什么都想用 DPDK 写」。如果需求是「给主机提供一个看起来是本地的盘」,那是 SNAP 的活,用 DPDK 自己撸一遍 NVMe-oF initiator 纯属自找麻烦;如果需求是「线速匹配几万条规则」,优先看 P4 和硬件流表,别一上来就 DPDK 轮询把 8 个核吃满。
3.3 第一次在 Arm 侧跑起容器:从 rshim 到 docker
开发阶段最省事的路径是分离主机模式 + rshim:主机侧通过/dev/rshim0把镜像推给 Arm 侧,Arm 侧起来之后就是一个标准 Linux。下面这段是常见的操作顺序。
# 1. 主机侧确认 rshim 通道存在(需要先加载 rshim 驱动/服务) ls -l /dev/rshim0/ # 正常应能看到 boot、misc、DEV_NAME 等节点 # 2. 准备一个适配该平台的 Arm64 镜像,推到 boot 节点 # 文件名和格式要求随固件版本变化,务必对照对应版本的说明 sudo dd if=bfb-image.bin of=/dev/rshim0/boot bs=1M status=progress # 3. 观察 Arm 侧启动过程:可以从 misc 节点读日志,或直接接串口 # 串口参数一般是 115200 8N1,这是最可靠的排错手段 sudo screen /dev/ttyUSB0 115200 # 4. Arm 侧起来之后,确认内核、网卡、加速引擎都被识别 ssh ubuntu@<arm-side-ip> # 账号以镜像默认值为准 uname -a lspci -nnD | head -20 ls /dev/mst/ 2>/dev/null || echo "mst 未初始化,可能需要先 mst start" # 5. 在 Arm 侧确认容器运行时可用,并注意架构必须是 arm64 docker info | grep -i architecture docker run --rm --platform linux/arm64 arm64v8/alpine uname -m # 输出 aarch64 才说明镜像架构匹配这几步里,第 2 步的dd是最容易翻车的地方:写错设备节点、镜像架构不匹配、镜像与当前固件版本不兼容,表现都是「写进去了但起不来」,而且没有明显报错。可靠的判断方式是接串口看启动日志,卡在哪一步一目了然。第 5 步的--platform参数看起来多余,实际上非常关键——在 x86 上构建的镜像直接拿到 Arm 侧跑,会报exec format error,这个错有时候被上层脚本吞掉,只表现为容器反复重启,查半天查不到原因。
另外,Arm 侧的资源是有限的。8 个核看起来不少,但真跑起容器、监控代理、日志采集之后,留给数据面的就不多了。我的习惯是先把 Arm 侧的 CPU、内存、磁盘占用做成一个固定的采集脚本,每次上线新服务前后各跑一次,对比增量,避免服务一个个加上去最后把数据面挤死。
4. 安全与存储加速落地:IPsec/RegEx 引擎与 NVMe SNAP 的配置路径
文档里安全和存储两块篇幅最大,也是实际项目里最容易「PPT 上很美、落地时打折」的部分。安全加速的边界在于:能卸载的是有状态的、模式化的检查,不能卸载的是需要看应用语义的判断。存储加速的边界在于:SNAP 给的是「像本地盘」的体验,不是「等于本地盘」的性能。这一章把两条路径的具体配置和验证方法写清楚。
4.1 安全卸载能吃什么、不能吃什么
文档给的加速能力列得很清楚:100Gbps IPsec、50Gbps RegEx、有状态防火墙、深度包检测、主机内省、微隔离、硬件信任根、安全固件升级、Arm TrustZone 隔离。拆成可落地的三类:
能整段卸载的:IPsec 加解密、ESP 封装解封装、对称加密(AES-XTS 用于静态数据)、公钥运算加速。这类工作模式固定、上下文简单,交给引擎几乎无损耗,主机侧 CPU 占用能压到接近零。验证方法是主机侧跑iperf3打满流量同时mpstat看 CPU,卸载前后对比软中断占比。
能部分卸载的:有状态防火墙和 NAT。五元组匹配、连接跟踪、简单 ACL 可以下沉到硬件流表;但涉及应用层协议识别、需要重组报文再判断的规则,还是要回到 Arm 侧甚至主机侧处理。这块最常见的误判是「以为规则越多卸载越划算」,实际上规则集一旦超出硬件表容量,就会大量 miss 回落到软件路径,性能断崖式下跌。判断方法是压测时同步看硬件流表的命中计数,如果 miss 率随规则数线性上升,说明表已经满了。
不能卸载的:需要理解业务语义的东西。文档里提到的恶意模式匹配、深度包检测,靠的是 RegEx 引擎做特征串扫描,这属于「模式匹配」层面;至于这个匹配结果该不该拦截、属于哪个租户、走什么处置流程,仍然要控制面决策。把策略引擎放到 Arm 侧、把匹配动作放硬件,是这类方案比较常见的拆法,好处是策略更新不用动数据面。
隔离这块值得单独说一句。文档强调的「与控制面隔离」「信任根转移到 DPU」,本质是在说:安全代理跑在 Arm 侧,主机上的恶意程序即使拿到 root,也很难篡改卡上的策略和日志。这是 DPU 相对于「在主机内核里装 agent」最实质的优势。落地时要确认两件事:Arm 侧的系统盘和配置是否做了完整性保护(否则一样能被刷),以及固件升级通道是否走了签名校验(文档提到 secure FW upgrade 和 root-of-trust,需要在配置里真正开启,而不是默认就有)。
4.2 NVMe SNAP 与 virtio-blk SNAP:把远端盘变成「本地盘」
SNAP 解决的问题很具体:主机操作系统和上面的应用只认本地 NVMe 盘或 virtio 盘,但存储实际在远端。SNAP 在中间做了一层模拟——主机看到一个标准的 NVMe 控制器,读写请求被 DPU 截获,转成 NVMe-oF 或 RDMA 打到远端存储,返回后再还给主机。文档里列的远端协议包括 NVMe-oF、iSCSI、iSER、NFS、CEPH,覆盖范围比较宽。
这个机制的价值在裸金属云上体现得最明显:租户拿到一台物理机,以为自己有一块本地 NVMe,实际上是远端共享存储,可以随时扩容、迁移、快照,物理机本身不用装任何存储客户端。文档里「OS agnostic、支持legacy OS」这句话的含金量就在这里——老系统没有 NVMe-oF initiator,靠 SNAP 也能用上远端存储。
配置路径大致分四步,每步都有对应的检查点:
- 确认 SNAP 能力已启用:查
mlxconfig里与 SNAP 相关的键,以及 Arm 侧 SNAP 服务是否在跑; - 在 Arm 侧配置远端存储连接:target 地址、传输方式、命名空间映射;
- 把模拟出的盘挂给主机:通过配置把 emulated controller 暴露到指定 PF/VF;
- 主机侧验证:
lsblk、nvme list应该能看到一块「本地」盘,smartctl之类的工具可能行为异常,这属于正常现象。
第 4 步的「工具行为异常」是个高频困惑点。主机侧看到的是一块模拟盘,很多依赖真实硬件信息的工具(SMART、温度、固件版本查询)拿不到有意义的数据,甚至报错。这不是配置错误,是模拟层的固有特性。验收时不要用这类工具判断盘是否正常,用fio做读写、用dmesg看有没有 IO error 更靠谱。
4.3 用 fio 和抓包把性能数字验一遍
文档给了「5M NVMe IOPS」「dual 100Gbps or single 200Gbps」这类指标,验收时必须自己测一遍。下面这段跑三组对比:本地盘基线、SNAP 挂载后的盘、以及远端直连(如果有条件),差异才有意义。
# 4K 随机读,队列深度 32,跑 60 秒,输出 JSON 便于对比 # --iodepth 与 --numjobs 的乘积决定并发压力,按你的目标 IOPS 反推 fio --name=randread \ --filename=/dev/nvme1n1 \ --rw=randread --bs=4k --iodepth=32 --numjobs=4 \ --ioengine=libaio --direct=1 --time_based --runtime=60 \ --group_reporting --output-format=json --output=snap_randread.json # 顺序写,看带宽上限,块大小 1M 更贴近大文件/备份场景 fio --name=seqwrite \ --filename=/dev/nvme1n1 \ --rw=write --bs=1M --iodepth=16 --numjobs=1 \ --ioengine=libaio --direct=1 --time_based --runtime=60 \ --group_reporting --output-format=json --output=snap_seqwrite.json # 提取关键指标,避免被平均值的漂亮数字骗到 jq '.jobs[0].read.iops, .jobs[0].read.clat_ns.percentile["99.000000"]' snap_randread.json参数上有几个点值得说明。--direct=1绕开页缓存,否则测的是内存速度不是盘;--iodepth=32配--numjobs=4是常见的并发组合,如果你的业务是单线程低队列,把 iodepth 降到 1~4 才反映真实体验;--time_based --runtime=60保证跑满一分钟,避免小样本抖动;--output-format=json是为了把多次测试结果留存对比,而不是靠肉眼看屏幕。
比 IOPS 更该看的是 p99 延迟。SNAP 路径上多了一跳网络和处理,平均延迟可能只涨一点,但 p99 会明显抬高,这个差异在数据库类负载上非常致命。我一般会同时记录 avg 和 p99,两者差距超过 3 倍就要怀疑路径上有排队。
网络侧同理,用ib_write_bw或iperf3打流,同时用ethtool -S看丢包和 pause 帧计数,用mpstat -P ALL 1看主机侧 CPU 是否真的降下来了。如果 CPU 占用没降、带宽也没涨,说明加速路径没走通,请求还在软件栈里绕。
5. 裸金属与虚拟化场景避坑:五条踩过的坑和一套排查顺序
前面几章讲的都是「怎么配」,这一章讲「配完之后为什么不好使」。DPU 这类设备的排错难点在于链路太长:主机应用 → 主机驱动 → PCIe → DPU 固件 → Arm 侧服务 → 网络 → 远端存储/对端。任何一环出问题,表象都可能是「慢」或者「不通」。下面五条是我和同行踩过的、重复率最高的坑。
5.1 五条高频踩坑记录
坑一:主机侧完全看不到设备,lspci 是空的
现象:上电后lspci里没有 Mellanox/BlueField 设备,操作系统里也没有新网口。
原因:多数是模式切换后没做固件复位,或者INTERNAL_CPU_MODEL设成了主机侧不枚举的模式。少数情况是 BIOS 里 PCIe 插槽被禁用、Above 4G Decoding 没开、或者插槽与别的设备共享导致资源不够。
解决:先确认物理层(换插槽、看 BIOS 的 PCIe 枚举),再通过带外管理口或串口登录 Arm 侧确认卡是活的,最后用mlxconfig query核对模式值并mlxfwreset。整个过程不要在唯一的远程链路上做。
坑二:200G 网口实测只有 90G 出头
现象:链路起来了,ethtool显示 link up,但打流只有一半带宽。
原因:PCIe 链路协商降级。卡是 Gen4 x16,主板插槽实际跑在 Gen3 x8,理论上限就不够。另一种可能是端口配置成了单口 100G 拆分模式,另一半个口没接。
解决:lspci -vv看LnkSta,确认 Speed 和 Width;不对就查主板手册的插槽共享表和 BIOS 设置。拆分模式用mlxconfig查端口配置,确认没有误设。
坑三:Arm 侧镜像写进去了,但起不来,串口一片空白
现象:dd写入返回成功,但 Arm 侧毫无反应,/dev/rshim0/misc也没有日志。
原因:镜像与当前固件版本不匹配,或者镜像不是该平台的 BFB 格式;还有一种情况是上一次启动卡在半路,rshim 状态没清干净。
解决:接串口是最快的判断手段,115200 8N1,能看到 bootloader 就有救。看不到就检查镜像来源和格式,必要时通过带外管理口做恢复。写镜像前先核对版本兼容表,别用「看起来差不多」的镜像。
坑四:容器在 Arm 侧起不来,日志只有 exec format error
现象:docker run之后容器反复重启,docker logs里一行exec format error,或者干脆没有日志。
原因:镜像架构不对。在 x86 机器上docker build出来的镜像默认是 amd64,拿到 Arm 侧必然跑不了。
解决:构建时指定--platform linux/arm64,或者用 buildx 做多架构构建。上线前在 CI 里加一步docker inspect检查 Architecture 字段,比事后排查省事得多。
坑五:接了 SNAP,主机侧能看到盘,但一压测就超时
现象:lsblk能看到模拟出来的 NVMe 盘,小文件读写正常,fio一上并发就出现大量超时,dmesg里刷 IO error。
原因:远端存储连接数或队列深度不够;Arm 侧 SNAP 服务的线程绑核不合理,和转发面抢核;或者 MTU 不一致导致大包分片。
解决:先看 Arm 侧 SNAP 服务的 CPU 占用,如果某个核跑满就是算力瓶颈,调整绑核;再查端到端 MTU,ping -M do -s逐级探测;最后看远端 target 的队列配置。这三步按顺序做,大部分问题能定位。
5.2 一套固定的排查顺序:从下往上,别跳步
链路太长的时候,最忌讳「猜」。固定按下面顺序走,每步都有明确的通过/不通过判据:
- 物理与 PCIe:
lspci能看到、LnkSta是 Gen4 x16、无 AER 报错; - 固件与模式:
flint q版本符合要求、mlxconfig query里模式值符合预期; - Arm 侧系统:串口能登录、
uname -a正常、关键服务在跑; - 主机侧设备:网口、VF、模拟盘按预期出现;
- 数据面:小流量通、大流量不丢、延迟在阈值内;
- 业务侧:应用层指标正常。
任何一步不通过,就地停下来解决,不要带着问题往下走。经验是往下走只会让现象更混乱,比如 PCIe 降速没解决就去调 SNAP 参数,最后测出来的所有数字都是错的。
日志入口也固定下来:主机侧看dmesg | grep -i mlx和journalctl -u mst*;Arm 侧看dmesg、journalctl和 SNAP 服务自己的日志;固件级问题看mstflint的寄存器 dump。把这几个入口写进运维手册,比每次临时找要快得多。
提示:这套顺序里,第 1、2 步是唯一需要停机操作的环节。上线前把这两步的基线数据(lspci 输出、mlxconfig 全量 query、固件版本)存档,后续任何异常都可以先跟基线比一遍,能省掉大量猜测。
6. 把文档参数变成验收清单:三组基线和一个观测习惯
文档给了一堆漂亮的数字,但你真正需要的是「在我这套环境里,什么算正常」。我的做法是上线前跑三组基线,把它们写成表格存档,后续任何变更都跟这张表比。
6.1 三组基线怎么测
第一组:裸卡带宽与延迟。两台机器背靠背,用ib_write_bw或iperf3分别测 64B、512B、1500B、9000B 四档报文,记录带宽、pps、p99 延迟。小包看 pps 上限,大包看带宽上限,这两条线决定了这块卡在你环境里的真实天花板。测试时主机侧用mpstat -P ALL 1同步记录 CPU,记下「跑满带宽时占几个核」,这是后面衡量卸载收益的基准。
第二组:加解密卸载。跑 IPsec 隧道,对比开启硬件卸载前后的带宽和主机 CPU 占用。判据不是「带宽涨了多少」,而是「主机侧软中断占比降了多少」——卸载的价值是把核还给业务,不是把 100G 变成 120G。
第三组:SNAP 存储路径。4K 随机读、1M 顺序写、混合读写三种负载,各记录 IOPS、带宽、avg 和 p99 延迟。同时记录 Arm 侧 SNAP 服务的 CPU 占用,作为容量规划依据。
6.2 验收指标表
| 观测项 | 采集方式 | 正常判据 | 异常时先查 |
|---|---|---|---|
| PCIe 链路 | lspci -vv的 LnkSta | Gen4 x16,无降级 | 插槽共享、BIOS 设置 |
| 端口带宽 | ib_write_bw/iperf3 | 达到标称的 90% 以上 | 拆分模式、MTU、对端 |
| 小包 pps | 64B 打流 | 接近线速,无明显丢包 | 绑核、流表容量 |
| 加解密吞吐 | IPsec 隧道打流 | 接近引擎标称值 | 卸载开关、算法选择 |
| 主机 CPU 占用 | mpstat -P ALL 1 | 卸载后软中断明显下降 | 是否真走了硬件路径 |
| SNAP 随机读 IOPS | fio4K randread | 与基线差距在 20% 内 | Arm 侧 CPU、远端队列 |
| SNAP p99 延迟 | fioJSON 输出 | 不超过基线的 3 倍 | 路径排队、MTU |
| 固件与模式 | flint q+mlxconfig query | 与存档基线一致 | 是否被误改 |
这张表的价值在于把「感觉有点慢」变成「第 4 行不达标,去查卸载开关」。团队里换人接手时,这张表比任何文档都管用。
6.3 一个具体技巧:变更前先做只读快照
最后分享一个习惯。凡是动mlxconfig、mlxfwreset、rshim 写镜像这类操作之前,先跑一遍只读快照,把lspci -vv、mlxconfig query全量、flint q、ethtool -S、Arm 侧dmesg尾部 200 行全部落到一个带时间戳的目录里:
#!/usr/bin/env bash # 变更前快照,只读操作,可在生产环境安全执行 TS=$(date +%Y%m%d_%H%M%S) OUT="/var/log/bf2_snapshot/${TS}" mkdir -p "${OUT}" lspci -vv > "${OUT}/lspci.txt" 2>&1 sudo mst start >/dev/null 2>&1 for dev in $(sudo mst status -v | awk '/\/dev\/mst/{print $2}'); do sudo flint -d "${dev}" q > "${OUT}/flint_$(basename ${dev}).txt" 2>&1 sudo mlxconfig -d "${dev}" query > "${OUT}/mlxconfig_$(basename ${dev}).txt" 2>&1 done ethtool -S <host-ifname> > "${OUT}/ethtool_stats.txt" 2>&1 || true dmesg | tail -200 > "${OUT}/dmesg.txt" echo "快照已保存到 ${OUT}"脚本本身没什么技术含量,价值全在「变更前」这三个字上。有一次改完模式主机侧设备消失,翻出半小时前的快照逐项对比,五分钟就定位到是模式值被写成了另一个含义相近的取值,而不是硬件故障——没有快照的话,大概率会把卡拔下来返修,白等两周。
从那以后我每次动 DPU 配置之前,都强制走一遍这个快照脚本,哪怕只是改一个看起来无关紧要的开关。硬件这东西,出问题的时候它不会告诉你哪一步错了,只会给你一个黑匣子;快照就是你自己的后悔药。希望帮到你。
本文还有配套的精品资源,点击获取