Home / Anwendungen und Use Cases / Managed KI oder Self-Hosting – wann gewinnt was im Betrieb?

Managed KI oder Self-Hosting – wann gewinnt was im Betrieb?

Wenn Inferenzjobs tagsüber zuverlässig laufen und nachts kaum Last erzeugen, sieht ein zusätzlich gemieteter GPU-Server zunächst günstiger aus als ein eigener Pool. Im Produktionsbetrieb entscheidet jedoch nicht der Stundenpreis allein: Warteschlangen, Startzeiten und fehlgeschlagene Übergaben verändern die Rechnung. Mein Standard wäre ein gemeinsam betriebener GPU-Pool. Separate Mietserver würde ich erst einsetzen, wenn Lastprofil und Fehlertoleranz ihren Betriebsaufwand rechtfertigen.

Ein gemeinsamer GPU-Pool gewinnt bei stetiger Last, obwohl er Leerlauf kostet

Die erste Option ist ein GPU-Node-Pool im bestehenden Kubernetes-Cluster. Inferenzdienste erhalten dort feste Ressourcenanforderungen über nvidia.com/gpu; der NVIDIA Device Plugin macht die Geräte für den Scheduler sichtbar. Der Beitrag KI-Infrastruktur fuer skalierbare Softwareentwicklung setzt einen breiteren Infrastrukturrahmen; für die Produktionsentscheidung würde ich ihn auf eine engere Frage reduzieren: Kann ein Pool die zugesagte Antwortzeit auch während eines Knotenausfalls halten?

Bei dauerhaftem Verkehr gewinnt der Pool, weil Modelle geladen bleiben und Anfragen nicht auf Provisionierung, Treiberstart und Modell-Download warten. Sein Preis ist sichtbarer Leerlauf: Reservierte GPU-Zeit fällt auch dann an, wenn nur wenige Tokens erzeugt werden. Dazu kommen Verantwortung für NVIDIA GPU Operator, Treiber, Patches und Kapazitätsplanung. Wer bereits Kubernetes-Rollouts, Registry-Zugriff und zentrale Telemetrie betreibt, muss dafür allerdings keinen zweiten Betriebsweg mit eigenen Zugangsdaten und eigener Alarmierung eröffnen.

Prüfen Sie diese Option mit dem p95 der End-to-End-Latenz, nicht allein mit GPU-Auslastung, weil ein zu kleiner Pool schon vor voller GPU-Auslastung an CPU-seitiger Vorverarbeitung oder langen Prompts scheitern kann. Falls Ihr Lasttest bei der vorgesehenen Spitzenrate beispielsweise 18 Sekunden p95 misst, ist das ein Messwert für genau diesen Test und kein Versprechen für andere Modelle. Erfassen Sie gleichzeitig Time to First Token, Warteschlangenalter und die Histogramme in Prometheus; histogram_quantile() kann die Verteilung aus den Bucket-Zählern berechnen, sofern die Buckets für den relevanten SLO-Bereich fein genug gewählt wurden.

Für reproduzierbare Rollouts gehören Modellrevision, Container-Image per sha256-Digest und Laufzeitversion zusammen, weil ein unverändertes API-Schema keine gleichbleibende Speicherbelegung garantiert. Ein Dienst mit vLLM 0.8.5 kann etwa nach einer Änderung von Kontextlänge oder Parallelität einen anderen GPU-Footprint haben; deshalb gehört vor jedem Rollout ein Test mit den längsten zugelassenen Eingaben. Als betrieblicher Richtwert, den das Team selbst einstellen muss, würde ich mindestens eine Knotenstörung während des Lasttests simulieren, bevor ich eine zweite Replik als echte Reserve verbuche.

Separate Mietserver gewinnen bei kurzen Spitzen und teuren Reserven

Die konkurrierende Option sind zeitweise gemietete GPU-Server außerhalb des regulären GPU-Pools. Jobs werden aus einer dauerhaften Queue abgeholt; nach Abschluss fährt der Server wieder herunter. Der Beitrag KI und ML: GPU Server mieten fuer skalierbare Infrastruktur legt den Blick auf elastische Kapazität; ich würde diese Kapazität nur dann als günstig bezeichnen, wenn Startzeit, Datentransfer und Bereitschaftskosten in derselben Rechnung stehen wie die GPU-Stunden.

