匹配服读写很忙?在香港服务器上评估 NVMe over Fabrics(RoCE)可行性与风险:CentOS 7 实战部署、参数调优与踩坑复盘
技术教程 2025-09-30 10:13 252


香港机房的半夜1点40,我被 PagerDuty 叫醒:“匹配服务器 p99 写延迟 > 15ms,持续 5 分钟。”

我们这套匹配系统高峰期是晚 8 点到 1 点,读写都是短小、随机、低延迟敏感。最初的方案是每台计算节点本地 NVMe,但写放大叠加业务高峰,单机 SSD 再快也会被打满。我当时想:要不把热数据“外置化”,用 100G RoCE 做 NVMe-oF? 这样计算节点无盘化/轻盘化,吞吐和尾延迟都可能更稳。

第二天,我就抱着两台 Mellanox 网卡、几根 DAC 线往机房跑,开始了这次 NVMe-oF(RoCE)可行性与风险评估。

目标与评估准则

目标

  • 将匹配服热数据从本地盘迁出到一台/多台 NVMe 存储节点,通过 NVMe-oF over RoCEv2 访问;
  • 不恶化 业务 p99/p99.9 延迟;
  • 高峰期间 IOPS 与稳定性可与“本地盘”方案相当或更优;
  • 在跨机架(ToR 间)情况下仍可控;
  • 具备快速回滚能力。

评估准则(SLO/监控指标)

  • 延迟:p50、p95、p99、p99.9(微秒级)
  • IOPS/吞吐:4k/8k 随机读写、70/30 混合
  • 抖动:1 分钟窗口内 p99 漂移
  • 网络:丢包、ECN 标记、CNP、PFC pause、队列利用率
  • 稳定性:连接重建、掉线自愈、ANA multipath 切换

现场环境与硬件清单

位置:葵涌机房,双 ToR,跨机架 2 跳

操作系统:CentOS 7.9(3.10 内核;为了 RDMA 体验,后续装 MLNX_OFED 与内核 backport 模块)

设备与版本(关键参数)

角色 型号/配置 关键参数
计算节点 ×4 Dell R7525,AMD EPYC 7302(16C)、256GB RAM 本地启动 SATA SSD,仅作系统盘
网卡(计算/存储) Mellanox ConnectX-5 EN(MCX516A-CDAT) 100GbE;FW 16.35.2006;支持 RoCEv2、DCQCN
存储节点 ×2 Supermicro 2U NVMe 机架式 Samsung PM9A3 3.84TB × 8 单盘顺序读 ~6.8GB/s,4k 随机读 ~850k IOPS
交换机(ToR) Arista 7060CX-32S(100G) EOS 4.28;开启 PFCECNDCB
线缆 100G DAC(1m/3m) 低延迟、避免光模块不一致
驱动/工具 MLNX_OFED 5.8-2.0.3.0、nvme-cli、nvmetcli、fio nvmet 走 RDMA,对比 NVMe/TCP

注:CentOS 7 原生内核对 NVMe-oF/roce 的体验一般,务必上 MLNX_OFED 并校验 FW/驱动匹配。

网络与拓扑(ASCII)

[Compute 1..4: CX5 @100G] --(100G DAC)--> [ToR A: Arista7060] == LAG/MLAG == [ToR B: Arista7060] <--(100G DAC)-- [Storage 1..2: CX5 @100G]
  • 同机架先测(Compute↔Storage 同 ToR),再跨机架(ToR A ↔ ToR B)
  • RoCEv2(UDP/IP)、VLAN 2301、优先级 PCP=3(仅 RoCE 流量开 PFC)
  • DCQCN 打开(ECN 标记→CNP 控制)

交换机配置要点(Arista EOS 示例)

! 全局开启 ECN 与 RoCE 优先级的 PFC
dcbx application priority ethernet 0x894f priority 3   ! 0x894f 是 RoCEv2 ethertype 映射示例
traffic-class pfc priority 3
priority-flow-control priority 3 enable

! ECN (WRED) 标记阈值,避免中小突发导致尾延迟失控
wred ecn
  random-detect ecn
  random-detect min-threshold 280KB max-threshold 320KB
  random-detect ecn-marking

! QoS queue & scheduler (示意,按设备实际 queue 映射)
class-map match-any ROCE-CLASS
  match dscp 48
  match cos 3
policy-map ROCE-POLICY
  class ROCE-CLASS
    priority level 1
    pause-priority

interface Ethernet1-4
  switchport
  spanning-tree portfast
  priority-flow-control priority 3 enable
  service-policy input ROCE-POLICY

经验:只对 RoCE 优先级开 PFC,其余关闭。否则容易 PFC 暴风(pause storm)把别的队列也拖死。

网卡侧(Mellanox)QoS/DCQCN 配置

# 安装 MLNX_OFED(与 FW 版本匹配),启用 rdma 栈
./mlnxofedinstall --with-nvmf --add-kernel-support
/etc/init.d/openibd restart

# 信任 DSCP,以便交换机 ECN/DSCP 生效
mlnx_qos -i eth0 --trust dscp

# 仅优先级3 开启 PFC
mlnx_qos -i eth0 --pfc 0,0,0,1,0,0,0,0

# DCQCN 参数(保守起步,再按压测调优)
echo 1  > /sys/module/mlx5_core/parameters/enable_dcbx
echo 1  > /sys/module/mlx5_core/parameters/enable_rsc
# DCQCN 内核参数可能随 OFED 版本不同,下面给出 ethtool + sysfs 混合示例
ethtool -C eth0 adaptive-rx off rx-usecs 0   # 低延迟场景,先关自适应合并
ethtool -K eth0 tso off gso off gro off lro off rxhash on

# IRQ 亲和与 NUMA 亲和
./set_irq_affinity.sh eth0  # Mellanox 提供脚本或使用 irqbalance 关闭后手工绑核

经验:低延迟优先级下,先关 GRO/LRO;吞吐型再看是否打开、以及 rx-usecs 的折中。

存储节点(Target)配置:nvmet + RDMA

  • 建 namespace(示例将 NVMe 盘直出,便于评估单盘与聚合差异;生产可叠加 mdraid/ZFS/软 RAID)
  • 通过 configfs/nvmetcli 暴露 NQN、端口、ACL
  • RoCE 走 4420 端口
# 安装工具
yum install -y nvmetcli nvme-cli

# 假设 /dev/nvme0n1 .. /dev/nvme7n1 为 8 块 NVMe 盘
nvmetcli clear

# 创建 subsystem
nvmetcli create-subsystem /subsystems/nqn.2025-09.hk.lab:roce.tgt \
  -a "allow_any_host=false" -s "RoCE Target"

# 绑定 namespace(示例绑定 2 块做评估)
nvmetcli create-namespace /subsystems/nqn.2025-09.hk.lab:roce.tgt/ namespaces/1 \
  -b /dev/nvme0n1 -f 512
nvmetcli create-namespace /subsystems/nqn.2025-09.hk.lab:roce.tgt/ namespaces/2 \
  -b /dev/nvme1n1 -f 512

# 端口(RDMA)
nvmetcli create-port /ports/1 -t rdma -a 10.230.1.10 -s 4420
nvmetcli add-subsystem-to-port /subsystems/nqn.2025-09.hk.lab:roce.tgt /ports/1

# ACL(允许指定主机 NQN)
echo "nqn.2025-09.hk.lab:compute.01" > /subsystems/nqn.2025-09.hk.lab:roce.tgt/allowed_hosts/add

小贴士:禁用 allow_any_host,只允许白名单 NQN,香港机房多租户场景尤其要注意隔离。

计算节点(Host)配置:nvme-cli 连接

yum install -y nvme-cli rdma-core

# 主机 NQN(/etc/nvme/hostnqn)
cat /etc/nvme/hostnqn
# 若需自定义:
echo "nqn.2025-09.hk.lab:compute.01" > /etc/nvme/hostnqn

# 连接 RoCE 目标
nvme connect -t rdma -a 10.230.1.10 -s 4420 -n nqn.2025-09.hk.lab:roce.tgt

# 查看设备
nvme list
nvme list-subsys

# 开启 NVMe multipath(根据内核与 nvme-cli 版本,CentOS7 需确认支持情况)
nvme multipath -e

NVMe/TCP 对照组(同一套 namespace,换 -t tcp、端口 4420/8009,验证差距):

nvme connect -t tcp -a 10.230.1.10 -s 4420 -n nqn.2025-09.hk.lab:roce.tgt

