Ubuntu 18.04上NVMe-oF实战:TCP与RDMA配置及性能调优

📅 发布时间:2026/10/21 14:15:53
Ubuntu 18.04上NVMe-oF实战:TCP与RDMA配置及性能调优
1. 为什么要在Ubuntu 18.04上折腾NVMe-oFNVMe-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 SSDhost端不需要本地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-tcphost端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_hostattr_allow_any_host设为1表示允许任何host连接测试环境图方便可以这么干。生产环境建议设为0然后通过allowed_hosts目录精确控制哪些host能连。序列号可以自定义但建议保持唯一echo NVME-TEST-001 | sudo tee attr_serial echo nvme-test-model | sudo tee attr_model3.2 把NVMe命名空间挂进subsystem假设后端盘是/dev/nvme1n1先确认它没有挂载任何文件系统lsblk /dev/nvme1n1然后创建namespacesudo 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 enabledevice_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并发现targetUbuntu 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-of4.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_depthecho 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_traddrenable写入失败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节点上减少跨节点访问。这些细节在文档里不会写但实际跑起来差别很明显。