Mietserver gewinnen bei planbaren Batch-Fenstern und seltenen Spitzen, weil zwischen den Läufen keine GPU reserviert bleiben muss. Sie verlieren bei engen interaktiven Latenzvorgaben, sofern ein kalter Server erst Image, Gewichte und Daten laden muss. Zusätzlich kosten ausgehender Datentransfer, private Netzanbindung, Imagespeicher und Tests eines zweiten Ausführungspfads Geld oder Arbeitszeit. Der nominelle GPU-Stundenpreis ist deshalb keine brauchbare Vergleichszahl, solange diese Positionen fehlen.

Die folgende Rechnung nutzt frei gesetzte Beispielpreise, keine Anbieterpreise: 2,40 Euro pro Stunde für einen durchgehend reservierten Pool, 3,60 Euro pro Mietstunde und 0,09 Euro pro übertragenem Gigabyte. Eine Anlaufzeit von 0,20 Stunden pro Lauf ist hier ein veränderbarer Planungswert; in der Praxis sollte sie aus Provisionierung und Modellstart gemessen werden. Das Skript lässt sich mit Python 3 unmittelbar ausführen:

python3 - <<'PY'
hours = 720
pool_eur_h = 2.40
rent_eur_h = 3.60
boot_h = 0.20
runs = 30
transfer_gb = 400
egress_eur_gb = 0.09
for duty in (0.25, 0.80):
    pool = hours * pool_eur_h
    rent = (hours * duty + runs * boot_h) * rent_eur_h + transfer_gb * egress_eur_gb
    print(f"{duty:.0%}: Pool {pool:.2f} EUR, Miete {rent:.2f} EUR")
PY

Bei 25 Prozent Auslastungszeit ergibt dieses Modell 1.728 Euro für den Pool und 705,60 Euro für Mietserver; bei 80 Prozent sind es 1.728 Euro gegenüber 2.131,20 Euro. Diese Ergebnisse gelten nur unter den eingesetzten Annahmen, weil weder Personalaufwand noch unterschiedliche GPU-Leistung eingerechnet sind. Ändern Sie deshalb zuerst runs, boot_h und das tatsächliche Transferprofil, statt aus dem Beispiel eine allgemeine Auslastungsschwelle abzuleiten.

Die Übergabe an Mietserver ist der eigentliche Produktionsvertrag

Ein externer Server darf nicht dadurch zum Single Point of Failure werden, dass der API-Dienst ihm einen Job direkt per HTTP übergibt und anschließend auf eine Antwort wartet. Ich würde für diesen Pfad eine dauerhafte Queue wie NATS JetStream verwenden, weil ein Worker nach einem Absturz einen nicht bestätigten Auftrag erneut erhalten können muss. AckWait und MaxDeliver sind dabei Betriebsparameter: Ein zu kurzes AckWait erzeugt parallele Wiederholungen langer Jobs, ein unbegrenztes Wiederholen kann defekte Eingaben dauerhaft im System halten.

Wiederholbare Zustellung ist nicht dasselbe wie genau einmalige Ausführung, weil ein Worker nach erfolgreichem Schreiben des Ergebnisses und vor dem Acknowledgement abstürzen kann. Speichern Sie daher eine Job-ID mit eindeutigem Ergebnisverweis und machen Sie den Schreibvorgang idempotent. Eingaben sollten einen stabilen Verweis auf Daten in Objektspeicher tragen statt den gesamten Payload in der Queue, weil große Nachrichten Wiederholungen und Recovery unnötig verteuern. Zugangsdaten für den Speicher müssen kurzlebig und auf den benötigten Pfad begrenzt sein, damit ein kompromittierter Mietserver nicht den gesamten Datenbestand lesen kann.

