土法炼钢 · 系统与基础设施

【SPDK 用户态存储】Initiator 与互通:SPDK host、内核 nvme-rdma/tcp 与多路径边界

文章导航

分类入口
storagelinux
标签入口
#spdk#nvme-of#initiator#nvme-rdma#nvme-tcp#multipath#ANA#v26.01

目录

Target 与传输就绪后,对端是谁接管队列,决定了延迟路径落在用户态还是内核。SPDK NVMe Driver 把远端 NVMe-oF 子系统接进同一套 spdk_nvme_* API;Linux 则提供 nvme-rdma / nvme-tcpnvme-cli。官方 Getting Started 写明:与内核 target/host 做过互通测试。本文钉住三条边界:SPDK host 怎么连、和内核 initiator 如何分工、多路径能做什么却不该神话什么。

本文是「SPDK / 用户态存储栈」系列第 10 篇(共 16 篇)。→ 系列目录

篇目 核心内容
第 9 篇 · 传输层 RDMA / TCP 与 interrupt
第 10 篇 · Initiator host 路径、内核互通、multipath
第 11 篇 · vhost 对 VMM 的块后端边界

上一篇传输层 · 下一篇vhost / virtio 边界

版本锚定:SPDK v26.01NVMe over Fabrics Target(Configuring the Linux NVMe-oF Host)、NVMe Driver(NVMe over Fabrics Host Support)、NVMe-oF Multipath HOWTO;内核侧指 nvme-rdma / nvme-tcpnvme-cli 常规用法。无伪造 IOPS、无臆造 failover 毫秒数。

本篇不展开:完整 ANA 状态机百科、厂商多路径栈、把 NVMe-oF 盘给虚拟机的 vhost 路径(第 11 篇)。


一、两条 host 路径

flowchart TB
  subgraph spdk_host ["SPDK host path"]
    app["app / bdevperf / spdk_tgt"]
    libnvme["lib/nvme fabrics"]
    bdevnvme["bdev_nvme"]
    app --> libnvme
    libnvme --> bdevnvme
  end
  subgraph k_host ["Linux kernel host path"]
    cli["nvme-cli"]
    kmod["nvme-rdma / nvme-tcp"]
    block["/dev/nvmeNnM"]
    cli --> kmod
    kmod --> block
  end
  tgt["SPDK or kernel NVMe-oF target"]
  bdevnvme --> tgt
  block --> tgt
路径 入口 上层消费者 完成模型
SPDK host spdk_nvme_probe() / bdev_nvme_attach_controller SPDK 应用、bdev 栈、用户态轮询 默认 qpair 轮询;可选 interrupt 相关 API(见驱动文档 / changelog)
内核 host modprobe nvme-rdmanvme-tcp + nvme connect 文件系统、O_DIRECT、内核块客户端 内核 NVMe 驱动与块层

官方术语:规范称连接方为 host;文档也承认民间称 initiator。下文按语境混用,对象都是「发起方」。


二、SPDK host:同一套 NVMe API,换 trid

NVMe Driver · NVMe over Fabrics Host Support

在 bdev 层,Block Device User Guide 用同一条 RPC 覆盖两种叶子:

# 语义示例:本机 PCIe
scripts/rpc.py bdev_nvme_attach_controller -b NVMe1 -t PCIe -a 0000:01:00.0

# 语义示例:远端 NVMe-oF(RDMA)
scripts/rpc.py bdev_nvme_attach_controller -b Nvme0 -t RDMA -a 192.168.100.1 \
  -f IPv4 -s 4420 -n nqn.2016-06.io.spdk:cnode1

(地址与 NQN 换成你的环境;本篇不附真实输出。)

限制:RDMA 内存注册等问题与 target 侧同类,官方指向 target 文档的 RDMA Limitations。Host 进程同样吃大页与核 mask;与 target 共机时要避免抢同一组专用核(第 3、15 篇)。


三、内核 initiator:nvme-cli 与驱动模块

Getting Started · Configuring the Linux NVMe over Fabrics Host

modprobe nvme-rdma   # 或 nvme-tcp
nvme discover -t rdma -a 192.168.100.8 -s 4420
nvme connect  -t rdma -n "nqn.2016-06.io.spdk:cnode1" -a 192.168.100.8 -s 4420
nvme disconnect -n "nqn.2016-06.io.spdk:cnode1"

