Nvidia DGX Spark Cluster mit vLLM für Hermes Agent (von NousResearch)

Ich hatte zu Testzwecken auf meinem PC zum ersten Test ein LM Studio am laufen mit einer 16 GB Grafikkarte und 48 GB RAM. Darauf liefen meine ersten Schritte mit selbstgehosteten Sprachmodellen. Ich war fasziniert und wollte mehr erfahren, insbesondere, wie diese Technologie skaliert werden kann.

Zuerst lief bei mir OpenClaw mit unterschiedlichen Modellen, was eben in den PC „hineingepasst“ hat. OpenClaw war stark in der Verwendung von Werkzeugen, mir persönlich aber zu vergesslich. Also bin ich zu Hermes Agent gewechselt. Und dort wollte ich auch ein größeres Modell testen.

Nach ein paar Nächten habe ich dann alles ans Laufen gebracht. Es gab mehrere Bugs und Probleme: Qwen3-VL-MoE Pydantic-Bug (NGC vLLM 0.19), NVIDIA Container Runtime Bug, Mistral Tokenizer-Validator zu strikt, Worker-Reconnect nach Head-Restart, Qwen2.5-VL-72B Tool-Calling zu zurückhaltend, NCCL-Multinode-Hänger…

Jetzt habe ich als Ergebnis ein 235B-MoE-Vision-Modell auf 2× DGX Spark mit Hermes-Tool-Calling am Laufen. Das fühlt sich gut an. Nicht perfekt, aber gut. Die Spezifikationen meines „End“setups:

  • vLLM 0.20.2 (Mainline mit Spark-Optimierungen)
  • Qwen3-VL-235B-A22B-Instruct-AWQ (235B Total, 22B aktiv pro Token, MoE)
  • Tensor-Parallel über 2 Nodes via RoCE 200 GbE
  • Vision + Tool-Calling (Hermes-Parser)
  • 65k Context
  • ~5-8 Min Recovery-Zeit nach Reboot und Auto-Restart bei Crash

Voraussetzungen

  • 2× NVIDIA DGX Spark (GB10 Blackwell, 128 GB Unified Memory pro Node)
  • 200 GbE Direktverbindung über ConnectX-7 (NIC enp1s0f1np1)
  • Ubuntu 24.04 mit NVIDIA Driver 580.x, CUDA 13.2
  • Docker mit NVIDIA Container Runtime
  • User admin in Docker-Gruppe auf beiden Nodes

Updates durchführen

sudo apt update
sudo apt dist-upgrade -y
sudo fwupdmgr refresh
sudo fwupdmgr upgrade -y
sudo reboot

Netzwerk-Setup

Beide Nodes müssen mit dem 200 GbE Kabel miteinander verbunden sein. Im Folgenden wird diese Verbindung konfiguriert.

Statische IPs auf den 200 GbE Ports konfigurieren:

  • Node 1: 192.168.100.10/24 auf enp1s0f1np1, MTU 9000
  • Node 2: 192.168.100.11/24 auf enp1s0f1np1, MTU 9000

Netplan auf Node 1

sudo tee /etc/netplan/40-cx7.yaml > /dev/null <<'EOF'
network:
  version: 2
  ethernets:
    enp1s0f1np1:
      addresses:
        - 192.168.100.10/24
      dhcp4: no
      mtu: 9000
EOF

sudo chmod 600 /etc/netplan/40-cx7.yaml
sudo netplan apply
sudo reboot

Netplan auf Node 2

sudo tee /etc/netplan/40-cx7.yaml > /dev/null <<'EOF'
network:
  version: 2
  ethernets:
    enp1s0f1np1:
      addresses:
        - 192.168.100.11/24
      dhcp4: no
      mtu: 9000
EOF

sudo chmod 600 /etc/netplan/40-cx7.yaml
sudo netplan apply
sudo reboot

Damit sollten sich die zwei Nodes gegeneinander pingen können.

Auf Node 1:

ping -c 4 192.168.100.11

Auf Node 2:

ping -c 4 192.168.100.10

Schritt 1: Passwordless SSH einrichten

Damit der Node 1 im geclusterten Modus den Cluster Node 2 starten kann, benötigt der einen passwortlosen zugriff auf den Node 2. Dazu werden die ssh-Keys zur Authentifizierung ausgetauscht.

Auf beiden Nodes:

ssh-keygen -t ed25519 -N ''

Auf Node 1:

ssh-copy-id admin@192.168.100.11

Auf Node 2:

ssh-copy-id admin@192.168.100.10

Verifizieren von Node 1 aus:

ssh admin@192.168.100.11 'nvidia-smi --query-gpu=name --format=csv,noheader'
# Erwartet: NVIDIA GB10 (ohne Passwort)

Schritt 2: uvx installieren (beide Nodes)

uvx wird benötigt, um den Download via hf zu starten und die modelle auf beide Nodes zu verteilen.

curl -LsSf https://astral.sh/uv/install.sh | sh
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
which uvx

Schritt 3: Repo klonen (beide Nodes)

Hier verwende ich nicht das Repo von nvidia. Das basiert auf einer alten Version von vllm und hat einen Bug bei Multi-Node-Setups. Daher geht es mit dem auf git verfügbaren aktuellen vllm – extra für DGX Spark Multi Node – weiter:
https://github.com/eugr/spark-vllm-docker

cd ~
git clone https://github.com/eugr/spark-vllm-docker.git

Schritt 4: Image bauen und auf Node 2 verteilen

Nur auf Node 1:

cd ~/spark-vllm-docker
./build-and-copy.sh -c 192.168.100.11

Dauer: ~5-10 Min (lädt prebuilt Wheels, baut Runner-Image, kopiert auf Node 2).

Verifizieren:

docker images | grep vllm-node
ssh admin@192.168.100.11 'docker images | grep vllm-node'

Schritt 5: Cluster-Konfiguration erstellen

Auf Node 1:

cd ~/spark-vllm-docker
./launch-cluster.sh --setup --check-config

Bestätigt beide Nodes interaktiv. Erzeugt .env:

CLUSTER_NODES=192.168.100.10,192.168.100.11
COPY_HOSTS=192.168.100.11
LOCAL_IP=192.168.100.10
ETH_IF=enp1s0f1np1
IB_IF=rocep1s0f1,roceP2p1s0f1

Schritt 6: Modell laden (und automatisch auf beide Nodes verteilen)

cd ~/spark-vllm-docker
./hf-download.sh QuantTrio/Qwen3-VL-235B-A22B-Instruct-AWQ -c --copy-parallel

Dauer: ~15-30 Min (~120 GB Download + paralleler RoCE-Sync).

Verifizieren:

du -sh ~/.cache/huggingface/hub/models--QuantTrio--Qwen3-VL-235B-A22B-Instruct-AWQ
ssh admin@192.168.100.11 'du -sh ~/.cache/huggingface/hub/models--QuantTrio--Qwen3-VL-235B-A22B-Instruct-AWQ'

Schritt 7: Manueller Cluster-Test

Auf Node 1:

cd ~/spark-vllm-docker
./launch-cluster.sh exec \
  vllm serve QuantTrio/Qwen3-VL-235B-A22B-Instruct-AWQ \
    --served-model-name qwen3-vl-235b-awq \
    --port 8000 --host 0.0.0.0 \
    --tensor-parallel-size 2 \
    --distributed-executor-backend ray \
    --gpu-memory-utilization 0.85 \
    --max-model-len 65536 \
    --load-format fastsafetensors \
    --enable-auto-tool-choice \
    --tool-call-parser hermes \
    --trust-remote-code

Wartezeit bis Application startup complete: ~5-7 Min.

In zweiter SSH-Session auf Node 1 verifizieren:

curl -s http://localhost:8000/v1/models | python3 -m json.tool

Tool-Call-Test auch auf Node 1:

curl -s http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen3-vl-235b-awq",
    "messages": [{"role":"user","content":"Wie ist das Wetter in Berlin?"}],
    "tools": [{
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get current weather",
        "parameters": {
          "type": "object",
          "properties": {"location": {"type": "string"}},
          "required": ["location"]
        }
      }
    }],
    "tool_choice": "auto"
  }' | python3 -m json.tool

Erwartung: tool_calls ist gefüllt mit get_weather und Berlin.

Test stoppen mit Ctrl+C im laufenden Terminal.

Schritt 8: systemd-Service einrichten

Hier wird der Autostart Service auf dem Node 1 eingerichtet. Im Autostart wird auch der Node 2 mit hochgefahren, also ist kein Autostart auf Node 2 zusätzlich notwendig.

Auf Node 1:

sudo nano /etc/systemd/system/vllm-spark.service

Inhalt

[Unit]
Description=vLLM Cluster Mode (Qwen3-VL-235B-A22B-AWQ)
After=docker.service network-online.target
Requires=docker.service
Wants=network-online.target

[Service]
Type=simple
User=admin
WorkingDirectory=/home/admin/spark-vllm-docker

ExecStartPre=-/usr/bin/docker rm -f vllm_node
ExecStartPre=-/usr/bin/ssh admin@192.168.100.11 docker rm -f vllm_node

ExecStart=/home/admin/spark-vllm-docker/launch-cluster.sh exec \
  vllm serve QuantTrio/Qwen3-VL-235B-A22B-Instruct-AWQ \
    --served-model-name qwen3-vl-235b-awq \
    --port 8000 --host 0.0.0.0 \
    --tensor-parallel-size 2 \
    --distributed-executor-backend ray \
    --gpu-memory-utilization 0.85 \
    --max-model-len 65536 \
    --load-format fastsafetensors \
    --enable-auto-tool-choice \
    --tool-call-parser hermes \
    --trust-remote-code

ExecStop=/home/admin/spark-vllm-docker/launch-cluster.sh stop

Restart=on-failure
RestartSec=60
TimeoutStartSec=900
TimeoutStopSec=180

[Install]
WantedBy=multi-user.target

Aktivieren und starten:

sudo systemctl daemon-reload
sudo systemctl enable vllm-spark.service
sudo systemctl start vllm-spark.service
sudo journalctl -u vllm-spark -f

Die Zwei Nodes verbrauchen damit jeweils ca. 115 GB – 120 GB ihres Speichers.

Fertigstellen

Nun kann das Modell in Hermes konfiguriert werden. Dazu muss man einfach auf dem Hermes-Rechner die Konfiguration anpassen:

hermes model

Url: http://AAA.XXX.YYY.ZZZ:8000/v1
Model: qwen3-vl-235b-awq

Damit kann Hermes Agent das lokale Modell verwenden.

Schreiben Sie einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahren Sie, wie Ihre Kommentardaten verarbeitet werden.