Von den Storage-Herstellern hört man immer die wunderbarsten Dinge über neue Technologien oder Protokolle. So auch bei Non-Volatile Memory Express (NVMe). Was dahintersteckt, und ob NVMe jetzt sämtliche klassischen Blockprotokolle wie iSCSI oder FCP ablöst, das schauen wir uns heute an.
Was ist NVMe?
Bekannt ist NVMe ursprünglich durch Desktop PCs und Laptops. Dort hat es als Schnittstelle für schnelle SSDs die „klassische“ SATA-Schnittstelle (meist in Form von M.2 SATA) abgelöst.
NVMe ist deshalb so schnell, weil es im Prinzip eine direkte PCIe-Verbindung darstellt. Bei SATA mussten immer irgendwelche Controller-Chips verwendet werden, die von PCIe auf SATA umwandeln
Durch das Wegfallen dieser Umsetzer, sowie zusätzlich dem Wegfall des uralten SCSI Protokolls, ist NVMe theoretisch und auch praktisch deutlich schneller als SATA. Der Erfolg von NVMe sollte sich daher in den letzten Jahren auch auf SANs ausweiten lassen. Denn was in einem PC schnell ist, das muss ja über’s SAN auch schnell sein, oder? Daher setzte sich das NVMe-Konsortium zusammen, um NVMe-over-Fabrics, NVMe-oF, zu standardisieren. Wobei es hier mehrere „Fabrics“ als Transportmedium geben sollte.
NVMe over Fabrics
So erschien um 2018 herum die ersten Implementierungen von NVMe-FC, NVMe-over-FibreChannel, auf der Bildfläche. Dieses Protokoll ersetzt im Fibre Channel Umfeld das klassische FCP Protokoll durch NVMe. Die restlichen Layer des FC Stacks bleiben hingegen unangetastet. Dadurch können auch so ziemlich alle FC-Switches ab Generation 6 das neue ULP (Upper Level Protokoll) sprechen.
Schon durch den einfachen Wechsel von FCP zu NVMe-FC konnten, in derselben Fabric und mit derselben Hardware, schon sehr beeindruckende Steigerungen in der Geschwindigkeit gemessen werden. Der I/O-Durchsatz stieg beispielsweise je nach Workload um zwischen 30% und 150%. Nur doch Wechsel des Protokolls.
Allerdings sind FC SANs heutzutage nicht mehr allzu weit verbreitet. Viele Datacenter nutzen inzwischen, oft aus Kostengründen, ausschließlich Ethernet-Technologien wie iSCSI oder NFS, um zentralen Storage anzubinden. Dort konnte NVMe bisher nicht sinnvoll verwendet werden.
In den letzten 2 Jahren hat allerdings eine zweite NVMe-oF Technologie an Fahrt gewonnen, und zwar NVMe-over-TCP (kurz: NVMe-TCP). Diese läuft über das ganz normale TCP/IP Protokoll und verspricht so eine höhere Performance ganz ohne große Investitionen in Netzwerktechnologie wie FC SANs oder (teure) Lossless Datacenter-Switches.
Natürlich muss an dieser Sache doch ein Haken sein. Denn wenn wir auf der einen Seite das alte und langsame SCSI Protokoll entfernen, nur um es auf der anderen Seite durch ein ähnlich altes und ähnlich kompliziertes TCP/IP-Protokoll zu ersetzen, dann kann man doch nichts an Performance gewinnen, richtig? Genau das waren auch die Bedenken der Kunden und Anwender, mit denen ich in den letzten Jahren gesprochen habe. Man verwendet ja gerade deshalb ein FC-SAN, damit man vom „langsamen“ TCP/IP bzw. Ethernet wegkommt, welches mit seinen Broadcast-Storms und Paketlatenzen ja gar keine Konkurrenz zu Fibre Channel ist. Und iSCSI, ja, iSCSI macht man schon ab und zu, aber natürlich nur für unkritische Daten. Weil es ist ja langsam, wegen TCP/IP.
Der Laborversuch
Also hab ich mich mal hingesetzt, und bei uns in unserem Lab einen Vergleich aufgebaut zwischen iSCSI und NVMe-TCP, aus reiner Neugierde. Jeder, der unser Lab kennt, weiß dass wir dort relativ viel Hardware herumstehen haben, aber nur selten Hardware der neuesten Generation. Unsere Server sind zwar relativ aktuelle Cisco UCS M5 Server mit 2.1 GHz Intel Xeon Gold 6230 CPUs, und das einzige Storage mit zumindest ein paar SSDs ist eine NetApp AFF A300 mit 24x800GB SATA SSDs. Denn, Spoiler-Alarm: ohne SSDs im Storage merkt man natürlich keinerlei Performance-Unterschied zwischen iSCSI und NVMe-TCP. Aber die Switches dazwischen sind ein wilder Mix aus drei verschiedenen Nexus Modellreihen und Generationen, noch dazu ist auch ACI im Spiel auf dem Nexus C93180:
Das Testszenario sieht also so aus, dass ich eine Linux VM (CentOS 9) mit 2 GB RAM unter ESX 7u3 sowohl mit iSCSI als auch mit NVMe-TCP an dasselbe Storage-System (AFF A300 mit ONTAP 9.11.1) angebunden habe. Dabei habe ich eine 80GB große LUN für iSCSI und einen ebensogroßen Namespace (das ist die Bezeichnung für eine LUN im NVMe-Protokoll) für NVMe-TCP auf demselben SSD Aggregat angelegt, um Messfehler durch die unterschiedliche Auslastung der zwei Controller auszuschließen. Der Controller hatte auf diesem Aggregat eine Grundlast von ca. 10MB/s und ein paar dutzend bis wenige hundert IOPS. Auch die VM hatte den ESX Server, auf dem sie lief, nicht exklusiv für sich sondern musste ihn sich mit einigen anderen VMs teilen, die allerdings so gut wie keine Last erzeugt haben, da darauf nicht aktiv gearbeitet wurde.
Mit folgenden Kommandos wurde die SVM im ONTAP aufgesetzt, um sowohl iSCSI als auch NVMe-TCP zu sprechen:
cl3::> vserver create -vserver nvme -data-services data-iscsi,data-nvme-tcp
[Job 33622] Job succeeded: Create nvme.
cl3::> vserver add-protocols -vserver nvme -protocols iscsi,nvme
cl3::> network route create -vserver nvme -destination 0.0.0.0/0 -gateway 10.92.0.1
cl3::> volume create -vserver nvme -volume nvme -size 100g -security-style unix
cl3::> volume create -vserver nvme -volume iscsi -size 100g -security-style unix
cl3::> network interface create -vserver nvme -lif lif1 -address 10.92.4.131 -netmask-length 16 -home-node cl3n1 -home-port a0a-1092 -service-policy default-data-iscsi
cl3::> network interface create -vserver nvme -lif lif2 -address 10.92.4.132 -netmask-length 16 -home-node cl3n1 -home-port a0a-1092 -service-policy default-data-nvme-tcp
cl3::> network interface create -vserver nvme -lif lif3 -address 10.92.4.133 -netmask-length 16 -home-node cl3n2 -home-port a0a-1092 -service-policy default-data-iscsi
cl3::> network interface create -vserver nvme -lif lif4 -address 10.92.4.134 -netmask-length 16 -home-node cl3n2 -home-port a0a-1092 -service-policy default-data-nvme-tcp
cl3::> vserver nvme create -vserver nvme
cl3::> vserver iscsi create -vserver nvme
cl3::> lun create -vserver nvme -path /vol/iscsi/disk.lun -size 80g -ostype linux
cl3::> igroup create -vserver nvme -igroup linux -ostype linux -protocol iscsi -initiator iqn.1994-05.com.redhat:52106ff8cfe5
cl3::> lun map -vserver nvme -path /vol/iscsi/disk -igroup linux
cl3::> nvme namespace create -vserver nvme -path /vol/nvme/disk.ns -size 80g -ostype linux
cl3::> nvme subsystem create -vserver nvme -subsystem subsys1 -ostype linux
cl3::> nvme subsystem host add -vserver nvme -subsystem subsys1 -host-nqn nqn.2014-08.org.nvmexpress:uuid:ec573842-6bfd-03a6-2faa-b22df85128f5
cl3::> nvme subsystem map add -vserver nvme -subsystem subsys1 -path /vol/nvme/disk.ns
Die Namen des Initiators (IQN bzw. NQN) findet man unter Linux mittels cat /etc/iscsi/initiatorname.iscsi
bzw. nvme show-hostnqn
.
Anschließend kann man sowohl die LUN als auch den NVMe-TCP Namespace unter Linux anbinden:
[root@linux ~]# touch /etc/multipath.conf
[root@linux ~]# systemctl start multipathd
[root@linux ~]# iscsiadm --mode discovery --type sendtargets --portal 10.92.4.131
10.92.4.131:3260,1082 iqn.1992-08.com.netapp:sn.6d588319c8bf11eda2e2d039ea049679:vs.3
10.92.4.133:3260,1084 iqn.1992-08.com.netapp:sn.6d588319c8bf11eda2e2d039ea049679:vs.3
[root@linux ~]# iscsiadm --mode node --target iqn.1992-08.com.netapp:sn.6d588319c8bf11eda2e2d039ea049679:vs.3 --portal 10.91.81.131 --login
Logging in to [iface: default, target: iqn.1992-08.com.netapp:sn.6d588319c8bf11eda2e2d039ea049679:vs.3, portal: 10.91.81.131,3260]
Login to [iface: default, target: iqn.1992-08.com.netapp:sn.6d588319c8bf11eda2e2d039ea049679:vs.3, portal: 10.91.81.131,3260] successful.
[root@linux ~]# iscsiadm --mode node --target iqn.1992-08.com.netapp:sn.6d588319c8bf11eda2e2d039ea049679:vs.3 --portal 10.91.81.133 --login
Logging in to [iface: default, target: iqn.1992-08.com.netapp:sn.6d588319c8bf11eda2e2d039ea049679:vs.3, portal: 10.91.81.133,3260]
Login to [iface: default, target: iqn.1992-08.com.netapp:sn.6d588319c8bf11eda2e2d039ea049679:vs.3, portal: 10.91.81.133,3260] successful.
[root@linux ~]# multipath -ll
3600a098038304446622b4f3264324f75 dm-2 NETAPP,LUN C-Mode
size=80G features='3 queue_if_no_path pg_init_retries 50' hwhandler='1 alua' wp=rw
|-+- policy='service-time 0' prio=50 status=active
| `- 33:0:0:0 sdb 8:16 active ready running
`-+- policy='service-time 0' prio=10 status=enabled
`- 34:0:0:0 sdc 8:32 active ready running
[root@linux ~]# modprobe nvme_tcp
[root@linux ~]# nvme connect-all -t tcp -a 10.92.4.132
[root@linux ~]# nvme list-subsys
nvme-subsys1 - NQN=nqn.1992-08.com.netapp:sn.cbdf97f6d87711ed80c800a098ba4d04:subsystem.subsys1
\
+- nvme1 tcp traddr=10.92.4.134,trsvcid=4420 live
+- nvme2 tcp traddr=10.92.4.132,trsvcid=4420 live
Nun tauchen die beiden neuen Devices unter Linux auf. Die iSCSI LUN unter /dev/mapper/3600a098038304446622b4f3264324f75
und der NVMe-TCP Namespace unter /dev/nvme1n1
.
Der Benchmark
Diese beiden Devices kann man nun mit FIO benchmarken. Dabei konzentrieren wir uns auf Random Reads und Random Writes, jeweils mit 4k Blockgröße. Folgende FIO Konfigurationsdatei wurde für die Random Reads verwendet:
[random-read]
ioengine=libaio
iodepth=32
rw=randread
bs=4k
size=50g
io_size=10g
numjobs=4
group_reporting=1
direct=1
Die Datei für die Random Writes sieht gleich aus, nur dass rw=randread
ersetzt wurde durch rw=randwrite
. Die Tests wurden zwei mal durchgeführt, einmal mit komplett frisch erzeugten (Thin Provisioned) LUNs/Namespaces auf dem Storage, und einmal nachdem diese einmal vollständig mit Zufallsdaten aus /dev/urandom
überschrieben wurden.
Die Ergebnisse habe ich in folgender Tabelle zusammengefasst.
Protokoll | Random Read IOPS | Random Read Latenz | Random Write IOPS | Random Write Latenz |
---|---|---|---|---|
iSCSI frische LUN | 59.000 | 2,1 ms | 46.400 | 2,7 ms |
NVMe-TCP frischer NS | 114.000 | 1,1 ms | 88.900 | 1,4 ms |
iSCSI gefüllte LUN | 52.100 | 2,4 ms | 46.300 | 2,7 ms |
NVMe-TCP gefüllter NS | 86.700 | 1,4 ms | 89.500 | 1.4 ms |
Ich muss zugeben, die Ergebnisse haben sogar mich ein wenig überrascht. Selbst auf unserer relativ alten Hardware (10Gbit Netzwerk, SATA SSDs, virtuelle Maschine anstatt physikalischem Server) war NVMe fast doppelt so perfomant, und das bei halber Latenz. Das ist ein ziemlich beachtlicher Unterschied. Noch dazu, da man diese Umstellung machen kann ohne irgend etwas an der Hardware oder Infrastruktur ändern zu müssen. Nur eine NVMe-oF Lizenz muss im Cluster vorhanden sein.
Fazit
NVMe-TCP ist ein Protokoll, das man nicht vernachlässigen sollte. Die Performance ist deutlich besser als bei der Verwendung von iSCSI, und der Aufwand, das Protokoll umzustellen, ist minimal. Zumindest unter Linux. Für Windows gibt es zumindest Stand heute (April 2023) keinen integrierten Treiber für NVMe-TCP. Ein kommerzieller Treiber von StarWind ist zwar verfügbar, getestet haben wir diesen allerdings noch nicht.
VMware hingegen unterstützt seit vSphere 7.0u3 NVMe-TCP, wenn man dort also die Flexibilität von NFS nicht benötigt kann man dort (z.B. für performance-kritische Workloads) auch heute schon NVMe-TCP nutzen.
Bei Fragen zum Thema SAN, Block Storage, oder NVMe-oF zögern Sie nicht auf uns zuzukommen!