rdma 换成 tcp 即切换传输。成功后出现内核 NVMe 块设备,其后路径与普通 nvme 盘相同——可接文件系统,或再经 storage/79O_DIRECT+io_uring,而不是 SPDK reactor。

互通矩阵(机制级)

官方断言测试过 SPDK 与 Linux 实现的互通。工程上仍建议按格子验证,而不是假设「任意版本组合永远兼容」:

Initiator ↓  Target → SPDK nvmf_tgt 内核 nvmet
SPDK bdev_nvme / libnvme 同栈主路径 应测;注意特性位与对齐
内核 nvme-rdma / nvme-tcp 官方主线示例 内核经典路径

版本钉:本系列 target 侧钉 SPDK v26.01;内核 initiator 的特性(TLS、认证、ANA)随内核版本变化,正文不声称某一发行版内核已具备与 v26.01 对等的全部实验特性。升级任一侧后应重跑 discover/connect 与数据路径冒烟。


四、何时选哪条 host 路径

目标 更合适的 host 原因
用户态数据面继续用 bdev(再导出 vhost/nvmf、做用户态流水线) SPDK 远端盘直接进 bdev 树
把远端盘交给内核 FS / 传统应用 内核 initiator 块设备节点与 VFS 集成现成
同机已为 SPDK 独占 CPU/大页,又想少进程 SPDK 避免再维护一套内核连接与 udev
运维只熟 nvme-cli,且应用不肯链 SPDK 内核 工具链与可观测在 sysfs/内核侧

混合可以存在(部分盘内核、部分盘 SPDK),但观测与故障域会分裂——排障坐标系见第 14 篇。

4.1 互操作时的「特性位」意识

Target 在 v26.01 宣称支持 NVMe 2.0 所需特性,并允许用 transport 选项掩码关掉部分 optional 命令。Initiator 若假设「凡是 NVMe-oF 都能 fused compare-and-write / 某 Identify CNS」,可能在掩码或旧内核上踩坑。changelog 历史里也曾记载 RDMA 上 fused 操作对 inline data 的对称性要求——同栈最省心,跨实现要按特性单测,不要只靠一次 nvme connect 成功。

站内 storage/03 讲清协议对象;本篇强调实现边界:协议兼容 ≠ 运维兼容。超时默认值、重连节奏、discovery 刷新,都是实现与发行版策略,必须写进联调清单。


五、多路径:能做,但不要神话

5.1 官方能力

NVMe-oF Multipath HOWTO 演示:

Block Device User Guide 也指向 NVMe bdev 的 multipath 文档。结论:SPDK target 与 initiator 都支持到同一 subsystem 的多独立路径,并可用 ANA 表达优选。

5.2 谨慎清单(无伪造数字)

多路径解决的是 可达性与负载分布,不自动提供:

  1. 跨故障域的数据冗余——那是 RAID/复制/上层集群的事;两条网线连同一 malloc bdev 只是双通道。
  2. 零中断切换——ANA 状态变更、路径下线、重连与超时如何咬合,取决于配置与内核/用户态策略;本篇不编造 RTO。
  3. 与内核 dm-multipath 叙事混用——内核 NVMe multipath 与 SPDK bdev_nvme multipath 是不同控制面;混部时要分清「谁在选 path」。
  4. 观测——HOWTO 用 bdev_nvme_get_io_paths、主机 ss -t 等查看路径;生产必须把 path 状态纳入第 13–14 篇的坐标系,而不是只看聚合 IOPS。

争论:有人主张「块存储多路径应完全像 FC SCSI 年代一样对应用透明」;另一侧认为 NVMe ANA + 显式策略更可控。SPDK 暴露 ANA state 与 multipath policy RPC,偏向 显式可控。透明性若要做,应在编排层,而不是假装 RPC 不存在。

5.3 推荐的最小验证剧本(无数字)

在声称「生产可切路径」之前,至少按官方 HOWTO 的逻辑跑通一次(环境允许时):

  1. 两 listener 都标 optimized,确认双路径可见(bdev_nvme_get_io_paths 或内核侧等价信息);
  2. 将一路标 non_optimized,确认流量迁到另一路(可用 ss 等观察连接活跃,而不是只看应用是否还活着);
  3. 再测一路 listener 进程级故障或链路 down,记录应用是否只读错误、是否自动恢复——把现象写进台账,不把「应该会」写进对外承诺
  4. 若启用 active_active,确认这是负载分布策略,而不是额外的耐久性保证。

