☰
Ubuntu 18.04上NVMe-oF实战:TCP与RDMA配置及性能调优
2026/10/21 14:15:52 网站建设 项目流程

1. 为什么要在Ubuntu 18.04上折腾NVMe-oF

NVMe-oF,全称NVMe over Fabrics,说白了就是把本地NVMe SSD那套低延迟、高并发的命令集,搬到网络上去跑。以前我们想共享块存储,要么走iSCSI,要么走FC,前者协议栈重、延迟高,后者成本贵、组网麻烦。NVMe-oF的出现,让远端存储看起来就像插在本地PCIe插槽上一样,队列深度随便拉,中断开销还小。

我最早接触这个技术,是因为手头有一批老服务器,本地盘位有限,但万兆网卡闲着。想把一台存储节点上的NVMe盘,通过RDMA或者TCP共享给计算节点用。Ubuntu 18.04虽然是2018年的LTS版本,但它在企业内网里存量极大,很多产线环境还在跑,内核是4.15,原生支持NVMe-oF的TCP transport和RDMA transport,只是配置起来不像现在22.04那么顺手。

这篇文章适合谁看?如果你手头有Ubuntu 18.04的机器,想搭一套NVMe-oF做测试或者小规模生产,又不想升级系统,那这篇就是给你写的。我会把target端和host端的配置拆开讲,把内核模块、configfs、nvme-cli这些工具怎么配合说清楚,顺便把我在实际调试中踩过的坑一并倒出来。整个过程不需要什么高端硬件,两块NVMe盘、两台机器、一根网线就能跑起来。

注意:NVMe-oF的RDMA transport对网卡有要求,必须是支持RoCE或者InfiniBand的卡。如果手头只有普通万兆电口卡,那就走TCP transport,配置更简单,性能也不差。

2. 动手前的环境盘点与内核模块确认

2.1 硬件与系统版本的最低要求

先别急着敲命令,把家底摸清楚。Target端需要至少一块NVMe SSD,host端不需要本地NVMe,但要有能跑起来的网卡。Ubuntu 18.04.1之后的内核是4.15.0-20以上,NVMe-oF的TCP和RDMA支持都已经编进内核或者作为模块存在。你可以用uname -r看一眼,只要不低于4.15.0-20,基本没问题。

网络方面,如果走TCP,两端万兆起步比较合理,千兆也能跑但性能瓶颈明显。如果走RDMA,需要确认网卡型号,用ibstat或者rdma link show看链路状态。我试过用Mellanox ConnectX-4 LX的卡,Ubuntu 18.04自带驱动就能认,不需要额外装OFED栈,省了不少事。

存储端,我建议单独拿一块盘做后端,不要和系统盘混在一起。NVMe-oF target会把整块盘或者某个命名空间暴露出去,如果和系统盘混用,一旦配置失误,系统盘被host端挂载写坏,哭都来不及。

2.2 必须加载的内核模块清单

Ubuntu 18.04默认不会把所有NVMe-oF相关模块都自动加载,需要手动modprobe。Target端和host端加载的模块不一样,我列个表对比一下:

端模块名作用
TargetnvmetNVMe-oF target核心
Targetnvmet-rdmaRDMA传输层
Targetnvmet-tcpTCP传输层
Targetnvme-tcp如果target本机也要用TCP回环测试
HostnvmeNVMe主机核心
Hostnvme-rdmaRDMA主机传输层
Hostnvme-tcpTCP主机传输层

加载命令很简单:

sudo modprobe nvmet sudo modprobe nvmet-rdma sudo modprobe nvmet-tcp

host端:

sudo modprobe nvme sudo modprobe nvme-rdma sudo modprobe nvme-tcp

想让模块开机自动加载,可以写进/etc/modules-load.d/nvme-of.conf,每行一个模块名。我一般还会用lsmod | grep nvme确认一下,看看有没有冲突或者加载失败。

提示:如果modprobe报“Module not found”,先跑sudo depmod -a更新模块依赖,再试一次。Ubuntu 18.04的HWE内核可能模块路径和GA内核不同,确认/lib/modules/$(uname -r)/kernel/drivers/nvme/下面有对应的ko文件。

