Wer aus der VMware-Welt kommt, kennt das Raw Device Mapping (RDM): Eine LUN wird nicht als Datastore formatiert, sondern eins zu eins in die VM gereicht. Die VM sieht sie als eigenes Blockdevice, der Hypervisor ist nur Vermittler.
Die Gründe dafür sind heute noch dieselben:
- Datenbanken und große Volumes: Ein 20-TB-Volume will ich nicht als qcow2 oder LVM-Volume zwischen zwei Abstraktionsebenen haben. Direkt durchgereicht bleibt die Latenz minimal.
- Storage-Snapshots und Replikation: Snapshots, SnapMirror, Clones, Backup-Integration kann das Array besser als jeder Hypervisor. Mit RDM bleibt die VM-Disk ein ganz normaler Namespace auf dem Storage.
- Bestandsdaten: Eine LUN, die unter VMware als RDM lief, reiche ich unter Proxmox einfach wieder durch. Keine Konvertierung, keine Kopierzeit.
- Shared Disks für Failover-Cluster und ähnliche Setups.
Der Haken unter VMware: RDM funktioniert nur mit SCSI-basierten Protokollen, also Fibre Channel oder iSCSI. Ein NVMe-oF-Namespace lässt sich unter vSphere ausschließlich als VMFS-Datastore nutzen. Wer auf NVMe/TCP setzen will, weil es schlicht das modernere und schnellere Protokoll ist, steht dort vor einer Wand.
Proxmox VE kennt diese Wand nicht. Der Host ist ein Debian mit aktuellem Kernel, NVMe/TCP ist dort seit Jahren stabil, und was der Kernel als Blockdevice sieht, kann ich per `qm set` in jede VM stecken.
Vorweg: Das geht nicht über die GUI
Proxmox VE hat keinen Storage-Plugin-Typ für NVMe over TCP, wie es ihn für iSCSI gibt. Die Verbindung zum Target baue ich auf dem Host mit `nvme-cli` selbst auf, und auch das Durchreichen eines Blockdevices in eine VM läuft ausschließlich über die Kommandozeile. Am Ende sind es eine Handvoll Befehle, die Konfiguration landet sauber in `/etc/pve/qemu-server/`, und die GUI zeigt die Disk danach ganz normal an.
Testaufbau
- Proxmox VE Cluster mit zwei Nodes
- NVMe/TCP-Target mit zwei Portalen: `10.10.10.10` und `10.10.20.10`
- Dediziertes Storage-Netz, MTU 9000, pro Node zwei Interfaces in den beiden Storage-VLANs
- VM 100 (Linux-Datenbankserver), die einen 2-TB-Namespace als zweite Disk bekommt
Ob am anderen Ende ein NetApp ONTAP, ein TrueNAS oder ein Linux-Target mit `nvmet` hängt, ist für die Proxmox-Seite egal.
Schritt 1: nvme-cli installieren und Kernel-Modul laden
Auf jedem Node, der die VM später ausführen soll:
apt update
apt install nvme-cli
modprobe nvme-tcp
echo nvme_tcp > /etc/modules-load.d/nvme-tcp.confDie letzte Zeile sorgt dafür, dass das Modul nach einem Reboot wieder geladen wird. Ohne sie fehlen die Namespaces nach dem nächsten Neustart und die VM startet nicht.
Schritt 2: Host-NQN ermitteln
Jeder NVMe-Initiator hat einen eindeutigen NVMe Qualified Name, analog zum IQN bei iSCSI. Den trage ich auf dem Storage in das Subsystem ein, das den Namespace exportiert:
cat /etc/nvme/hostnqn
# nqn.2014-08.org.nvmexpress:uuid:3f2a9c1e-7b44-4d2a-9e0b-1c5e8f4a2d71
cat /etc/nvme/hostid
# 3f2a9c1e-7b44-4d2a-9e0b-1c5e8f4a2d71Fehlen die Dateien, erzeugt man sie mit nvme gen-hostnqn und uuidgen. Jeder Node im Cluster braucht seine eigene Host-NQN.
Schritt 3: Target discovern
nvme discover -t tcp -a 10.10.10.10 -s 4420Gekürzte Ausgabe:
Discovery Log Number of Records 2, Generation counter 7
=====Discovery Log Entry 0======
trtype: tcp
subtype: nvme subsystem
portid: 1
trsvcid: 4420
subnqn: nqn.1992-08.com.example:sn.a1b2c3d4:subsystem.pve-db01
traddr: 10.10.10.10
=====Discovery Log Entry 1======
trtype: tcp
subtype: nvme subsystem
portid: 2
trsvcid: 4420
subnqn: nqn.1992-08.com.example:sn.a1b2c3d4:subsystem.pve-db01
traddr: 10.10.20.10Das Subsystem `pve-db01` ist über beide Portale erreichbar. Genau das will ich für Multipath.
Schritt 4: Verbinden – mit beiden Pfaden
Der Linux-NVMe-Stack bringt natives Multipathing mit. Ich verbinde über beide Controller-IPs, der Kernel fasst die Pfade zu einem Device zusammen. Kein `multipathd`, keine `multipath.conf`.
nvme connect -t tcp -n nqn.1992-08.com.example:sn.a1b2c3d4:subsystem.pve-db01 -a 10.10.10.10 -s 4420
nvme connect -t tcp -n nqn.1992-08.com.example:sn.a1b2c3d4:subsystem.pve-db01 -a 10.10.20.10 -s 4420Alternativ erledigt `nvme connect-all -t tcp -a 10.10.10.10 -s 4420` Discovery und Connect in einem Rutsch. Kontrolle:
nvme list
Node Generic SN Model Namespace Usage Format FW Rev
-------------------- --------- -------- ----------- --------- -------------------- ----------- ------
/dev/nvme1n1 /dev/ng1n1 a1b2c3d4 NVMe Target 1 2.20 TB / 2.20 TB 4 KiB + 0 B 1.0
nvme list-subsys
nvme-subsys1 - NQN=nqn.1992-08.com.example:sn.a1b2c3d4:subsystem.pve-db01
\
+- nvme1 tcp traddr=10.10.10.10,trsvcid=4420 live optimized
+- nvme2 tcp traddr=10.10.20.10,trsvcid=4420 live non-optimizedZwei Pfade, ein Device `/dev/nvme1n1`. Die Pfadzustände kommen vom Storage per ANA (Asymmetric Namespace Access), dem NVMe-Pendant zu ALUA. Der Kernel nutzt den optimierten Pfad und schwenkt bei Ausfall um.
Zeigt `nvme list-subsys` stattdessen zwei separate Devices, ist natives Multipathing aus. Prüfen mit `cat /sys/module/nvme_core/parameters/multipath` – steht da `N`, hilft `nvme_core.multipath=Y` in der Kernel-Cmdline. Auf einem frischen Proxmox VE ist `Y` aber der Standard.
Schritt 5: Verbindung reboot-fest machen
Den Schritt vergessen viele, und dann startet nach dem nächsten Kernel-Update keine VM mehr. `nvme-cli` bringt einen systemd-Service mit, der beim Boot alle Einträge aus `/etc/nvme/discovery.conf` verbindet:
cat <<EOF > /etc/nvme/discovery.conf
--transport=tcp --traddr=10.10.10.10 --trsvcid=4420
--transport=tcp --traddr=10.10.20.10 --trsvcid=4420
EOF
systemctl enable nvmf-autoconnect.serviceDer Service läuft vor den Proxmox-Diensten, sodass die Namespaces da sind, bevor `pve-guests` die VMs mit `onboot: 1` startet. Einmal durchbooten und mit `nvme list` gegenprüfen.
Und: Schritte 1 bis 5 auf **jedem** Node des Clusters durchführen, sonst gibt es keine Live-Migration.
Schritt 6: Stabilen Device-Pfad ermitteln
`/dev/nvme1n1` ist keine verlässliche Adresse – nach einem Reboot oder mit einem weiteren Subsystem kann daraus `nvme2n1` werden. Für die VM-Config brauche ich einen Pfad, der am Namespace selbst hängt:
ls -l /dev/disk/by-id/ | grep nvme1n1
nvme-eui.6a1b2c3d4e5f60718293a4b5c6d7e8f9 -> ../../nvme1n1
nvme-uuid.9c7e2b11-4f3a-4e8d-b5a6-2d1c0f9e8a7b -> ../../nvme1n1
nvme-NVMe_Target_a1b2c3d4_1 -> ../../nvme1n1Ich nehme den `nvme-uuid.`- oder `nvme-eui.`-Link. Beide werden vom Storage vergeben und sind deshalb auf jedem Node identisch – das macht die Live-Migration später möglich.
Schritt 7: Namespace in die VM durchreichen
Der eigentliche RDM-Moment. Ich hänge den Namespace als `scsi1` an VM 100:
qm set 100 --scsihw virtio-scsi-single
qm set 100 --scsi1 /dev/disk/by-id/nvme-uuid.9c7e2b11-4f3a-4e8d-b5a6-2d1c0f9e8a7b,iothread=1,discard=on,ssd=1,backup=0,shared=1,serial=db01-dataDie Optionen:
- virtio-scsi-single + iothread=1 Jede Disk bekommt ihren eigenen I/O-Thread. Die Datenbank-Disk soll sich keinen Thread mit der OS-Disk teilen.
- discard=on TRIM/UNMAP wird bis zum Storage durchgereicht, Thin Provisioning funktioniert.
- ssd=1 Das Gast-OS behandelt die Disk als SSD.
- backup=0 vzdump und Proxmox Backup Server lassen die Disk in Ruhe. Mehr dazu unten.
- shared=1 Sagt Proxmox, dass das Device auf allen Nodes unter demselben Pfad erreichbar ist. Ohne dieses Flag verweigert `qm migrate` die Migration, weil die Disk als lokale Ressource gilt.
- serial= Frei wählbare Seriennummer, die der Gast sieht. Hilft bei mehreren durchgereichten Disks enorm.
In der VM-Config steht danach:
cat /etc/pve/qemu-server/100.conf
scsi0: local-zfs:vm-100-disk-0,discard=on,iothread=1,size=64G,ssd=1
scsi1: /dev/disk/by-id/nvme-uuid.9c7e2b11-4f3a-4e8d-b5a6-2d1c0f9e8a7b,backup=0,discard=on,iothread=1,serial=db01-data,shared=1,ssd=1
scsihw: virtio-scsi-singleDas funktioniert als Hotplug im laufenden Betrieb, die VM muss nicht gestoppt werden.
Schritt 8: In der VM prüfen
lsblk -o NAME,SIZE,SERIAL,MODEL
NAME SIZE SERIAL MODEL
sda 64G drive-scsi0 QEMU HARDDISK
sdb 2T db01-data QEMU HARDDISK
mkfs.xfs -L dbdata /dev/sdb
mkdir /data
echo 'LABEL=dbdata /data xfs defaults,noatime 0 0' >> /etc/fstab
mount /dataDie VM arbeitet jetzt direkt auf dem NVMe/TCP-Namespace, ohne Dateisystem, qcow2 oder LVM des Hypervisors dazwischen.
Live-Migration mit RAW Device Mapping
Wenn Schritt 5 auf allen Nodes erledigt und `shared=1` gesetzt ist, läuft die Migration wie bei jedem Shared Storage:
qm migrate 100 pve02 --onlineProxmox prüft, ob der `by-id`-Pfad auf dem Zielnode existiert. Deshalb steht nicht `/dev/nvme1n1` in der Config: Der `by-id`-Pfad ist auf beiden Nodes gleich, auch wenn der Namespace dort als `nvme3n1` auftaucht.
Die Fallstricke
Keine Proxmox-Snapshots für diese Disk. Ein durchgereichtes Blockdevice kann Proxmox nicht snapshotten. Die Antwort ist das Array: Snapshot auf dem Namespace, davor ein kurzes `fsfreeze` im Gast. Bei einer Datenbank ohnehin die konsistentere Lösung.
Backup bewusst entscheiden. Mit `backup=1` würde vzdump bei jedem Lauf die kompletten 2 TB als Raw-Stream sichern. Ich lasse die Disk aus dem PVE-Backup raus und sichere sie über das Array.
Nur eine VM pro Namespace. Es sei denn, Cluster-Software oder ein Cluster-Dateisystem koordiniert den Zugriff. Zwei normale VMs auf demselben Namespace zerstören sich gegenseitig das Dateisystem.
Finger weg vom Host. Der Namespace ist auf dem Proxmox-Host ein ganz normales Blockdevice. Ein versehentliches `pvcreate` oder `mkfs` dort schreddert die Daten der VM. Ich blende die Devices in LVM aus:
cat <<EOF > /etc/lvm/lvmlocal.conf
devices {
global_filter = [ "r|/dev/disk/by-id/nvme-uuid.9c7e2b11-.*|" ]
}
EOFBoot-Reihenfolge. `nvmf-autoconnect` muss durch sein, bevor die VM startet. Bei Storage-Netzen mit LACP oder STP hilft `startup: order=1,up=30` an der VM.
Storage-Netz sauber bauen. NVMe/TCP ist latenzempfindlicher als iSCSI: dediziertes VLAN, Jumbo Frames durchgängig, kein Routing, pro Pfad ein eigenes Interface.
RDM oder lieber LVM auf NVMe/TCP?
Für die Masse der VMs ist der klassische Weg sinnvoller: Namespace am Host verbinden, LVM Volume Group drauf, als Shared LVM in Proxmox einbinden. Dann verwaltet Proxmox die Disks und ich brauche nicht für jede VM einen eigenen Namespace.
RDM nehme ich gezielt für große Datenbanken, Volumes, die vom Array gesnapshottet oder repliziert werden, Bestandsdaten aus der VMware-Migration und Shared Disks. Beides lässt sich auf demselben Cluster und Storage mischen.
Fazit
Was unter VMware mit NVMe over TCP nicht geht, ist unter Proxmox VE eine Sache von zehn Minuten: Namespace per `nvme-cli` verbinden, Autoconnect einrichten, `by-id`-Pfad ermitteln, mit `qm set` und `shared=1` in die VM hängen. Natives Multipathing bringt der Kernel mit, Live-Migration funktioniert, und die VM arbeitet ohne Zwischenschicht auf dem Storage. Die GUI hilft dabei nicht – aber die paar Zeilen VM-Config sind nachvollziehbar und sauber dokumentierbar. Für mich einer der Punkte, an denen Proxmox VE gegenüber vSphere klar vorne liegt.
FAQ: Proxmox RAW Device Mapping mit NVMe over TCP
Unterstützt Proxmox VE NVMe over TCP offiziell?
Es gibt keinen eigenen Storage-Plugin-Typ. Die Verbindung läuft über `nvme-cli` und den Debian-Kernel, was stabil und performant ist. Für LVM-on-NVMe/TCP und Disk-Passthrough reicht das vollkommen.
Geht das auch mit iSCSI?
Ja, derselbe Ablauf. Statt `nvme connect` nutzt man `iscsiadm`, das Device liegt unter `/dev/disk/by-id/scsi-…` oder bei Multipath unter `/dev/mapper/…`. Der `qm set`-Befehl bleibt identisch.
Kann ich eine RDM-Disk später in eine normale Proxmox-Disk umwandeln?
Ja, mit `qm disk move` auf ein Proxmox-Storage. Umgekehrt geht es mit `qemu-img convert` auf das Blockdevice.
Warum sehe ich die Disk nicht in der GUI zum Anlegen?
Die GUI legt nur Volumes auf konfigurierten Storages an. Durchgereichte Blockdevices werden per `qm set` oder direkt in der Config eingetragen – angezeigt werden sie danach aber ganz normal.




Schreibe einen Kommentar