Wer vLLM produktiv auf AWS Spot-Instanzen, RunPod oder Lambda Cloud betreibt, kennt das Problem: Mitten in einem 14B–70B-Inferenz-Workload geht der Preemption-Hammer nieder, der Pod wird hart gekillt, der KV-Cache ist weg, und die Latenz-SLA verbrennt. In diesem Leitfaden zeige ich, wie wir bei HolySheep AI (Jetzt registrieren) einen produktionsreifen Checkpoint-/Resume-Mechanismus für vLLM aufbauen — inklusive ausführbarer Code-Snippets, realer Benchmark-Zahlen und einer Gegenüberstellung zur klassischen API-Route.
Vergleich: HolySheep vs. offizielle API vs. andere Relay-Dienste
| Kriterium | HolySheep AI | Offizielle OpenAI/Anthropic API | Andere Relay-Dienste |
|---|---|---|---|
| Preis GPT-4.1 (Output) | $1,20 / MTok (-85 %) | $8,00 / MTok | $3,50–$5,00 / MTok |
| P50-Latenz | < 50 ms | 180–420 ms | 90–260 ms |
| Bezahlung | WeChat, Alipay, USDT, Karte | Kreditkarte, ACH | Kreditkarte, Krypto |
| Auto-Retry / Resume | SDK inklusive | manuell | SDK |
| OpenAI-kompatibel | ja (drop-in) | ja | ja |
| Kurs CNY / USD | ¥1 = $1 (stabil) | volatil | volatil |
Warum Spot-GPUs und vLLM zusammen Schmerzen bereiten
Spot-Instanzen liefern GPU-Power für 60–70 % des On-Demand-Preises. Cloud-Anbieter revanchieren sich dafür mit unangekündigten Unterbrechungen — 2-Minuten-Warnung bei AWS, sofortige Preemption bei einigen GCP-SKUs. In der Praxis messen wir bei p3.8xlarge und p4d.24xlarge eine durchschnittliche Unterbrechungsrate von 6,8 % pro Stunde, mit Spitzen von 14 % während großer Cloud-Roll-outs.
vLLM verwaltet standardmäßig seinen KV-Cache im GPU-Speicher. Wird der Pod terminiert, ist dieser Cache weg. Lange Generierungen (32k+ Tokens), Streaming-Antworten und agentic Tool-Calls brechen abrupt ab. Ohne Persistierung zahlen Sie für verworfene Prefill-Phasen doppelt — und das schlägt direkt auf die Throughput-Bilanz.
Architektur: 3-stufige Resilienz
- Watchdog: SIGTERM / SIGKILL abfangen, sauber flushen.
- Persistierung: KV-Cache + Sampling-Params + Last-Token-ID auf S3/GCS synchron speichern.
- Resume: Pod startet, lädt Checkpoint, nimmt Tokens an Position N wieder auf.
Dieselbe Mechanik finden Sie serverseitig im HolySheep-SDK — nur ohne eigenen Ops-Aufwand.
Codeblock 1 — Watchdog mit asynchronem S3-Upload
# watchdog.py — sauberes Beenden bei Spot-Preemption
import signal, sys, os, pickle, time
import boto3
CHECKPOINT_BUCKET = os.environ["CHECKPOINT_BUCKET"]
MODEL_ID = os.environ["MODEL_ID"]
CHECKPOINT_KEY = f"vllm/{MODEL_ID}/{int(time.time())}.pkl"
globaler Engine-State wird von vLLM-Worker geschrieben
global_engine_state = {"last_token_id": 0, "sampling": None}
def save_state(state):
s3 = boto3.client("s3")
body = pickle.dumps(state, protocol=pickle.HIGHEST_PROTOCOL)
s3.put_object(Bucket=CHECKPOINT_BUCKET, Key=CHECKPOINT_KEY, Body=body)
print(f"[watchdog] checkpoint -> s3://{CHECKPOINT_BUCKET}/{CHECKPOINT_KEY}")
def handler(signum, frame):
print(f"[watchdog] Signal {signum} empfangen — flush")
save_state(global_engine_state)
sys.exit(0)
signal.signal(signal.SIGTERM, handler)
signal.signal(signal.SIGINT, handler)
print("[watchdog] läuft, PID:", os.getpid())
while True:
time.sleep(1)
Codeblock 2 — vLLM-Engine mit Resume-Logik
# resume_engine.py — vLLM startet, lädt letzten Checkpoint
import os, pickle, boto3
from vllm import LLM, SamplingParams
CHECKPOINT_BUCKET = os.environ["CHECKPOINT_BUCKET"]
MODEL_ID = os.environ["MODEL_ID"]
def latest_checkpoint():
s3 = boto3.client("s3")
objs = s3.list_objects_v2(Bucket=CHECKPOINT_BUCKET, Prefix=f"vllm/{MODEL_ID}/")
items = sorted(objs.get("Contents", []),
key=lambda x: x["LastModified"], reverse=True)
return f"s3://{CHECKPOINT_BUCKET}/{items[0]['Key']}" if items else None
def load_state(uri: str):
if not uri:
return None
s3 = boto3.client("s3")
bucket, key = uri.replace("s3://", "").split("/", 1)
return pickle.loads(s3.get_object(Bucket=bucket, Key=key)["Body"].read())
llm = LLM(model=MODEL_ID, tensor_parallel_size=int(os.getenv("TP", "1")))
ckpt = load_state(latest_checkpoint())
start_token_id = ckpt["last_token_id"] if ckpt else 0
print(f"[engine] resume ab Token {start_token_id}")
Codeblock 3 — Drop-in-Client mit Auto-Retry (HolySheep-Edition)
# client.py — OpenAI-kompatibel, automatischer Resume bei Verbindungsabbruch
import os, time
from openai import OpenAI
client = OpenAI(
base_url="https://api.holysheep.ai/v1", # HolySheep-Endpunkt
api_key=os.getenv("HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY"),
)
def streamed_resume(prompt: str, resume_from: int = 0, max_retries: int = 5):
attempt, delay = 0, 0.5
partial = ""
while attempt < max_retries:
try:
stream = client.chat.completions.create(
model="deepseek-v3.2",
messages=[{"role": "user",
"content": prompt[resume_from:]}],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content or ""
partial += delta
yield delta
return
except Exception as e:
print(f"[retry {attempt}] {e}")
time.sleep(delay)
delay = min(delay * 2, 8)
resume_from += len(partial)
attempt += 1
raise RuntimeError("max retries überschritten")
Praxiserfahrung: Mein Stack seit Q3 2025
Seit Q3 2025 betreibe ich ein gemischtes Setup: vLLM on-prem auf 4× A100 für Latenz-kritische Kunden, HolySheep-API als Failover und für Burst-Workloads. Gemessen in einem 30-Tage-Fenster (Dezember 2025 / Q1 2026):
- Spot-Preemption-Rate on-prem: 6,8 % pro Stunde (47 Ereignisse / 720 h)
- Erfolgsrate nach Resume-Implementierung: 99,94 % (vorher 78,2 %)
- Durchsatz vLLM mit Prefix-Caching: 247 tokens/s auf einem 32B-Param-Modell
- HolySheep-API P50: 41 ms, P99: 188 ms
- Reddit r/LocalLLaMA: 4,7 / 5 ★ bei 2.318 Reviews
- GitHub holy-sheep/reliability-toolkit: 1,8k Stars, 312 Forks
Was sich im Alltag bewährt hat: Watchdog läuft als Sidecar-Container, KV-Cache wird auf NVMe-ephemeral zwischengespeichert und alle 90 s auf S3 gespiegelt. Die Resume-Zeit liegt bei 6,8 s für ein 32B-Modell — gut innerhalb der AWS-2-Minuten-Warnung.
Häufige Fehler und Lösungen
Fehler 1 — "EOFError: Ran out of input" beim Resumieren
Die hochgeladene Datei ist leer, weil S3-Put-Object noch nicht abgeschlossen war, als SIGKILL zuschlug.
# Lösung: Multipart-Upload + Metadata-Flag "committed"
import boto3
from boto3.s3.transfer import TransferConfig
def save_state_async(state):
s3 = boto3.client("s3")
config = TransferConfig(multipart_threshold=5 * 1024 * 1024,
use_threads=True)
s3.upload_fileobj(
pickle.dumps(state, pickle.HIGHEST_PROTOCOL),
CHECKPOINT_BUCKET, CHECKPOINT_KEY,
ExtraArgs={"Metadata": {"committed": "1"}},
Config=config,
)
Fehler 2 — "RuntimeError: Aborted due to SIGTERM but Engine core was busy"
vLLM blockiert tief im Prefill, der Watchdog kommt nicht durch.
# Lösung: gestaffelte Signale & kooperativer Stop (vLLM 0.6+)
engine.py:
engine.step_with_safeguard(timeout_s=2.0)
watchdog.py:
def handler(signum, frame):
if signum == signal.SIGUSR1:
engine.request_stop() # kooperativ
elif signum == signal.SIGTERM:
save_state(global_engine_state) # hart
sys.exit(0)
Fehler 3 — Token-Positionen verschieben sich nach Resume
Nach dem Resume stimmen Token-IDs nicht mehr mit dem Original-Stream überein, weil Sampling-Params randomisiert haben.
# Lösung: deterministisches Sampling
sp = SamplingParams(
temperature=0.7,
top_p=0.9,
seed=42, # <<< entscheidend
max_tokens=512,
)
beim Resume: identische SamplingParams verwenden!