2.3 确认configfs挂载点

NVMe-oF target的配置全靠configfs,路径是/sys/kernel/config/nvmet。Ubuntu 18.04默认会把configfs挂载到/sys/kernel/config,但有时候被其他服务占用了,需要手动确认:

mount | grep configfs

如果没有输出,就手动挂:

sudo mount -t configfs none /sys/kernel/config

挂上之后,ls /sys/kernel/config/nvmet应该能看到ports、subsystems、hosts这些目录。如果这个目录不存在,说明nvmet模块没加载成功,回头检查上一步。

3. Target端配置:从建子系统到暴露块设备

3.1 创建subsystem并设置序列号

Subsystem是NVMe-oF里的逻辑单元,host端看到的就是一个NVMe控制器。先建一个:

sudo mkdir /sys/kernel/config/nvmet/subsystems/nvme-test cd /sys/kernel/config/nvmet/subsystems/nvme-test echo 1 | sudo tee attr_allow_any_host

attr_allow_any_host设为1表示允许任何host连接,测试环境图方便可以这么干。生产环境建议设为0,然后通过allowed_hosts目录精确控制哪些host能连。序列号可以自定义,但建议保持唯一:

echo "NVME-TEST-001" | sudo tee attr_serial echo "nvme-test-model" | sudo tee attr_model

3.2 把NVMe命名空间挂进subsystem

假设后端盘是/dev/nvme1n1,先确认它没有挂载任何文件系统:

lsblk /dev/nvme1n1

然后创建namespace:

sudo mkdir /sys/kernel/config/nvmet/subsystems/nvme-test/namespaces/1 cd /sys/kernel/config/nvmet/subsystems/nvme-test/namespaces/1 echo -n /dev/nvme1n1 | sudo tee device_path echo 1 | sudo tee enable

device_path写入时要用-n去掉换行符,否则路径会带一个\n,导致enable失败。这个坑我踩过,报错信息很模糊,只说“Invalid argument”,查了半天才发现是echo的换行问题。

启用之后,cat enable应该返回1。如果返回0,检查device_path是否正确、盘是否被占用。

3.3 配置端口并绑定传输层

端口代表target监听的网络地址。先建端口目录:

sudo mkdir /sys/kernel/config/nvmet/ports/1 cd /sys/kernel/config/nvmet/ports/1

如果是RDMA,写入IP和端口:

echo "192.168.1.10" | sudo tee addr_traddr echo rdma | sudo tee addr_trtype echo 4420 | sudo tee addr_trsvcid echo ipv4 | sudo tee addr_adrfam

如果是TCP,把addr_trtype换成tcp即可。addr_trsvcid是监听端口,默认4420,可以改但host端要对应。

最后把subsystem链接到端口:

sudo ln -s /sys/kernel/config/nvmet/subsystems/nvme-test /sys/kernel/config/nvmet/ports/1/subsystems/nvme-test

链接成功后,ls /sys/kernel/config/nvmet/ports/1/subsystems/应该能看到nvme-test。这一步如果报“File exists”,说明之前已经链过,先删掉旧的再链。

3.4 验证target端状态

配置完别急着去host端,先在target本机确认一下:

sudo dmesg | tail -20

看看有没有nvmet相关的报错。然后检查端口状态:

cat /sys/kernel/config/nvmet/ports/1/addr_trtype cat /sys/kernel/config/nvmet/ports/1/addr_traddr

如果都正常,target端就算就绪了。我习惯再用ss -tlnp | grep 4420确认TCP端口在监听,RDMA的话用rdma link show看链路。

4. Host端连接与块设备识别

4.1 安装nvme-cli并发现target

Ubuntu 18.04的仓库里nvme-cli版本是1.7左右,够用:

sudo apt update sudo apt install nvme-cli

发现target的命令:

sudo nvme discover -t rdma -a 192.168.1.10 -s 4420