Vor dem ersten produktiven Job braucht der Worker einen Kompatibilitätscheck. Für eine Laufzeit mit CUDA 12.4 müssen NVIDIA-Treiber und Container zur gewählten CUDA-Version passen, weil ein erfolgreich gestarteter Container noch keinen funktionierenden GPU-Kernel beweist. Prüfen Sie deshalb Geräteerkennung, Modell-Download und eine echte Testinferenz, bevor der Worker Jobs annimmt. Die vom Anbieter veröffentlichte Obergrenze von sieben MIG-Instanzen auf einer NVIDIA A100 mit 80 GB ist dabei keine Kapazitätszusage für Ihr Modell: Speicherbedarf, Instanzgröße und Interferenz müssen mit Ihrer konkreten Konfiguration geprüft werden.

Für den Rückweg sollte die Job-ID durch OpenTelemetry-Traces, Queue-Ereignisse und Ergebnisobjekte laufen, weil sonst ein fehlgeschlagener Auftrag über mehrere Systeme hinweg nicht verlässlich zuzuordnen ist. Ein Ablaufdatum für Ergebnisse und eine Dead-Letter-Queue gehören ebenfalls zum Vertrag: Ohne sie bleibt nach einem abgebrochenen Mietfenster unklar, welche Jobs noch erwartet werden und welche bewusst gescheitert sind. Diesen Vertrag würde ich vor der ersten automatischen Skalierung testen, nicht erst bei der ersten nächtlichen Störung.

Warteschlangenalter entscheidet früher als GPU-Auslastung

Für beide Optionen ist DCGM_FI_DEV_GPU_UTIL aus dem NVIDIA DCGM Exporter nützlich, aber als einzelnes Skalierungssignal ungeeignet, weil ein voller Eingangsbuffer schon Kunden warten lassen kann, während die GPU noch Daten lädt. Im Pool würde ich Queue-Alter, laufende Anfragen und verfügbaren GPU-Speicher gemeinsam betrachten. Bei Mietservern ist das Alter des ältesten noch ausführbaren Jobs wichtiger als die Zahl der wartenden Jobs, weil ein einzelner langer Auftrag sonst hinter vielen kurzen verborgen bleiben kann.

Setzen Sie einen SLO-Grenzwert als zu kalibrierende Betriebsgröße, etwa maximal 60 Sekunden Queue-Wartezeit für Batch-Aufträge, und messen Sie den Anteil der Jobs, der ihn tatsächlich einhält. Für interaktive Anfragen muss der Grenzwert aus dem Antwortzeitbudget abgeleitet werden, weil eine Provisionierung von mehreren Minuten nicht durch spätere schnelle Inferenz ausgeglichen wird. Ein zusätzlicher Mietserver hilft zudem erst nach seiner gemessenen Bereitstellungszeit; eine Skalierungsregel, die nur aktuelle Queue-Länge liest, reagiert bei schnell wachsenden Spitzen zwangsläufig spät.

Ich würde keinen automatischen Fallback von interaktiver Inferenz auf frisch gemietete Einzelserver einführen, solange der externe Pfad nicht regelmäßig unter Last geprobt wird. Der Grund ist operativ: Ein ungeübter Fallback addiert im Störfall DNS-, Netzwerk-, Treiber- und Datenzugriffsfehler zu einem bereits überlasteten Dienst. Stattdessen würde ich den Pool für die zugesagte Grundlast dimensionieren und Mietserver zunächst nur für explizit markierte, wiederholbare Batch-Jobs zulassen. Erst wenn deren Fehlerquote, Startzeit und Kosten über mehrere Lastfenster beobachtet wurden, verdient der Pfad mehr Verantwortung.

Der erste Schritt ist eine Woche Messdaten für Ankunftsrate, Queue-Alter, Laufzeit und Datentransfer pro Job. Spielen Sie damit einen Spitzenzeitraum nach und lassen Sie gleichzeitig einen Pool-Knoten ausfallen. Falls der Pool den SLO hält, rechnen Sie seine Leerlaufkosten gegen reale Mietangebote; falls nicht, testen Sie einen Mietserver mit einem wiederholbaren Batch-Job samt Abbruch und erneuter Zustellung. So entsteht eine Entscheidung aus Betriebsdaten statt aus GPU-Stundenpreisen.

Markiert: