RDMA-Einrichtung in der Praxis + Cluster-Uplink-Design
Die vorangegangenen Artikel der N-Reihe haben für RDMA argumentiert (N02) und die Topologieoptionen durchgegangen (N04, N05). Dieser hier ist der praktische Teil: Treiber installieren, den Pfad nachweisen, GPUDirect einschalten, NCCL validieren – und dann eine Ebene höher überlegen, wie sich das gesamte Cluster mit der Welt verbindet.
Wir setzen Ubuntu 22.04 oder 24.04 voraus, Mellanox/NVIDIA ConnectX-5- oder ConnectX-6-NICs und entweder InfiniBand HDR/NDR oder RoCEv2 über ein verlustfreies Ethernet-Fabric. Die Befehle sind genau die, die wir auf Kentino-Testaufbauten eintippen, bevor ein Kentino AI Cluster mit 4 Knoten ausgeliefert wird.
Treiber: MLNX_OFED oder Upstream rdma-core?
| Option | Was Sie bekommen | Wann Sie sie wählen |
|---|---|---|
| MLNX_OFED (jetzt NVIDIA DOCA-OFED) | Von NVIDIA getestetes Treiberpaket, GPUDirect peermem, perftest, mlxconfig | Produktive KI-Cluster mit ConnectX-6/7 und GPUDirect |
Upstream rdma-core + in-tree mlx5
|
Das, was Ubuntu mitliefert, kein zusätzliches Repository | Laborrechner, Einzelknoten, kein GPUDirect, keine Firmware-Werkzeuge |
Für alles, was im Produktivbetrieb NCCL-Verkehr trägt, installieren Sie MLNX_OFED. Das Upstream-Modul mlx5 funktioniert zwar, aber Sie verlieren mlxconfig, das mitgelieferte perftest, und – am wichtigsten – ein kernelseitiges nvidia-peermem in der Version, die gegen denselben OFED-Baum getestet wurde.
Eine saubere Installation unter Ubuntu 22.04 mit Kernel 5.15.x:
tar xf MLNX_OFED_LINUX-*.tgz && cd MLNX_OFED_LINUX-*
sudo ./mlnxofedinstall --add-kernel-support --with-nvmf --force
sudo /etc/init.d/openibd restart && sudo systemctl enable openibd
Das Flag --add-kernel-support ist entscheidend. Lassen Sie es bei einem Kernel außerhalb der OFED-Matrix weg, scheitert der DKMS-Build stillschweigend – und Sie betreiben unbemerkt das Standardmodul mlx5 weiter. Prüfen Sie den User-Space-Stack mit dpkg -l | grep -E 'libibverbs|rdma-core|mlnx-ofed'.
Den Link hochbringen und prüfen, ob die NIC das Fabric sieht
Drei Befehle verraten Ihnen alles Wichtige:
sudo mst start && mst status # firmware tools
ibstat # port state, width, speed
ibv_devinfo -v # GIDs, max_qp, MTU, hw revision
Ein gesunder Port zeigt State: Active, Physical state: LinkUp, die erwartete Rate: (z. B. 200) und den richtigen Link layer: (InfiniBand oder Ethernet). Die beiden Felder, die gerne übersehen werden:
-
Link layer: InfiniBandvs.Ethernet. Eine ConnectX im Dual-Mode wird umgeschaltet mitmlxconfig -d /dev/mst/mt4125_pciconf0 set LINK_TYPE_P1=2(1=IB, 2=Ethernet). Neustart erforderlich. -
Rate:entspricht der erwarteten Geschwindigkeit. Ein 200-Gb/s-NDR-Port, der mit 100 Gb/s hochkommt, ist der häufigste stille Fehler: schlechtes Kabel, falsche DAC-Länge oder ein fest heruntergesetzter Switch-Port. Prüfen Sie das, bevor Sie benchmarken.
Bestätigen Sie bei RoCE zusätzlich, dass v2 ausgewählt ist (v1 ist Ethertype 0x8915, v2 nutzt UDP/4791 und ist das, was jeder moderne Stack verwendet): sudo cma_roce_mode -d mlx5_0 -p 1 -m 2.
Subnet Manager (nur InfiniBand)
InfiniBand ist kein Ethernet – im Fabric wird nichts geroutet, solange kein Subnet Manager (SM) LIDs vergeben hat. In einem Laborfabric: sudo apt install opensm && sudo systemctl enable --now opensm auf einem Knoten. Im Produktivbetrieb lassen Sie den eingebetteten SM auf dem Switch laufen. Zwei Software-SMs, die gegeneinander laufen, bedeuten einen Nachmittag Fehlersuche, den niemand braucht. RoCE hat keinen SM – das Routing ist Aufgabe des Ethernet-Fabrics, weshalb sich die RoCE-Konfiguration vor allem um QoS auf dem Switch dreht und kaum um den Host.
Nachweisen, dass RDMA wirklich funktioniert: perftest
Bevor Sie NCCL oder ein Framework starten, weisen Sie die Leitung nach mit perftest. Zwei Knoten, Server zuerst:
# server (node A) # client (node B)
ib_send_bw -d mlx5_0 -F --report_gbits -D 10
ib_send_bw -d mlx5_0 -F --report_gbits -D 10 10.10.1.1
Erwartete Werte in einem sauberen Fabric:
| Link |
ib_send_bw (große Nachrichten) |
ib_send_lat (2 Byte) |
|---|---|---|
| 100 Gb/s EDR / 100 GbE RoCE | 95–98 Gb/s | 1.0–1.5 µs |
| 200 Gb/s HDR / 200 GbE RoCE | 188–197 Gb/s | 0.9–1.3 µs |
| 400 Gb/s NDR | 370–395 Gb/s | 0.8–1.1 µs |
Liegen Sie 20% unter diesen Werten, fangen Sie nicht mit NCCL-Tuning an. Dann stimmt das Fabric nicht. Prüfen Sie der Reihe nach: (1) MTU, (2) PFC bei RoCE, (3) das Paar aus Kabel und Transceiver, (4) PCIe-Generation und Lane-Anzahl der NIC, (5) NUMA-Platzierung. Latenzen unter einer Mikrosekunde sind für NDR im selben Rack erreichbar; alles über 5 µs in einem RoCE-Fabric mit einem einzigen Switch ist defekt.
GPUDirect RDMA: den DMA-Pfad einschalten
Der ganze Sinn von RDMA in einem KI-Cluster besteht darin, dass die NIC GPU-Speicher direkt liest und schreibt und den Host umgeht. Dafür braucht es nvidia-peermem (oder, auf neueren Kerneln, DMA-BUF – NVIDIA empfiehlt inzwischen DMA-BUF, wo der Kernel es unterstützt, doch die meisten Produktiv-Stacks liefern weiterhin peermem aus).
sudo modprobe nvidia-peermem
lsmod | grep nvidia_peermem
echo nvidia-peermem | sudo tee /etc/modules-load.d/nvidia-peermem.conf
Lädt es nicht, wurde der Kernel nicht gegen eine OFED-fähige RDMA-Peer-Memory-API gebaut. Die Installationsreihenfolge zählt: zuerst OFED, dann der NVIDIA-Treiber, dann nvidia-peermem – peermem wird zur Installationszeit gegen die OFED-Header gebaut.
Weisen Sie den durchgängigen Pfad mit einem RDMA-Write von GPU zu GPU nach:
# server # client
ib_write_bw -d mlx5_0 --use_cuda=0 -F --report_gbits -D 10
ib_write_bw -d mlx5_0 --use_cuda=0 -F --report_gbits -D 10 10.10.1.1
--use_cuda=0 registriert CUDA-Speicher auf GPU 0 als RDMA-Puffer. Liegt das Ergebnis innerhalb weniger Prozent des Falls mit Hostspeicher, funktioniert GPUDirect. Ist es 5× langsamer, läuft der Pfad über den Hostspeicher – meist ein Ladeproblem von peermem oder eine PCIe-Topologie, bei der NIC und GPU an gegenüberliegenden NUMA-Knoten hängen.
MTU und PFC für RoCE (hier entscheidet sich das Schicksal von RoCE-Clustern)
RoCEv2 über ein verlustfreies Ethernet-Fabric braucht drei Dinge im Zusammenspiel:
- Eine große MTU durchgängig. Stellen Sie 9000 auf jeder NIC, jedem Switch-Port und jedem Router-Hop ein. RoCE wählt die größte MTU im IB-Stil, die in die Ethernet-MTU passt – 9000 im Ethernet ergeben eine RoCE-MTU von 4096 Byte, und genau die wollen Sie.
- PFC auf der RDMA-Priorität. Eine Pause auf Link-Ebene, die Paketverluste in einer festgelegten Verkehrsklasse verhindert. Gängige Praxis: RDMA auf Priorität 3, alles andere auf Priorität 0.
- ECN-Markierung auf Switches und NICs. ECN ist das langfristige Stausignal, PFC die kurzfristige Notbremse. ECN erledigt die Arbeit die meiste Zeit; PFC greift nur, wenn ECN nicht hinterherkommt.
Auf der Host-Seite, mit mlnx_qos:
sudo mlnx_qos -i enp1s0f0 --pfc 0,0,0,1,0,0,0,0 # PFC on prio 3
sudo mlnx_qos -i enp1s0f0 --trust dscp
echo 106 | sudo tee /sys/class/infiniband/mlx5_0/tc/1/traffic_class
Der DSCP-Wert (26) und die PFC-Priorität (3) müssen an jedem Hop übereinstimmen. Das Switch-Fabric muss das spiegeln: PFC auf Priorität 3 aktiviert, ECN-Markierung auf diesen Queues, verlustfreie Pufferkonfiguration und ein Headroom pro Port, der auf das BDP der längsten Strecke ausgelegt ist.
Einen Switch von einem Hersteller mit dokumentierten RoCE-Vorlagen zu kaufen (NVIDIA Spectrum, Arista, Cisco Nexus 9000), spart eine Woche. Die PFC-Konfiguration auf einer generischen Broadcom-Whitebox von Hand zu bauen, ist machbar, aber ein Projekt für sich. Wir haben es gemacht. Wir empfehlen es nicht.
DCQCN (Data Center Quantized Congestion Notification) ist der Regelkreis, der PFC und ECN verbindet: ECN markiert Pakete, wenn eine Queue volläuft, der Empfänger schickt ein CNP zurück, der Sender drosselt und fährt wieder hoch, sobald die Queue abläuft. PFC ist der Rückfall, wenn DCQCN nicht schnell genug reagiert. Auf moderner ConnectX-6/7-Firmware ist es standardmäßig aktiv, und bei 4–16 Knoten genügen die Voreinstellungen. Das Feintuning (Alpha, Zielrate, Byte-/Timer-Schwellen) ist etwas für Leute in Größenordnungen, in denen 0.5% beim Allreduce zwei Wochen Arbeit wert sind.
NCCL: die Variablen, auf die es ankommt
NCCL ist die Schicht, die RDMA für PyTorch, JAX, DeepSpeed, vLLM mit Tensor-Parallelität und Ähnliches nutzt. Sie erkennt die Umgebung meist korrekt von selbst. Vier Umgebungsvariablen tauchen in jedem produktiven Startskript auf:
| Variable | Was sie bewirkt | Wann Sie sie setzen |
|---|---|---|
NCCL_IB_DISABLE |
1 erzwingt TCP-Sockets statt IB/RoCE |
Nur zur Fehlersuche |
NCCL_SOCKET_IFNAME |
Schnittstelle für den NCCL-Bootstrap (nicht der Datenpfad) | Immer – zeigen Sie auf die Management-NIC, damit der Bootstrap nicht versehentlich über das RDMA-Fabric läuft |
NCCL_IB_HCA |
Welche HCA(s) NCCL für die Datenebene nutzt | Knoten mit mehreren NICs – explizit schlägt automatisch |
NCCL_NET_GDR_LEVEL |
Wie aggressiv GPUDirect RDMA abhängig von der PCIe-Topologie genutzt wird |
PIX/PHB wenn GPUs und NICs sich einen PCIe-Switch / NUMA-Knoten teilen |
Ein funktionierender Start auf einem Cluster mit 4 Knoten und 4 GPUs pro Knoten:
export NCCL_SOCKET_IFNAME=eno1 # 1 GbE management network
export NCCL_IB_HCA=mlx5_0,mlx5_1 # both RDMA NICs
export NCCL_IB_GID_INDEX=3 # RoCE v2 GID
export NCCL_NET_GDR_LEVEL=PHB
export NCCL_DEBUG=INFO # one-shot, then drop to WARN
mpirun -np 16 -N 4 --hostfile hosts -x NCCL_SOCKET_IFNAME \
-x NCCL_IB_HCA -x NCCL_IB_GID_INDEX -x NCCL_NET_GDR_LEVEL \
-x NCCL_DEBUG ./build/all_reduce_perf -b 8 -e 8G -f 2 -g 1
Die Ausgabe von NCCL_DEBUG=INFO ist beim ersten Lauf Pflichtlektüre. Sie verrät Ihnen, welchen Transport NCCL gewählt hat (Channel ... via NET/IB/0 GDR) – je Rank und je Channel. Taucht irgendwo via NET/Socket auf, wird der RDMA-Pfad nicht genutzt – dann haben Sie nicht das getestet, was Sie zu testen glaubten.
Validieren mit nccl-tests
nccl-tests validiert den gesamten Stack – Treiber, OFED, peermem, NCCL, Netzwerk – durchgängig. Die entscheidende Zahl ist die Busbandbreite (busbw), nicht die Algorithmusbandbreite (algbw). Die Busbandbreite normalisiert die Ring-/Baumgröße und ist der Wert, den Sie mit der Leitungsgeschwindigkeit der NIC vergleichen.
| Clustergröße | Erwartete Allreduce-busbw (große Nachrichten) |
|---|---|
| 1 Knoten, 4 GPUs über NVLink | 200–400 GB/s |
| 1 Knoten, 8 GPUs über NVLink | 250–500 GB/s |
| 2 Knoten, je 4 GPUs, 200 GbE RDMA | 20–24 GB/s |
| 4 Knoten, je 4 GPUs, 200 GbE RDMA + GDR | 20–22 GB/s |
Allreduce zwischen Knoten ist auf etwa Leitungsrate der NIC / 2 begrenzt (Allreduce sendet und empfängt jedes Byte einmal pro Rank). 200 Gb/s ≈ 25 GB/s Obergrenze; beobachtete 22 GB/s sind gesund. Bricht der Wert beim Übergang von 1 auf 2 Knoten um das 5-Fache ein, wird RDMA für den Hop zwischen den Knoten nicht genutzt. Lesen Sie die Ausgabe von NCCL_DEBUG=INFO aufmerksam.
Jetzt der Blick von oben: das Uplink-Design des Clusters
Alles bisher Beschriebene betrifft die Datenebene – das Fabric, über das GPUs miteinander sprechen. Die andere Hälfte eines brauchbaren Clusters ist der Uplink: wie dieses Fabric mit der Außenwelt verbunden wird. Der Uplink wird ständig falsch gebaut.
Ein Kentino AI Trainingscluster mit 4 Knoten hat drei Beziehungen nach außen:
- Das Unternehmensnetz / WAN – Modell-Registries, Datensatzspeicher (S3, NFS, MinIO), Git, Container-Registries, Telemetrie.
- Entwickler-Workstations – Ingenieure, die sich per SSH verbinden, Jobs starten, Checkpoints herauskopieren und Jupyter betreiben.
- Andere Cluster – ein zweites Trainingscluster, ein Inferenzcluster, ein CI-/Eval-Cluster.
Die Bandbreitenrechnung
Wenn 4 Knoten Allreduce mit jeweils rund 22 GB/s fahren, bewegt das interne Fabric rund 700 Gb/s aggregierten Ost-West-Verkehr. Der Uplink muss das nicht erreichen. Er muss zur Datenaufnahmerate:
- 4 Knoten × 4 GPUs × rund 1 GB/s pro GPU (Bild-/Videomodell) = 16 GB/s ≈ 128 Gb/s dauerhaftes Lesen aus dem Objektspeicher.
- LLM-Pretraining auf tokenisiertem Text ist weit anspruchsloser – 1–4 Gb/s, weil Token dicht gepackt sind und ein Batch lange läuft.
- Ein Fine-Tuning, das häufig Checkpoints nachlädt, erreicht für wenige Sekunden Spitzen von 40 Gb/s und fällt dann fast auf null.
| Workload | Dauerhafte Aufnahme | Spitze | Uplink |
|---|---|---|---|
| LLM-Pretraining (tokenisierter Text) | 1–4 Gb/s | 20 Gb/s | 25 GbE |
| Training von Bild-/Videomodellen | 50–150 Gb/s | 200 Gb/s | 2× 100 GbE LAG oder 1× 200 GbE |
| Fine-Tuning / RLHF mit Checkpoint-Wechsel | 5–20 Gb/s | 50 Gb/s | 25–100 GbE |
| Inferenzcluster hinter einem Load Balancer | 5–50 Gb/s | modellabhängig | 25–100 GbE |
Häufiger Fehler: €40k für einen 400-GbE-Uplink auszugeben, weil das interne Fabric 400 GbE liefert. Falsche Zielgröße. Die richtige Zielgröße ist die Leserate des Datensatzes. Wir haben Cluster mit 4 Knoten und einem 25-GbE-Uplink gebaut, die wochenlang unter Volllast auf LLM-Token-Daten liefen.
Aggregierte vs. zugeteilte Bandbreite
Ein Knoten mit vier 100-GbE-NICs hat 400 Gb/s aggregierte Leitungskapazität. Diese Kapazität wird pro Flow zugeteilt, nicht gebündelt. Eine einzelne TCP-Verbindung zwischen zwei IPs nutzt genau eine NIC – höchstens 100 Gb/s. ECMP und Per-Flow-Hashing verteilen verschiedene Flows auf die vier NICs.
-
Allreduce besteht aus vielen parallelen Flows – einem pro Channel und Peer. NCCL verteilt von Haus aus über mehrere NICs (
NCCL_IB_HCAmit beiden Einträgen). 4× 100 GbE kommt für NCCL funktional nahe an 400 Gb/s heran. - Ein einzelner Datensatz-Stream (ein HTTP GET, das einen 1-TB-Shard zieht) ist ein einziger Flow. 4× 100 GbE geben ihm 100 Gb/s, nicht 400. Um die Summe zu nutzen, muss der Dataloader parallele Streams öffnen – was DALI, WebDataset und MosaicMLs Streaming allesamt von Haus aus tun.
Trennung der Fabrics
- 100/200/400 GbE RoCE oder HDR/NDR InfiniBand
- NCCL, Datensatz-Lesezugriffe, Checkpoint-Schreibvorgänge
- Verlustfrei, PFC/ECN, dedizierte Switches
- Steril – kein Verkehr außerhalb der KI-Last
- 1 GbE oder 10 GbE
- SSH, Prometheus, NTP, syslog, IPMI/BMC
- Günstige Switches, Standard-L2/L3, kein spezielles QoS
- Funktioniert immer, auch wenn das Daten-Fabric ausgefallen ist
Betreiben Sie immer zwei Fabrics. Die Management-Ebene darf für ihre Funktion nicht von der Datenebene abhängen – Sie brauchen SSH-Zugang, wenn das RDMA-Fabric kaputt ist.
Die Management-Ebene ist nicht optional. Wenn das Daten-Fabric ausfällt – defekter Transceiver, falsche PFC-Konfiguration, abgestürzter Switch –, brauchen Sie einen SSH-Pfad, der nicht vom kaputten Fabric abhängt. Einen RoCE-Sturm über genau die Leitung zu debuggen, die stürmt, ist ein Fehler, den man genau einmal macht. IPMI/BMC und NTP gehören ebenfalls hierher; ein Uhrenversatz bleibt unsichtbar, bis Ihr verteiltes Framework anfängt, falsche Gradienten zu produzieren.
BGP unnumbered für geroutetes Clos
Ab etwa 8 Knoten wird L2-Leaf-Spine unangenehm – Spanning-Tree-Grenzen, Skalierung der MAC-Tabellen, Broadcast-Stürme, kein natives Multipath. Die moderne Antwort ist ein geroutetes L3-Clos: Jede Leaf-Spine-Verbindung ist unnumbered, BGP trägt die Routen, ECMP verteilt die Flows über die Spines.
BGP unnumbered peert über IPv6-Link-Local-Adressen, die der Kernel automatisch vergibt – damit entfällt die /31-Buchhaltung pro Link vollständig. Ein Leaf mit FRRouting unter Linux sieht ungefähr so aus:
router bgp 65001
neighbor swp1 interface remote-as external
neighbor swp2 interface remote-as external
address-family ipv4 unicast
network 10.1.1.0/24
redistribute connected
Jedes Leaf und jedes Spine ist ein eigenes AS, eBGP verteilt die Loopbacks, ECMP über die Spines gibt es gratis dazu. RFC 7938 dokumentiert das Muster; Cumulus/NVIDIA, Arista, Cisco und Juniper unterstützen unnumbered BGP heute alle. Bei 4 Knoten genügt ein einzelner Switch. Bei 8–16 Knoten mit zwei Spines fängt geroutetes Clos an, sich zu rechnen. Ab 16 Knoten ist es die einzige sinnvolle Antwort.
Anbindung an andere Cluster
Setzen Sie ein Trainings- und ein Inferenzcluster nicht auf dasselbe RDMA-Fabric. Training bedeutet riesige, stoßweise Allreduce-Lasten, Inferenz gleichmäßige kleine Nachrichten; die QoS-Anforderungen laufen auseinander, und ein aus dem Ruder gelaufener Trainingslauf hungert den Inferenzpfad aus. Zwei getrennte Fabrics, die sich an einem L3-Router mit normalem IP-Routing treffen, sind die richtige Antwort. Steuerverkehr zwischen den Clustern – Job-Queues, Log-Versand, Artefakttransfer – läuft über die Management-Ebene oder eine dedizierte Ethernet-Verbindung zwischen den Clustern. Eine 70B-Gewichtsdatei ist rund 140 GB groß, ein Checkpoint das 2-Fache. Bei 25 GbE dauert das rund 90 Sekunden, bei 100 GbE rund 22 Sekunden. Planen Sie mit dem oberen Ende.
Was als Nächstes zu tun ist
Ein sinnvoller Ablauf, um RDMA auf einem frischen Cluster in Betrieb zu nehmen:
-
Zuerst verkabeln und den Link Layer prüfen. Führen Sie
ibstatauf jedem Knoten aus. Gleiche Rate, gleiche MTU, gleicher Link Layer. Bringen Sie die physikalische Ebene in Ordnung, bevor Sie Software anfassen. -
Installieren Sie MLNX_OFED, nicht nur
rdma-core, wenn GPUDirect oder NCCL im Spiel sind. Passen Sie den OFED-Build zum laufenden Kernel. -
Führen Sie
perftestzwischen jedem Knotenpaar aus, bevor Sie Frameworks anfassen. Werte innerhalb von 5% des theoretischen Maximums = gesund. Alles darunter = anhalten und beheben. -
Laden Sie
nvidia-peermem, und weisen Sie es nach mitib_write_bw --use_cuda=0. Das Ergebnis sollte dem Fall mit Hostspeicher entsprechen. - Konfigurieren Sie PFC, ECN und DCQCN, wenn Sie RoCE einsetzen. Unter IB gibt es diesen Schritt nicht – das ist der halbe Grund, warum Leute IB wählen.
-
Führen Sie
nccl-testsAllreduce auf 2 Knoten aus, dann auf 4, dann auf 8. Prüfen Sie die Ausgabe vonNCCL_DEBUG=INFObeim ersten Lauf jeder Clustergröße. Vergewissern Sie sich, dassNET/IB ... GDRauftaucht. - Trennen Sie die Fabrics. Management über günstiges 1/10 GbE. Daten über das RDMA-Fabric. Nicht mischen.
- Dimensionieren Sie den Uplink nach Ihrer Datensatz-Aufnahmerate, nicht nach der internen Fabric-Geschwindigkeit. Die meisten Cluster brauchen weit weniger externe Bandbreite, als sie haben.
- Ab 8 Knoten planen Sie ein geroutetes Clos mit BGP unnumbered. Unter 8 Knoten genügt ein einzelner Switch.
Die Folgeartikel der N-Reihe behandeln die Latenzzerlegung (N06) und die Routing-Komplexität (N07) – dort stecken das DCQCN-Tuning und die Pathologien des ECMP-Hashings. Die K-Reihe setzt hier an mit verteiltem Training (K02), Inferenzclustern (K03) und Storage (K04) – die alle voraussetzen, dass der RDMA-Stack von dieser Seite bereits funktioniert.
Dies ist Teil des Kentino Wiki, einer Referenzreihe zu KI-Compute und den Systemen, die es verbinden. Korrekturen willkommen unter info@kentino.com.