fio 压测方法

通用参数(低延迟、短 IO):

  • 引擎:libaio(CentOS 7 环境更稳)
  • I/O 大小:4k、8k
  • 队列深度:QD=1/4/8/16
  • 访问模式:randread、randwrite、randrw rwmixread=70
  • 运行时间:每组 120s(预热 30s)
  • io_uring 在 CentOS 7 不建议(内核太老)

示例作业文件 fio-nvmf.fio:

[global]
ioengine=libaio
iodepth=8
direct=1
time_based=1
runtime=120
ramp_time=30
numjobs=1
group_reporting=1
filename=/dev/nvme0n1
cpus_allowed_policy=split

[randread-4k]
bs=4k
rw=randread

[randwrite-4k]
bs=4k
rw=randwrite

[randrw-4k-70r]
bs=4k
rw=randrw
rwmixread=70

运行:

fio fio-nvmf.fio --output=fio_roce.json --output-format=json

结果(同机架 vs 跨机架、RoCE vs TCP vs 本地盘)

机架内 RTT ~ 3–4µs,跨机架(两跳)RTT ~ 8–10µs(光口/DAC混合微差)

4k 随机读(QD=8)

场景 p50 (µs) p99 (µs) p99.9 (µs) IOPS (K)
本地 NVMe(直连) 68 220 450 720
NVMe-oF RoCE(同机架) 82 260 520 640
NVMe-oF RoCE(跨机架) 94 310 680 590
NVMe-oF TCP(同机架) 115 420 1100 470

4k 随机写(QD=8)

场景 p50 (µs) p99 (µs) p99.9 (µs) IOPS (K)
本地 NVMe(直连) 75 260 520 610
NVMe-oF RoCE(同机架) 95 320 640 540
NVMe-oF RoCE(跨机架) 112 380 820 500
NVMe-oF TCP(同机架) 150 560 1400 410

4k 随机混合 70/30(QD=8)

场景 p99 (µs) IOPS (K) 抖动(p99 漂移 /min)
本地 NVMe(直连) 280 660 3–5%
NVMe-oF RoCE(同机架) 320 600 5–7%
NVMe-oF RoCE(跨机架) 380 560 7–9%
NVMe-oF TCP(同机架) 520 470 10–14%

结论

在我们这类“短小随机、延迟敏感”的匹配负载上,RoCE 比 NVMe/TCP 明显更稳、更低延迟;

相比本地盘,RoCE 方案 损失 10–25µs 的 p50,但 p99/p99.9 可通过调优逼近,可接受;

跨机架会增加尾延迟,设计上计算节点尽量与存储节点 同 ToR。

真实踩坑与修复过程

坑 1:PFC 暴风把整机架打跪

现象:高峰时段,某条链路错误上报,交换机统计里 pause 帧暴增,连带其他业务丢包。
根因:我们一开始把 PFC 开在了多个优先级(历史 QoS 模板遗留),RoCE 流量+备份流量一起被 pause。
修复:只给 RoCE 优先级(PCP=3)开 PFC,其他全部关;并设置 ECN 阈值在 280–320KB,控制突发。恢复后尾延迟明显收敛。

坑 2:DCQCN/CNP 不生效,p99 抖动大

现象:ECN 统计一直 0,CNP 不触发,负载上来后 p99 飘到 600–800µs。
根因:网卡 dscp trust 没启,交换机在 DSCP→队列映射里没有把 RoCE v2 的 DSCP 抬到对应队列。
修复:mlnx_qos -i eth0 --trust dscp,交换机 class-map 同步匹配 DSCP=48;随后 ECN 有标记,CNP 生效,尾延迟回落。

坑 3:irqbalance 抢核,NUMA 反亲和

现象:同样压力下,Compute-02 延迟比 Compute-01 高 15–20%。
根因:网卡在 NUMA node1,但 fio/journal 线程落在 node0,且 irqbalance 把中断分散到远核。
修复:关闭 irqbalance,使用 Mellanox 的 set_irq_affinity.sh 绑定到本地 NUMA;fio 加 numactl --cpunodebind。延迟立刻收敛。

坑 4:NVMe-TCP 对照组异常好看(假的)

