vLLM auf Kubernetes mit gemeinsamem NFS-Modellspeicher bereitstellen
Ihre vLLM-Pods laden möglicherweise bei jedem Start dieselben großen Modelldateien erneut herunter.
Wenn Sie LLM-Inferenz auf Kubernetes betreiben, haben Sie vielleicht den naheliegenden Weg gewählt: vLLM wird so konfiguriert, dass das Modell beim Start des Pods von Hugging Face geladen wird. Das funktioniert, führt später aber zu Problemen:
- Ein Pod fällt um 2 Uhr nachts aus. Der Ersatz-Pod kann keine einzige Anfrage bedienen, bevor er nicht mehrere Minuten lang viele Gigabyte an Modellgewichten von Hugging Face heruntergeladen hat.
- Sie müssen bei steigendem Traffic skalieren. Jeder neue Pod lädt das Modell separat herunter, nutzt dieselbe Netzwerkbandbreite und verzögert dadurch die Reaktion auf die höhere Nachfrage.
- Hugging Face setzt Rate Limits durch oder ist nicht erreichbar. Ihre Pods können nicht erfolgreich starten.
Der bessere Ansatz: Laden Sie das Modell einmalig in einen gemeinsam genutzten Speicher und lassen Sie jeden Pod das Modell von dort laden. Dadurch vermeiden Sie wiederholte Downloads, entfernen externe Runtime-Abhängigkeiten und ermöglichen jedem neuen Pod schnellen Zugriff auf die Modelldateien.
In dieser Anleitung stellen Sie vLLM auf Kubernetes bereit und verwenden verwalteten NFS-Speicher für Modelldateien.
Das Beispiel nutzt einen einzelnen H100-GPU-Node, damit die Einrichtung übersichtlich bleibt. Das Muster lässt sich jedoch auf beliebig viele Nodes erweitern. Genau darin liegt der Vorteil: Sobald das Modell auf NFS gespeichert ist, erhält zusätzliche GPU-Kapazität direkten Modellzugriff, statt erneut einen langen Download auszulösen.
Wichtigste Erkenntnisse
Wiederholte Downloads vermeiden: Laden Sie LLM-Modelle einmalig in gemeinsam genutzten NFS-Speicher, anstatt sie bei jedem Pod-Start erneut abzurufen. So lassen sich Startverzögerungen von Minuten auf Sekunden reduzieren.
Schneller skalieren: Neue vLLM-Replikate können schneller Traffic verarbeiten, weil sie Modelle direkt von NFS laden, statt auf Multi-Gigabyte-Downloads zu warten.
Externe Abhängigkeiten reduzieren: Speichern Sie Modelle innerhalb Ihrer eigenen Infrastruktur, damit Pod-Neustarts und Skalierungsereignisse nicht von der Verfügbarkeit von Hugging Face abhängen.
Gleichzeitigen Zugriff ermöglichen: NFS mit ReadWriteMany-Zugriff erlaubt mehreren Pods, dieselben Modelldateien gleichzeitig zu lesen. Das ist ideal für horizontale Skalierung.
Ein produktionsnahes Muster aufbauen: Dieser Speicheransatz funktioniert für viele LLM-Deployments und skaliert von einer Single-Node-Konfiguration bis hin zu größeren Multi-GPU-Clustern.
Das Problem: Warum Modelldownloads bei jedem Start schaden
Betrachten wir genauer, welche Kosten entstehen, wenn Modelle beim Pod-Start heruntergeladen werden.
Modelldateien sind groß. Mistral-7B-Instruct-v0.3, das in dieser Anleitung verwendete Modell, ist etwa 15 GB groß. Größere Modelle wie Llama 70B können mehr als 140 GB umfassen. Immer wenn ein Pod startet und das Modell von Hugging Face herunterlädt, müssen diese Daten erneut über das Internet übertragen werden.
Jeder Pod-Neustart führt zu einem weiteren Download. Pods können abstürzen. Nodes können gewartet oder ersetzt werden. Deployments werden regelmäßig durchgeführt. Beim Download-beim-Start-Ansatz lösen all diese Ereignisse einen vollständigen erneuten Modelldownload aus. Wenn ein Inferenz-Pod an einem Tag dreimal neu startet, werden dieselben Modelldateien dreimal heruntergeladen.
Skalierung wird zum Bandbreitenengpass. Wenn ein Horizontal Pod Autoscaler während einer Traffic-Spitze zusätzliche Replikate erstellt, lädt jeder neue Pod gleichzeitig das Modell herunter. Drei neue Pods bedeuten drei parallele Multi-Gigabyte-Downloads, die alle um Bandbreite konkurrieren. Statt sofort mehr Serving-Kapazität bereitzustellen, wartet Ihre Plattform auf abgeschlossene Downloads.
Hugging Face wird zu einer Runtime-Abhängigkeit. Das ist das weniger offensichtliche Risiko. Im Normalbetrieb scheint die Verfügbarkeit von Hugging Face vielleicht kein Problem zu sein. Stellen Sie sich jedoch einen Ausfall um 2 Uhr nachts vor: Ein GPU-Node ist nicht verfügbar, Kubernetes plant einen Ersatz-Pod ein, und Hugging Face limitiert Ihre IP-Adresse oder hat selbst Störungen. Ihre Wiederherstellung nach einem Infrastrukturproblem hängt dann von einem externen Dienst ab, den Sie nicht kontrollieren.
Das Prinzip ist einfach: Kontrollieren Sie Ihre Abhängigkeiten. Externe Dienste wie Hugging Face sollten für die erstmalige Modellbeschaffung genutzt werden, nicht als notwendige Abhängigkeit bei jedem Pod-Start. Ob ein Pod wegen Skalierung, Deployment oder Fehlerbehebung startet: Er sollte aus Infrastruktur laden, die Sie selbst kontrollieren.
Die Lösung: Einmal herunterladen und überall Inferenz ausführen
Das Muster ist einfach:
- Einmal herunterladen: Ein Kubernetes Job lädt das Modell von Hugging Face auf eine NFS-Freigabe herunter.
- Auf NFS speichern: Die Modelldateien bleiben auf verwaltetem NFS-Speicher verfügbar.
- Von NFS laden: Jeder vLLM-Pod mountet die NFS-Freigabe und lädt das Modell direkt von diesem Pfad.
Damit werden die oben beschriebenen Probleme gelöst.
ReadWriteMany-Zugriff: NFS erlaubt mehreren Pods, gleichzeitig aus demselben Speicherpfad zu lesen. Ob Sie ein Replikat oder zehn ausführen, alle können dieselben Modelldateien verwenden.
Persistenz: Modelldateien bleiben über Pod-Neustarts, Node-Ersetzungen und Cluster-Upgrades hinweg verfügbar. Sie laden das Modell einmal herunter und behalten es, bis Sie es bewusst entfernen.
Zugriff innerhalb derselben Region ohne externe Runtime-Abhängigkeit: Die NFS-Freigabe befindet sich in derselben Region wie der Kubernetes-Cluster. Das Laden des Modells erfolgt über das private Netzwerk, ist schnell, zuverlässig und unabhängig von Drittanbietern.
Verwaltete Infrastruktur: Der NFS-Dienst wird vom Infrastrukturprovider betrieben, sodass Sie keine eigenen NFS-Server warten müssen.
Der Skalierungsvorteil ist besonders wichtig. Wenn Sie später einen neuen GPU-Node hinzufügen und ein weiteres vLLM-Replikat bereitstellen, kann der Pod sofort auf das Modell zugreifen. Er startet, mountet NFS, lädt das Modell in den GPU-Speicher und beginnt mit der Verarbeitung von Anfragen. Die Startzeit beschränkt sich auf das Laden des Modells, nicht auf Modelldownload plus Modellladen.
Beim Download-pro-Pod-Ansatz bedeutet ein neues Replikat, dass Sie mehrere Minuten auf einen weiteren Download warten müssen, bevor die zusätzliche Kapazität nutzbar ist.
Architekturübersicht
Der Datenfluss besteht aus zwei getrennten Phasen.
Einmalige Einrichtung:
Hugging Face → Download Job → NFS-Freigabe
Jeder Pod-Start:
NFS-Freigabe → vLLM-Pod → GPU-Speicher → Bereit zum Verarbeiten
Vergleichen Sie das mit dem Muster, bei dem bei jedem Start der Download von Hugging Face erfolgt:
Hugging Face → vLLM-Pod → GPU-Speicher → Bereit zum Verarbeiten
Voraussetzungen
Bevor Sie beginnen, benötigen Sie Folgendes:
Cloud-Konto mit H100-GPU-Kontingent
H100-GPUs erfordern in der Regel eine Kontingentfreigabe. Wenn Sie noch keinen Zugriff haben, beantragen Sie GPU-Kapazität über das Control Panel oder den Supportprozess Ihres Cloud-Anbieters.
Hugging-Face-Konto mit Modellzugriff
Diese Anleitung verwendet mistralai/Mistral-7B-Instruct-v0.3. Es handelt sich um ein nicht eingeschränktes Modell, daher ist keine Freigabe erforderlich und Sie können direkt starten.
Außerdem benötigen Sie ein Hugging-Face-Zugriffstoken mit Leseberechtigungen.
kubectl lokal installiert
Installieren Sie kubectl für Ihr Betriebssystem gemäß der offiziellen Kubernetes-Installationsanleitung.
Grundlegende Kubernetes-Kenntnisse
Sie sollten mit kubectl-Befehlen und grundlegenden Kubernetes-Konzepten wie Pods, Deployments, Services, Persistent Volumes und Persistent Volume Claims vertraut sein. Wenn Sie neu bei Managed Kubernetes sind, beginnen Sie mit der Einführung Ihres Providers zu Kubernetes.
Schritt 1: Infrastruktur einrichten
Sie benötigen drei Infrastrukturressourcen: ein VPC, einen Kubernetes-Cluster mit GPU-Node und eine NFS-Freigabe. Alle Ressourcen müssen in derselben Region erstellt werden.
Region auswählen
Nicht jede Cloud-Region stellt sowohl H100-GPU-Nodes als auch verwalteten NFS-Speicher bereit. Wählen Sie eine Region, in der beide Dienste verfügbar sind. Prüfen Sie das Control Panel oder die Dokumentation Ihres Cloud-Anbieters auf die aktuell unterstützten Regionen.
VPC erstellen
Das VPC stellt private Netzwerkkommunikation zwischen dem Kubernetes-Cluster und der NFS-Freigabe bereit.
- Navigieren Sie im Cloud Control Panel zum Netzwerkbereich.
- Erstellen Sie ein neues VPC.
- Wählen Sie dieselbe Region, die Sie auch für den Kubernetes-Cluster und die NFS-Freigabe nutzen möchten.
- Vergeben Sie einen beschreibenden Namen, zum Beispiel vllm-vpc.
- Nutzen Sie den Standard-IP-Bereich oder definieren Sie einen eigenen Bereich.
- Erstellen Sie das VPC.
Kubernetes-Cluster erstellen
- Navigieren Sie im Cloud Control Panel zum Kubernetes-Bereich.
- Erstellen Sie einen neuen Cluster.
- Wählen Sie dieselbe Region wie für das VPC.
- Wählen Sie das zuvor erstellte VPC aus.
- Konfigurieren Sie die Node Pools:
-
- Management Pool: Verwenden Sie Standard-Compute-Nodes für System-Workloads.
- GPU Node Pool: Fügen Sie einen GPU Node Pool hinzu, wählen Sie einen H100-GPU-Node-Typ aus und setzen Sie die Anzahl der Nodes auf 1.
- Erstellen Sie den Cluster und warten Sie, bis die Bereitstellung abgeschlossen ist.
kubectl mit dem Cluster verbinden
Nachdem der Cluster läuft, konfigurieren Sie den kubectl-Zugriff anhand der kubeconfig-Anweisungen Ihres Cloud-Anbieters.
Überprüfen Sie den Zugriff:
kubectl get nodes
Erwartete Ausgabe, wobei die Node-Namen je nach Umgebung abweichen:
NAME STATUS ROLES AGE VERSION
mgmt-xxxxx Ready <none> 5m v1.34.1
mgmt-yyyyy Ready <none> 5m v1.34.1
gpu-h100-zzzzz Ready <none> 5m v1.34.1
Sie sollten mindestens zwei Node-Typen sehen: Management-Nodes und einen GPU-Node.
Schritt 2: Verwaltete NFS-Freigabe erstellen
In diesem Schritt erstellen Sie den persistenten Speicherort, der die Modelldateien aufnehmen wird.
- Navigieren Sie im Cloud Control Panel zum Speicherbereich.
- Erstellen Sie eine neue NFS-Freigabe.
- Konfigurieren Sie die Freigabe:
-
- Name: Verwenden Sie einen beschreibenden Namen wie llm-models.
- Größe: 100 GB reichen für diese Anleitung aus. Wenn Sie später mehr Kapazität benötigen, können Sie die NFS-Freigabe vergrößern.
- VPC: Wählen Sie dasselbe VPC wie für den Kubernetes-Cluster.
Erstellen Sie die NFS-Freigabe und warten Sie, bis ihr Status aktiv ist.
Mount Source notieren
Sobald die Freigabe aktiv ist, notieren Sie den Mount-Source-Wert aus der NFS-Übersicht. Er enthält die Host-IP-Adresse und den Mount-Pfad, die für die Kubernetes-Konfiguration benötigt werden.
Die Mount Source nutzt das Format <HOST>:<PATH>, zum Beispiel:
10.100.32.2:/2633050/7d1686e4-9212-420f-a593-ab544993d99b
Für die PersistentVolume-Konfiguration teilen Sie diesen Wert in zwei Angaben auf:
Host: Die IP-Adresse vor dem Doppelpunkt, zum Beispiel 10.100.32.2.
Pfad: Alles nach dem Doppelpunkt, zum Beispiel /2633050/7d1686e4-9212-420f-a593-ab544993d99b.
Das ist wichtig, weil die NFS-Freigabe nun Ihre persistente Modellbibliothek ist. Jedes hier gespeicherte Modell kann von jedem Pod im Cluster genutzt werden, jetzt und in Zukunft. Sie müssen es nicht erneut herunterladen, wenn Pods neu starten oder Sie skalieren.
Schritt 3: Kubernetes mit NFS verbinden
Als Nächstes erstellen Sie die Kubernetes-Ressourcen, mit denen Pods auf die NFS-Freigabe zugreifen können.
Namespace erstellen
Erstellen Sie zuerst einen eigenen Namespace für die vLLM-Ressourcen:
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: vllm
Wenden Sie die Datei an:
kubectl apply -f namespace.yaml
PersistentVolume erstellen
Das PersistentVolume teilt Kubernetes mit, wie die Verbindung zur NFS-Freigabe hergestellt wird. Verwenden Sie die Mount Source aus Schritt 2 und ersetzen Sie <NFS_HOST> durch die IP-Adresse sowie <NFS_MOUNT_PATH> durch den Pfad:
# pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: vllm-models-pv
labels:
pv-name: vllm-models-pv
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteMany # Allows multiple pods to read simultaneously
persistentVolumeReclaimPolicy: Retain
nfs:
server: <NFS_HOST> # IP from Mount Source, for example 10.100.32.2
path: <NFS_MOUNT_PATH> # Path from Mount Source, for example /2633050/7d1686e4-...
Wichtige Details:
- ReadWriteMany: Dieser Zugriffsmodus erlaubt allen vLLM-Pods, gleichzeitig aus der NFS-Freigabe zu lesen. Viele Block-Storage-Systeme unterstützen nur ReadWriteOnce.
- Retain Reclaim Policy: Wenn die PVC gelöscht wird, bleiben die Daten auf der NFS-Freigabe erhalten. Das schützt heruntergeladene Modelle vor versehentlichem Entfernen.
PersistentVolumeClaim erstellen
Der PersistentVolumeClaim ist die Ressource, auf die Pods verweisen, wenn sie Zugriff auf den Speicher benötigen:
# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: vllm-models-pvc
namespace: vllm
spec:
accessModes:
- ReadWriteMany
storageClassName: "" # Empty string binds to a pre-provisioned PV
resources:
requests:
storage: 100Gi
selector:
matchLabels:
pv-name: vllm-models-pv
Wenden Sie beide Ressourcen an:
kubectl apply -f pv.yaml
kubectl apply -f pvc.yaml
Prüfen Sie, ob die PVC gebunden wurde:
kubectl get pvc -n vllm
Erwartete Ausgabe:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
vllm-models-pvc Bound vllm-models-pv 100Gi RWX 10s
Der Status sollte Bound anzeigen. Wenn er auf Pending bleibt, prüfen Sie, ob die PV-Konfiguration zum PVC-Selector passt und ob NFS-Host und Pfad korrekt sind.
Kubernetes kann nun auf die NFS-Freigabe zugreifen. Jeder Pod, der vllm-models-pvc mountet, kann den gemeinsamen Speicher nutzen.
Schritt 4: Modell einmalig herunterladen
Das ist der zentrale Teil des Download-Once-Musters. Sie verwenden einen Kubernetes Job, um das Modell auf NFS herunterzuladen. Ein Job läuft bis zum Abschluss und ist kein dauerhaft laufendes Deployment.
Hugging-Face-Token als Secret erstellen
Erstellen Sie zuerst ein Secret mit Ihrem Hugging-Face-Token. Ersetzen Sie <YOUR_HUGGINGFACE_TOKEN> durch Ihr Token:
# hf-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: hf-token
namespace: vllm
type: Opaque
stringData:
HF_TOKEN: <YOUR_HUGGINGFACE_TOKEN>
Wenden Sie das Secret an:
kubectl apply -f hf-secret.yaml
Model-Download-Job bereitstellen
Erstellen Sie nun den Job, der das Modell herunterlädt:
# model-download-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: model-download
namespace: vllm
spec:
ttlSecondsAfterFinished: 300 # Clean up job after 5 minutes
template:
spec:
restartPolicy: Never
securityContext:
runAsUser: 1000
runAsGroup: 1000
containers:
- name: download
image: python:3.11-slim
command:
- /bin/sh
- -c
- |
pip install --target=/tmp/pip huggingface_hub &&
export PYTHONPATH=/tmp/pip &&
python -c "from huggingface_hub import snapshot_download; snapshot_download('mistralai/Mistral-7B-Instruct-v0.3', local_dir='/models/Mistral-7B-Instruct-v0.3')"
env:
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-token
key: HF_TOKEN
- name: HOME
value: /tmp
- name: HF_HOME
value: /tmp/hf_home
volumeMounts:
- name: nfs-storage
mountPath: /models
volumes:
- name: nfs-storage
persistentVolumeClaim:
claimName: vllm-models-pvc
Dieser Job führt folgende Aufgaben aus:
- Er installiert den Hugging Face Hub Client.
- Er lädt Mistral-7B-Instruct-v0.3 nach /models/Mistral-7B-Instruct-v0.3 auf der NFS-Freigabe herunter.
- Er nutzt local_dir, sodass die Dateien direkt gespeichert werden und nicht innerhalb der Hugging-Face-Cache-Struktur landen.
- Er läuft aus Sicherheitsgründen als Nicht-Root-Benutzer.
Wenden Sie den Job an:
kubectl apply -f model-download-job.yaml
Download überwachen
Verfolgen Sie die Ausgabe des Jobs:
kubectl logs job/model-download -n vllm -f
Sie sehen, wie der Hugging Face Hub Client installiert wird und anschließend der Fortschritt des Modelldownloads ausgegeben wird. Abhängig von den Netzwerkbedingungen dauert der Download üblicherweise etwa 5 bis 10 Minuten.
Warten Sie, bis der Job abgeschlossen ist:
kubectl wait --for=condition=complete job/model-download -n vllm --timeout=15m
Erwartete Ausgabe:
job.batch/model-download condition met
Prüfen Sie, ob der Job erfolgreich war:
kubectl get jobs -n vllm
NAME STATUS COMPLETIONS DURATION AGE
model-download Complete 1/1 3m 5m
Das ist der entscheidende Punkt: Der Download findet nur einmal statt. Jeder Pod, der ab jetzt bereitgestellt wird, ob heute, morgen oder nächsten Monat, verwendet dieselben Modelldateien. Pod-Neustarts erfordern keinen weiteren Download mehr.
Schritt 5: vLLM bereitstellen
Nun stellen Sie vLLM bereit. Der Pod mountet die NFS-Freigabe und lädt das Modell direkt. Beim Start muss das Modell nicht mehr von Hugging Face heruntergeladen werden.
Deployment erstellen
# vllm-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm
namespace: vllm
labels:
app: vllm
spec:
replicas: 1 # Single replica for this tutorial
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0 # Delete old pod before creating new pod because of GPU constraints
maxUnavailable: 1
selector:
matchLabels:
app: vllm
template:
metadata:
labels:
app: vllm
spec:
tolerations:
- key: nvidia.com/gpu
operator: Exists
effect: NoSchedule
containers:
- name: vllm
image: vllm/vllm-openai:latest
args:
- --model
- /models/Mistral-7B-Instruct-v0.3
- --served-model-name
- Mistral-7B-Instruct-v0.3
ports:
- containerPort: 8000
name: http
env:
- name: VLLM_PORT
value: "8000"
- name: HF_TOKEN
valueFrom:
secretKeyRef:
name: hf-token
key: HF_TOKEN
volumeMounts:
- name: model-cache
mountPath: /models
resources:
limits:
nvidia.com/gpu: 1
requests:
nvidia.com/gpu: 1
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 60
periodSeconds: 30
volumes:
- name: model-cache
persistentVolumeClaim:
claimName: vllm-models-pvc
Wichtige Konfigurationsdetails:
- replicas: 1: Diese Anleitung verwendet einen GPU-Node. Erhöhen Sie diesen Wert, nachdem Sie weitere GPU-Nodes hinzugefügt haben.
- maxSurge: 0: GPU-Nodes stellen häufig eine GPU bereit. Diese Einstellung löscht bei Updates zuerst den alten Pod, bevor ein neuer erstellt wird, da beide nicht gleichzeitig auf einer einzelnen GPU laufen können.
- tolerations: Diese erlauben es dem Pod, auf GPU-Nodes mit Taints eingeplant zu werden.
- nvidia.com/gpu: 1: Dadurch wird eine GPU für den Pod angefordert.
- volumeMounts: Die NFS-PVC wird unter /models gemountet, wo die Modelldateien gespeichert sind.
- –model /models/Mistral-7B-Instruct-v0.3: Diese Option verweist vLLM direkt auf den Modellpfad auf NFS.
Hinweis zu Container-Images: Diese Anleitung lädt das vLLM-Image der Einfachheit halber von Docker Hub. In Produktionsumgebungen sollten Sie das Image in eine Container Registry spiegeln, die Sie kontrollieren, und es von dort referenzieren. Das folgt demselben Prinzip der kontrollierten Abhängigkeiten, da Docker Hub dann nur noch eine einmalige Quelle und keine Runtime-Abhängigkeit mehr ist.
Service erstellen
# vllm-service.yaml
apiVersion: v1
kind: Service
metadata:
name: vllm
namespace: vllm
labels:
app: vllm
spec:
type: ClusterIP
ports:
- port: 8000
targetPort: 8000
protocol: TCP
name: http
selector:
app: vllm
Wenden Sie beide Dateien an:
kubectl apply -f vllm-deployment.yaml
kubectl apply -f vllm-service.yaml
Warten, bis der Pod bereit ist
kubectl wait --for=condition=ready pod -l app=vllm -n vllm --timeout=10m
Prüfen Sie den Pod-Status:
kubectl get pods -n vllm -o wide
Erwartete Ausgabe:
NAME READY STATUS RESTARTS AGE IP NODE
vllm-xxxxxxxxx-xxxxx 1/1 Running 0 2m 10.108.1.123 gpu-h100-xxxxx
Achten Sie darauf, was nicht passiert: Es gibt keinen Modelldownload-Schritt. vLLM startet, mountet NFS und lädt das Modell direkt in den GPU-Speicher.
Wenn der Pod das Modell zuerst herunterladen müsste, würden Sie mehrere zusätzliche Minuten warten.
Schritt 6: Inferenz-Endpunkt testen
Prüfen Sie nun die Bereitstellung, indem Sie Anfragen an den vLLM-Endpunkt senden.
Port-Forwarding zum Service einrichten
Da dieses Beispiel einen ClusterIP-Service verwendet, nutzen Sie Port-Forwarding, um lokal darauf zuzugreifen:
kubectl port-forward svc/vllm -n vllm 8000:8000
Lassen Sie diesen Befehl in einem Terminal laufen. Öffnen Sie ein weiteres Terminal für die nächsten Befehle.
Verfügbare Modelle auflisten
curl -s http://localhost:8000/v1/models | jq .
Erwartete Ausgabe:
{
"object": "list",
"data": [
{
"id": "Mistral-7B-Instruct-v0.3",
"object": "model",
"created": 1234567890,
"owned_by": "vllm",
"root": "/models/Mistral-7B-Instruct-v0.3",
"parent": null,
"max_model_len": 32768,
"permission": [...]
}
]
}
Chat-Completion-Anfrage senden
curl -s http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Mistral-7B-Instruct-v0.3",
"messages": [{"role": "user", "content": "What is the capital of France?"}],
"max_tokens": 50
}' | jq .
Erwartete Ausgabe:
{
"id": "chatcmpl-xxxxxxxxxxxxxxxx",
"object": "chat.completion",
"created": 1234567890,
"model": "Mistral-7B-Instruct-v0.3",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "The capital of France is Paris..."
},
"finish_reason": "length"
}
],
"usage": {
"prompt_tokens": 10,
"total_tokens": 60,
"completion_tokens": 50
}
}
Sie haben nun einen funktionierenden LLM-Inferenz-Endpunkt, der auf gemeinsam genutztem NFS-Speicher basiert.
In einer Produktionsbereitstellung sollten Sie den Service über Gateway API oder einen LoadBalancer-Service veröffentlichen, anstatt Port-Forwarding zu verwenden. Viele Managed-Kubernetes-Umgebungen unterstützen Gateway-API-Integrationen, mit denen sich HTTPS-Routing unkompliziert einrichten lässt.
Für fortgeschrittenere vLLM-Bereitstellungsstrategien und Ansätze zum Modell-Caching sollten Sie sich mit vLLM-Kubernetes-Mustern für Modellladen und Caching beschäftigen.
Beachten Sie, dass sich diese Anleitung gezielt auf Modellspeicher konzentriert. Ein produktives LLM-Deployment erfordert außerdem Authentifizierung, Rate Limiting, effizientes Request-Routing über mehrere Replikate, Observability und weitere betriebliche Aspekte, die nicht Teil dieser Anleitung sind.
Skalierung: Was passiert, wenn Sie weitere GPUs hinzufügen?
Hier zeigt sich der eigentliche Wert des NFS-basierten Musters.
Angenommen, Sie betreiben bisher ein vLLM-Replikat und der Traffic steigt. Sie benötigen mehr Kapazität.
Szenario: Auf mehrere Replikate skalieren
Fügen Sie zuerst über das Control Panel Ihres Cloud-Anbieters einen weiteren GPU-Node zum Cluster hinzu. Sobald dieser bereit ist, skalieren Sie das Deployment:
kubectl scale deployment vllm -n vllm --replicas=2
Beobachten Sie, wie der neue Pod startet:
kubectl get pods -n vllm -w
NAME READY STATUS RESTARTS AGE
vllm-xxxxxxxxx-xxxxx 1/1 Running 0 30m
vllm-xxxxxxxxx-yyyyy 0/1 Pending 0 5s
vllm-xxxxxxxxx-yyyyy 0/1 ContainerCreating 0 10s
vllm-xxxxxxxxx-yyyyy 1/1 Running 0 45s
Achten Sie auf die Zeit. Der neue Pod wechselt in etwa 45 Sekunden von Pending zu Running. Das ist die Modellladezeit, ohne zusätzlichen mehrminütigen Download.
Mit der Methode, bei der jeder Pod selbst herunterlädt, würde dieser Vorgang deutlich länger dauern. Der neue Pod müsste das gesamte Modell von Hugging Face herunterladen, während der erste Pod weiterhin den gesamten Traffic allein verarbeitet.
Szenario: Später weitere GPU-Nodes hinzufügen
Dasselbe Muster funktioniert auch, wenn Sie morgen, nächste Woche oder nächsten Monat weitere GPU-Nodes hinzufügen. Das Modell liegt bereits auf NFS. Neue Pods können sofort darauf zugreifen:
- Fügen Sie dem Cluster einen GPU-Node hinzu.
- Skalieren Sie das Deployment:
kubectl scale deployment vllm -n vllm --replicas=3
- Der neue Pod startet und ist nach ungefähr 45 Sekunden bereit.
- Es gibt keine Downloads, keine unnötigen Wartezeiten und keine Konkurrenz um Bandbreite.
Für Deployments mit mehreren Replikaten sollten Sie einen Load Balancer vor vLLM platzieren. Verwenden Sie die Gateway API oder die Load-Balancing-Dokumentation Ihres Kubernetes-Anbieters, um dies einzurichten. Um die Speichergrundlagen besser zu verstehen, sollten Sie sich mit Kubernetes PersistentVolumes und PersistentVolumeClaims befassen.
Cleanup
GPU-Nodes können kostspielig sein. Wenn die Tests abgeschlossen sind, entfernen Sie die Ressourcen, um unnötige Kosten zu vermeiden.
Löschen Sie die Ressourcen über das Control Panel Ihres Cloud-Anbieters:
- Löschen Sie die NFS-Freigabe.
- Löschen Sie den Kubernetes-Cluster.
- Löschen Sie das VPC.
Löschen Sie die Ressourcen in dieser Reihenfolge, um Abhängigkeitskonflikte zu vermeiden.
Häufig gestellte Fragen
Wie unterscheidet sich NFS von anderen Speicheroptionen für LLM-Modelle?
NFS bietet ReadWriteMany-Zugriff. Das bedeutet, dass mehrere Pods dieselben Modelldateien gleichzeitig lesen können. Das ist wichtig für die horizontale Skalierung von LLM-Inferenz-Workloads. Block-Storage-Optionen unterstützen häufig nur ReadWriteOnce, wodurch die Nutzung auf einen Pod pro Volume beschränkt ist. Object Storage kann in einigen Architekturen ebenfalls verwendet werden, erfordert aber meist zusätzliche Werkzeuge, um ihn als Dateisystem einzubinden, und kann beim Modellladen eine höhere Latenz als NFS haben.
Kann dieses Muster auch mit anderen LLM-Inferenz-Frameworks als vLLM genutzt werden?
Ja. Dieser Ansatz funktioniert mit jedem LLM-Inferenz-Framework, das Modelle von einem Dateisystempfad laden kann, darunter TensorRT-LLM, llama.cpp und ähnliche Tools. Die wichtigste Voraussetzung ist, dass der Inferenz-Container die NFS-PersistentVolumeClaim mounten und die Modelldateien von diesem Mount-Pfad lesen kann. Passen Sie den Modellpfad in der Deployment-Konfiguration entsprechend an den NFS-gemounteten Ordner an.
Was passiert, wenn die NFS-Freigabe keinen Speicherplatz mehr hat?
Verwaltete NFS-Freigaben lassen sich in der Regel über Control Panel oder API vergrößern. Nach der Größenänderung steht die zusätzliche Kapazität ohne Downtime zur Verfügung. Diese Anleitung startet mit 100 GB, was für Mistral-7B-Instruct-v0.3 mit etwa 15 GB ausreicht. Größere Modelle wie Llama 70B, die mehr als 140 GB umfassen können, benötigen mehr Speicher. Wählen Sie die anfängliche NFS-Größe anhand Ihrer Modellanforderungen und erweitern Sie sie bei Bedarf.
Ist das Laden von NFS langsamer als das Laden von lokalem Speicher?
Für das Modellladen ist die NFS-Performance üblicherweise ausreichend, da das Laden einmal pro Pod-Start erfolgt und nicht bei jeder Inferenzanfrage. Die Modelldateien werden beim Start des Pods in den GPU-Speicher geladen, und die Inferenz nutzt anschließend das im Speicher befindliche Modell, statt wiederholt von NFS zu lesen. Wenn Sie Modelle häufig neu laden oder während des Trainings Checkpointing durchführen, kann lokaler NVMe-Speicher bessere Leistung bieten. Für Inferenz-Workloads, bei denen Modelle einmal geladen werden und im Speicher bleiben, bietet NFS Skalierungsvorteile ohne spürbare Performance-Nachteile.
Wie kann ich ein auf NFS gespeichertes Modell aktualisieren oder ersetzen?
Es gibt mehrere Möglichkeiten, ein Modell zu aktualisieren. Sie können eine neue Modellversion in ein anderes Verzeichnis auf derselben NFS-Freigabe herunterladen, zum Beispiel /models/Mistral-7B-Instruct-v0.4, und anschließend das vLLM-Deployment auf den neuen Pfad umstellen. So können Sie die neue Version testen, während das alte Modell für Rollbacks verfügbar bleibt.
Alternativ können Sie das alte Modellverzeichnis entfernen und die neue Version in denselben Pfad herunterladen. Da der Download über einen Kubernetes Job erfolgt, können Sie den Job mit aktualisierten Parametern erneut ausführen. Die NFS-Freigabe bleibt über Pod-Neustarts hinweg bestehen, sodass Modellupdates auch nach Deployment-Änderungen verfügbar bleiben.
Fazit
Sie sind vom wiederholten Modelldownload bei jedem Pod-Start zu einem Muster gewechselt, bei dem Sie einmal herunterladen und überall Inferenz ausführen.
Sie haben ein Modellspeicher-Muster aufgebaut, das als Grundlage für produktive LLM-Deployments dienen kann.
Sie haben gemeinsam genutzten Modellspeicher erstellt, der mit dem Kubernetes-Cluster skaliert.
Sie haben externe Runtime-Abhängigkeiten entfernt, weil Hugging Face nur noch als einmalige Quelle dient und nicht mehr als Voraussetzung für jeden Pod-Start.
Das Prinzip hinter diesem Ansatz lautet: Kontrollieren Sie Ihre Abhängigkeiten. Externe Dienste sollten für die initiale Beschaffung genutzt werden, nicht als Runtime-Voraussetzung. Wenn Infrastruktur sich von Fehlern erholen, auf Traffic-Spitzen reagieren oder regelmäßige Deployments durchführen muss, sollte sie sich auf Komponenten verlassen, die unter Ihrer Kontrolle stehen.