没有上述剧本就写进 SLA,属于流程事故,不是传输层事故。


六、安全与实验特性边界

Getting Started 另述:TCP TLS 1.3 PSK(experimental)、DH-HMAC-CHAP 带内认证等。要点:

这些改变的是连接建立与信任,不改变「附着成功后 bdev/内核盘如何提交 I/O」的主模型。生产启用前按官方实验标签与内核支持矩阵单独评估。

把安全开关与多路径开关同时打开时,更要把联调矩阵写全:每条 path 是否都走 secure-channel、PSK/密钥轮换是否覆盖所有 listener、认证失败是「连不上」还是「连上后第一条 I/O 失败」。症状不同,值班手册不能共用同一段「检查网线」。


还有一类边界来自「谁拥有超时时钟」。SPDK host 上超时与重试更多落在应用或 bdev_nvme 选项;内核 initiator 上则与 nvme 驱动参数、sysfs 与发行版默认值纠缠。两端同时改超时却不记录,会出现「同一次链路抖动,一侧快速失败、一侧长时间挂起」的分裂症状。联调清单里应单列超时与重连参数来源,并在故障演练中只改一侧、观察另一侧,形成可复核的因果,而不是两侧一起拧。

若 SPDK host 与内核 host 同时连接同一 subsystem,要确认 target 的 max qpairs、host 允许列表与资源配额是否按「双 initiator」规划。两边抢同一套队列与 ANA 状态时,现象可能是间歇性 connect 失败或路径抖动,而不是清晰的权限错误。除非有明确的迁移窗口需求,生产上应避免无文档的双栈并行挂载。

七、开放问题

  1. 跨实现 multipath 行为差异(SPDK ANA RPC vs 内核 NVMe multipath)在双厂商路径下的一致性,仍依赖集成测试而非单一规范段落。
  2. Host 侧 interrupt mode 与内核 initiator 共置:用户态可睡、内核路径仍中断驱动时,同机延迟谱如何建模,缺少标准方法。
  3. 发现服务与多租户过滤(v26.01 custom discovery filter)在 initiator 大规模自动附着时的策略语言,仍属产品空白。
  4. 内核块设备节点上的上层(FS、LVM、dm)与 SPDK 多路径并发:一旦远端盘进了 VFS,故障切换语义可能被文件系统日志与挂起策略放大——这超出 NVMe-oF 本身,却是真实部署的主要风险源。

从系列问题清单回看:第 4 个关键问题(NVMe-oF 如何落到 bdev、传输差在哪)在 08–10 收束到「对象映射 + 传输模型 + host 选择」;多路径与安全是同一问题上的运维外环,不是性能彩蛋。下一篇离开 fabrics host,改谈把 bdev 喂给虚拟机的 vhost 边界。


八、参考资料

规范 / 官方文档(A)

源码(A)

站内

实验台账


Host 侧一旦选定,观测工具也要跟着换:SPDK 看 RPC 与 reactor,内核看 sysfs 与 nvme 日志,不要混用两套口径硬比。

把「能 ping 通网卡」当成「多路径已验证」是不够的:必须看到 path 列表与流量迁移,才算演练完成。

九、小结

  1. SPDK host 用同一 NVMe API + bdev_nvme 吃远端盘;内核 hostnvme-rdma/nvme-tcp 得到 /dev/nvme*
  2. 官方做过互通;版本组合仍要冒烟,实验安全特性单独评估。
  3. 多路径与 ANA 可用,解决可达与选路,不替代冗余,也不该用未测数字吹「无感切换」。
  4. 下一步:把 bdev 送给虚拟机——见 第 11 篇 vhost

附:选型一句话

这三句盖不住所有拓扑,但能挡住「先上多路径和 TLS,再决定 initiator 在哪」的常见倒置。机制上,initiator 选择决定了完成通知落在 reactor 还是内核块层——与第 1 篇坐标系里的「另一格」直接对应。

系列目录 · 上一篇:传输层 · 下一篇:vhost / virtio 边界

读完这篇,下一步读什么

优先读同系列或同问题的下一篇,把单篇消费变成主题集群。


By .