现象:某轮测试 NVMe-TCP 数据出奇地好。
根因:对照组同时开了 GRO/LRO/TCP coalesce,统计口径不同,导致 p50 下降但 p99.9 爆炸。
修复:统一网卡 offload 策略,只比较 apples-to-apples。

坑 5:跨机架时偶发连接重建

现象:夜里低谷时偶发 disconnect/reconnect。
根因:上游运营商维护导致某上行抖动,引起 MLAG 侧路由 flap。
修复:NQN 多端口暴露 + Host 侧启用 ANA multipath;结合 udev rule 自动重连;并在路由维护窗口设置业务降权。

安全与隔离

  • NQN 白名单、VLAN 隔离、ACL 必须开;
  • 运维入口与数据面物理/逻辑隔离;
  • 存储节点磁盘加密(如果合规需要);
  • 监控里加入 异常 NQN/连接尝试告警。

成本与收益(粗略)

方案 服务器/网卡/交换机 线缆/光模块 复杂度 延迟 IOPS 可运维性
本地盘(分布式散盘) 最优 容量难以池化
NVMe-oF RoCE 中高(需 QoS/DCQCN) 接近最优 容量/带宽池化
NVMe-oF TCP 简单但尾延迟偏高

对我们这种“匹配服读写很忙、尾延迟敏感”的业务,RoCE 在高峰稳定性上更有优势。

渐进上线与回滚

  • 灰度:1 台计算节点切 RoCE,观察 24–48 小时;
  • 双写/镜像:关键路径双写到本地盘与 NVMf,验证一致性;
  • 回滚:保留本地盘卷与挂载,nvme disconnect → fstab 切回本地盘 → 服务重启;
  • 告警门槛:p99 连续 3 分钟 > 基线 + 30% 自动回滚。

我用到的关键命令清单(便于复盘)

# RoCE 连接与状态
nvme list-subsys
rdma link
ethtool -S eth0 | egrep "ecn|cnp|pause|prio"

# 交换机(Arista)
show interfaces counters rates
show qos ecn
show priority-flow-control counters

# fio 常用
fio --name=read --filename=/dev/nvme0n1 --rw=randread --bs=4k --iodepth=8 --ioengine=libaio --runtime=120 --time_based --ramp_time=30 --group_reporting

我给团队的最终建议

能同机架就别跨机架;

  • 只对 RoCE 优先级开 PFC,配合温和的 ECN 阈值与 DCQCN;
  • 固件/驱动严格匹配,CentOS 7 必走 MLNX_OFED;
  • 生产前做 p99.9 对齐和 抖动评估,而不是只看 p50;
  • 做好 回滚链路 与 多路径;
  • 监控里要有 ECN/CNP/PFC 指标,否则就是“盲开快车灯”。

上线灰度后的第二个晚上,我守在监控大屏前,p99 一直贴着绿线。机房保安路过,问我怎么还不下班。我举起已经没什么冰的冻柠茶说:
“匹配服读写很忙,但我们不慌。RoCE 顶住了。”
后来我们把这套方案写进了基建蓝图:计算层轻盘化、存储池中心化、QoS 与拥塞端到端可观测。不是最“酷”的技术,但在香港机房的真实高峰里,它让业务稳住了。

附:完整配置片段与脚本(节选)

systemd 单元:NVMe-oF 自动连接

[Unit]
Description=Connect NVMe-oF (RoCE) targets
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/nvmf_roce_connect.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

/usr/local/sbin/nvmf_roce_connect.sh

#!/bin/bash
set -euo pipefail
TARGET_IP="10.230.1.10"
NQN="nqn.2025-09.hk.lab:roce.tgt"
PORT="4420"
PROTO="rdma"

if ! nvme list-subsys | grep -q "$NQN"; then
  nvme connect -t $PROTO -a $TARGET_IP -s $PORT -n $NQN || exit 1
fi
nvme list

udev 规则:异常断开自动重连(简化示例)

echo 'ACTION=="remove", SUBSYSTEM=="nvme", RUN+="/usr/local/sbin/nvmf_roce_connect.sh"' > /etc/udev/rules.d/99-nvmf-reconnect.rules
udevadm control --reload

内核与网卡优化(CentOS 7)

# /etc/sysctl.d/99-roce.conf
net.core.netdev_max_backlog = 50000
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.core.rmem_default = 134217728
net.core.wmem_default = 134217728

# 生效
sysctl --system