
香港机房的半夜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;开启 PFC、ECN、DCB |
| 线缆 | 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