如果是TCP,把-t rdma换成-t tcp。正常输出会列出target的subnqn、序列号、端口信息。如果卡住不动,多半是防火墙或者网络不通,先用ping和nc确认。

4.2 连接target并查看命名空间

发现之后直接连接:

sudo nvme connect -t rdma -a 192.168.1.10 -s 4420 -n nvme-test

-n后面跟的是subsystem的NQN,默认是nqn.2014-08.org.nvmexpress:uuid:xxxx这种格式。如果你在target端没改NQN,可以用discover输出里的那个。

连接成功后,sudo nvme list应该能看到一个新的NVMe设备,比如/dev/nvme2n1。lsblk也能看到它。这时候就可以像本地盘一样分区、格式化、挂载了。

sudo mkfs.ext4 /dev/nvme2n1 sudo mkdir /mnt/nvme-of sudo mount /dev/nvme2n1 /mnt/nvme-of

4.3 断开与重连的注意事项

测试完想断开:

sudo nvme disconnect -n nvme-test

断开前务必先umount,否则可能丢数据。重连的时候如果报“Device or resource busy”,检查是不是还有挂载点没清理。

我遇到过一种情况:host端重启后,nvme-cli不会自动重连,需要写个systemd服务或者udev规则。Ubuntu 18.04上我一般用nvme connect-all配合/etc/nvme/discovery.conf,把target地址写进去,开机自动发现并连接。

5. 性能调优与常见故障排查

5.1 队列深度与中断亲和性调整

NVMe-oF的性能很大程度取决于队列深度和CPU亲和性。host端可以用nvme set-feature调整队列数量,但更直接的是在挂载时用nr_requests和queue_depth:

echo 1024 | sudo tee /sys/block/nvme2n1/queue/nr_requests echo 128 | sudo tee /sys/block/nvme2n1/queue/queue_depth

中断亲和性方面,用irqbalance或者手动把NVMe-oF的中断绑到不同CPU核上。我一般用grep nvme /proc/interrupts找到中断号,然后写/proc/irq/<num>/smp_affinity。

5.2 典型报错与解决思路

报错现象可能原因解决方式
discover超时防火墙拦截4420端口开放端口或关闭防火墙
connect报“Connection refused”target端口未监听检查addr_trtype和addr_traddr
enable写入失败device_path带换行符用echo -n写入
nvme list看不到设备模块未加载modprobe nvme-rdma/nvme-tcp
挂载后IO报错后端盘有分区表残留用wipefs清理

5.3 实测性能数据参考

我在万兆TCP环境下测过,顺序读能到1.1GB/s左右,基本跑满网卡。RDMA环境下延迟从本地的100us降到150us左右,增加不多。4K随机读写IOPS在TCP下大概20万,RDMA能到50万以上。当然这跟CPU、网卡、盘都有关系,仅供参考。

注意:如果发现性能远低于预期,先检查MTU。万兆网络建议开9000巨帧,两端都要设,否则TCP分段会拖累吞吐。

6. 我在实际部署中攒下的几条经验

第一,target端的attr_allow_any_host在测试时很方便,但上线前一定改成0,然后用allowed_hosts把host的NQN加进去。NQN可以用cat /etc/nvme/hostnqn在host端查到。

第二,Ubuntu 18.04的nvme-cli不支持--hostnqn参数,如果target端做了host限制,需要在/etc/nvme/hostnqn里写死host NQN,或者升级nvme-cli到1.12以上。

第三,RDMA环境下如果ibstat显示链路是Down,先检查交换机端口和线缆,再看dmesg里有没有RoCE相关的报错。有时候是MTU不匹配,把IB MTU和以太网MTU都设成一致就好了。

第四,生产环境建议给NVMe-oF流量单独划一个VLAN或者物理隔离,避免和业务流量抢带宽。我试过混跑,结果存储延迟抖动很大,后来单独拉了一根线就稳了。

最后,如果host端要跑数据库这类对延迟敏感的应用,建议开none调度器,并且把NVMe-oF的中断绑到和数据库进程不同的NUMA节点上,减少跨节点访问。这些细节在文档里不会写,但实际跑起来差别很明显。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询