Setup-Guide NVMe-oF unter Proxmox VE
NVMe over TCP
Standardmäßig kommuniziert NVMe-oF via TCP auf Port 4420.
Voraussetzung
Folgendes Paket muss installiert sein: nvme-cli
apt install nvme-cli -y
Anschließend kann das nvme_tcp Modul manuell geladen werden (nicht persistent über reboots hinweg):
modprobe nvme_tcp
Damit dieses beim booten standardmäßig geladen wird:
echo "nvme_tcp" > /etc/modules-load.d/nvme_tcp.conf
Anpassungen der hostnqn
NVMe-oF besitzt wie iSCSI (IQN) eine einzigartige Host-Adresse, die hostnqn.
Die NVMe Base Specification definiert für NVMe Qualified Names (NQN) zwei zulässige Formate. Standardmäßig wird beim Erzeugen der Datei /etc/nvme/hostnqn das UUID-basierte Format verwendet:
nqn.2014-08.org.nvmexpress:uuid:8e5e052f-8c2a-86d1-ed11-f3f8bac1a80e
Das Präfix nqn.2014-08.org.nvmexpress ist dabei ausschließlich für diese UUID-Form reserviert und darf nicht mit eigenen Bezeichnern kombiniert werden. Um stattdessen eine sprechende NQN mit dem Hostnamen zu verwenden, muss das domainbasierte Format genutzt werden:
nqn.<jjjj-mm>.<umgekehrte Domain>:<eindeutiger Bezeichner>
Dabei gelten folgende Bedingungen:
<jjjj-mm>: Jahr und Monat, zu dem die Domain im Besitz der Organisation war.- Die Domain wird in umgekehrter Reihenfolge angegeben (z. B.
de.examplefür example.de). - Der Bezeichner nach dem Doppelpunkt ist frei wählbar, muss aber innerhalb der NVMe-oF-Umgebung eindeutig sein – hierfür eignet sich der Hostname.
- Die Gesamtlänge darf maximal 223 Bytes betragen; empfohlen sind Kleinbuchstaben, Ziffern,
-,.und:.
Beispiel – Umstellung von UUID- auf Hostnamen-basierte NQN für den Host pve-01:
Vorher: nqn.2014-08.org.nvmexpress:uuid:8e5e052f-8c2a-86d1-ed11-f3f8bac1a80e Nachher: nqn.2005-03.de.example:pve-01
Die neue NQN wird in /etc/nvme/hostnqn eingetragen. Die Datei /etc/nvme/hostid bleibt unverändert bestehen. Nach der Änderung muss die neue NQN im Host-Mapping des Storage-Targets hinterlegt werden, da bestehende Zuordnungen auf der alten NQN basieren.
Discovery
NVMe-oF TCP Targets können nun wie folgt erkannt werden:
nvme discover --transport=tcp --traddr=<IP-STORAGE>
Je nach Implementation des NVMe-oF Protokolls auf dem SAN, werden hier alle verfügbaren Controller / IPs des SANs zurückgegeben. In manchen Fällen erhält man allerdings nur ein Log Entry von der IP, auf welchem der Request gestellt worden ist. Meist sind aber noch weitere IPs (bspw. bei hochverfügbaren Multicontroller Systemen) zum Verbinden verfügbar.
Eine Ausgabe mit allen Endpunkten sieht bspw. wie folgt aus:
root@pve-01:~# nvme discover --transport=tcp --traddr=192.168.50.10 --trsvcid=4420
Discovery Log Number of Records 2, Generation counter 7
=====Discovery Log Entry 0======
trtype: tcp
adrfam: ipv4
subtype: nvme subsystem
treq: not specified, sq flow control disable supported
portid: 1
trsvcid: 4420
subnqn: [FAKE]
traddr: 192.168.50.10
eflags: none
sectype: none
=====Discovery Log Entry 1======
trtype: tcp
adrfam: ipv4
subtype: nvme subsystem
treq: not specified, sq flow control disable supported
portid: 2
trsvcid: 4420
subnqn: [FAKE]
traddr: 192.168.51.10
eflags: none
sectype: none
=====Discovery Log Entry 2======
trtype: tcp
adrfam: ipv4
subtype: nvme subsystem
treq: not specified, sq flow control disable supported
portid: 2
trsvcid: 4420
subnqn: [FAKE]
traddr: 192.168.50.11
eflags: none
sectype: none
=====Discovery Log Entry 3======
trtype: tcp
adrfam: ipv4
subtype: nvme subsystem
treq: not specified, sq flow control disable supported
portid: 2
trsvcid: 4420
subnqn: [FAKE]
traddr: 192.168.51.11
eflags: none
sectype: none
Targets verbinden
Die in der Discovery zurückgegebenen Subsysteme können entweder einzeln pro Portal oder gesammelt verbunden werden.
Einzelnes Target verbinden:
nvme connect --transport=tcp --traddr=192.168.50.10 --trsvcid=4420 -n <subnqn>
Die <subnqn> entspricht dem Wert aus dem jeweiligen Discovery Log Entry. Der Befehl wird für jedes Portal (jede traddr) wiederholt — erst dadurch entstehen mehrere Pfade, die der native NVMe-Multipath zu einem Subsystem zusammenfasst.
Alle Targets eines Discovery-Portals verbinden:
nvme connect-all --transport=tcp --traddr=192.168.50.10 --trsvcid=4420
connect-all führt die Discovery aus und verbindet automatisch alle zurückgegebenen Einträge. Liefert das SAN im Discovery Log alle Controller-IPs zurück (siehe Discovery oben), genügt ein einziger Aufruf für sämtliche Pfade. Gibt das Target hingegen nur Einträge der angefragten IP zurück, muss connect pro Portal-IP ausgeführt werden.
Bereits verbundene Subsysteme werden bei erneutem Aufruf mit einer Fehlermeldung quittiert (unterdrückbar mit --quiet); doppelte Verbindungen entstehen dabei nicht.
Das Ergebnis kann anschließend mit nvme list (Namespaces) und nvme list-subsys (Pfade) kontrolliert werden, siehe Targets kontrollieren.
Hinweis: Auf diese Weise aufgebaute Verbindungen überleben keinen Neustart, siehe Automatisches Setup bei Start. Empfohlene Parameter wie --ctrl-loss-tmo=-1 sollten bereits beim manuellen Verbinden mitgegeben werden, siehe Optimierte Multipathing Einstellungen.
Targets kontrollieren
Die Targets / Pfade können mit nvme list-subsys -v, samt IO-Policy aufgelistet werden.
Allerdings werden die ANA-States (Vergleichbar mit ALUA) nicht angezeigt.
Diese können mit nvme list-subsys -o json /dev/nvme0n1 eingesehen werden (optimized / non-optimized bspw.).
Automatisches Setup bei Start
Targets verbinden
Mit nvme connect bzw. nvme connect-all aufgebaute Verbindungen überleben keinen Neustart. Für den automatischen Verbindungsaufbau beim Booten werden die Discovery-Parameter in der Datei /etc/nvme/discovery.conf hinterlegt, ein Eintrag pro Discovery-Portal:
--transport=tcp --traddr=192.168.50.10 --trsvcid=4420 [--ctrl-loss-tmo=-1 --keep-alive-tmo=10]
--transport=tcp --traddr=192.168.51.10 --trsvcid=4420 [--ctrl-loss-tmo=-1 --keep-alive-tmo=10]
--transport=tcp --traddr=192.168.50.11 --trsvcid=4420 [--ctrl-loss-tmo=-1 --keep-alive-tmo=10]
--transport=tcp --traddr=192.168.51.11 --trsvcid=4420 [--ctrl-loss-tmo=-1 --keep-alive-tmo=10]
Bitte ggf. Konfiguration in [] mit Optimierte Multipathing Einstellungen abgleichen.
Beim Systemstart führt der systemd-Service nvmf-autoconnect.service effektiv ein nvme connect-all gegen alle dort hinterlegten Einträge aus und stellt damit sämtliche Pfade automatisch wieder her.
Der Service muss aktiviert sein:
systemctl enable nvmf-autoconnect.service
Prüfen lässt sich der Status mit:
systemctl is-enabled nvmf-autoconnect.service
I/O Policy umstellen
Standardmäßig befinden sich die NVMe-oF Targets in der io-policy numa, um dies bei Start automatisch zu bspw. Round-Robin zu ändern, bedarf es einer udev-Regel.
Folgende I/O Policies sind verfügbar:
| Policy | Beschreibung | |
|---|---|---|
numa | Standard. Pfad mit geringster NUMA-Distanz zur anfragenden CPU, keine Lastverteilung. => Wenig sinnvoll, wenn mehrere optimized Pfade verfügbar sind | |
round-robin | Verteilt I/O reihum über alle optimierten Pfade (Bandbreitenaggregation). | |
queue-depth | Wählt den Pfad mit der geringsten aktuellen Queue-Tiefe. |
Zur Laufzeit (nicht reboot-fest):
echo "round-robin" > /sys/class/nvme-subsystem/nvme-subsys0/iopolicy #subsys0 entsprechend anpassen
Damit die Policy beim Start automatisch gesetzt wird, ist die udev-Regel /etc/udev/rules.d/71-nvme-io-policy.rules erforderlich:
ACTION=="add|change", SUBSYSTEM=="nvme-subsystem", ATTR{iopolicy}="round-robin"
Aktivieren und prüfen zur Laufzeit:
udevadm control --reload-rules
udevadm trigger --subsystem-match=nvme-subsystem
cat /sys/class/nvme-subsystem/nvme-subsys*/iopolicyOptional: Eingrenzung auf ein bestimmtes Subsystem
Die obige Regel greift für alle NVMe-Subsysteme, also auch für lokale NVMe-SSDs. Bei Single-Path-Geräten ist das wirkungslos und damit unkritisch. Soll die Policy dennoch gezielt nur für ein bestimmtes Target gesetzt werden, kann die Regel über die Subsystem-NQN oder Modell eingegrenzt werden.
NQN des Subsystems ermitteln:
cat /sys/class/nvme-subsystem/nvme-subsys*/subsysnqn
Regel mit NQN-Match:
ACTION=="add|change", SUBSYSTEM=="nvme-subsystem", ATTR{subsysnqn}=="nqn.2002-10.com.infortrend:raid.sn1234567.001", ATTR{iopolicy}="round-robin"
Regel mit Model-Match:
ACTION=="add", SUBSYSTEM=="nvme-subsystem", ATTR{subsystype}=="nvm", ATTR{model}=="GS 4000 Series", ATTR{iopolicy}="round-robin"
Mehrere Targets lassen sich über zusätzliche Zeilen oder ein Wildcard-Match (z.B. ATTR{subsysnqn}=="nqn.2002-10.com.infortrend:*") abdecken.
Weitere Empfehlungen
Die Verwendung von Jumbo Frames (MTU 9000) wird für einen höheren Durchsatz empfohlen. Sofern dies bei allen Teilnehmern konfiguriert werden kann.
Zu 5: Vergeben Sie eine gewünschte ID, wie der Storage genannt werden soll.
Zu 6: Wählen Sie Ihre zuvor erstellte VG aus.
Zu 7: Wenn mehrere Proxmox VE Nodes auf die LUN zugreifen können, aktivieren Sie „Shared Storage“.
In einem Proxmox VE-Cluster sind die neuen PV und VG möglicherweise nicht sofort auf allen Knoten sichtbar. Um sie auf allen Knoten sichtbar zu machen, können Sie entweder alle Knoten neu starten oder den folgenden Befehl auf allen Knoten ausführen:
pvscan --cache
Weitere Empfohlene Konfigurationen
Optimierte Multipathing Einstellungen
In der Standard-Konfiguration werden Pfade nach ca. 10 Minuten Downtime gelöscht. Diese werden anschließend auch nicht automatisch wieder hinzugefügt und es bedarf einem manuellen Eingriff.
Um dies zu verhindern, müssen bestimmte Parameter in der discovery.conf zusätzlich gesetzt werden:
--transport=tcp --traddr=192.168.50.10 --trsvcid=4420 --ctrl-loss-tmo=-1 --keep-alive-tmo=10
--transport=tcp --traddr=192.168.51.10 --trsvcid=4420 --ctrl-loss-tmo=-1 --keep-alive-tmo=10
| Parameter | Standardwert | Beschreibung | ||
--ctrl-loss-tmo=-1 | 600 s | Zeit, nach der ein unterbrochener Pfad endgültig entfernt wird. Mit -1 verbleibt der Pfad unbegrenzt im Zustand connecting und stellt die Verbindung nach Behebung der Störung selbstständig wieder her, statt gelöscht zu werden. | ||
--keep-alive-tmo=10 | 30 s | Keep-Alive-Timeout (KATO) zur Erkennung stiller Pfadausfälle, bei denen die Verbindung ohne TCP-Fehler verstummt (z. B. Switch-Ausfall im Pfad). Verkürzt die Worst-Case-Erkennungszeit von 30 auf 10 Sekunden. Werte unter 5 Sekunden sind nicht empfohlen (Gefahr unnötiger Verbindungsabrisse unter Last). |
Beobachtete Probleme
Fehlende Targets nach Boot
Persistente Konfiguration fehlt
Bitte Automatisches Setup bei Start (siehe oben) berücksichtigen
Netzwerkverbindung zu spät verfügbar
Der nvmf-autoconnect.service startet zwar erst nach network-online.target, dieses Target gilt jedoch bereits als erreicht, sobald ifupdown2 die Interfaces konfiguriert hat und nicht, wenn das Netzwerk tatsächlich Pakete transportiert.
LACP-Negotiation am Bond oder STP-Listening/Learning am Switch-Port (ohne Edge-Port-/PortFast-Konfiguration bis zu 30 Sekunden) können zu diesem Zeitpunkt noch andauern. Der einmalig ausgeführte nvme connect-all läuft dann in dieses Zeitfenster: TCP-Connects zu noch nicht erreichbaren Portalen schlagen fehl und werden nicht wiederholt.
Typisches Symptom: Nach dem Boot fehlen einzelne Pfade, ein manueller Connect funktioniert anschließend jedoch problemlos. --ctrl-loss-tmo=-1 hilft hier nicht, da der Parameter nur bestehende Verbindungen schützt und ein nie zustande gekommener Controller kann nicht reconnecten.
Diagnose
Zeitlichen Zusammenhang zwischen Netzwerk-Start und Connect-Fehlern prüfen:
journalctl -b -u networking.service -u nvmf-autoconnect.service -o short-precise
Liegen die connect failed-Meldungen nur Millisekunden bis wenige Sekunden hinter dem Abschluss von networking.service, ist die Race-Condition bestätigt.
Mögliche Lösung (statisch): Startverzögerung per systemd-Override
Statt die Unit-Datei direkt zu bearbeiten (würde bei Paket-Updates überschrieben), wird ein Drop-in angelegt:
systemctl edit nvmf-autoconnect.service
Inhalt:
[Service]ExecStartPre=/bin/sleep 15
Der Service wartet damit 15 Sekunden, bevor nvme connect-all ausgeführt wird und ausreichend für LACP-Verbindungen; bei STP ohne Edge-Port ggf. auf 30–45 Sekunden erhöhen. (Nachhaltiger lässt sich die Ursache switchseitig beheben, indem die Host-Ports als RSTP-Edge-Ports (PortFast) konfiguriert werden, damit entfällt das Forwarding-Delay-Fenster vollständig.) Das Drop-in liegt anschließend unter /etc/systemd/system/nvmf-autoconnect.service.d/override.conf.
Verifikation
Override prüfen und Reboot-Test durchführen:
systemd-delta --type=extended | grep nvmf
systemctl cat nvmf-autoconnect.service
Nach dem Neustart müssen alle Pfade vorhanden sein:
nvme list-subsys
Zusätzlich im Journal kontrollieren, dass der Service erst nach der Wartezeit ausgeführt wurde und keine Connect-Fehler mehr auftreten:
journalctl -b -u nvmf-autoconnect.serviceNoch Fragen?

Unser Experte für unter anderem Ceph, GRAID, PetaSAN und Proxmox

