Alle Beiträge

Modelle orchestrieren, nicht nur Container: Kubernetes im KI-Zeitalter

Artikel anhören (experimentell) · 18:44

00:00 / 18:44

Ein Router-Knoten, der Anfragen auf ein Rack unterschiedlich großer GPU-Modellserver verteilt, mit gestrichelten Linien, die aus dem Cluster zu externen Modell-APIs entkommen

Der größte Teil meiner Arbeit lebt auf der Anwendungsschicht. Dort werden Produkte gewonnen: die Abläufe, die APIs, die Teile, auf die Nutzer zeigen können. Viele Produkte stehen direkt auf einem einzigen Anbieter, einem OpenAI- oder Anthropic-Modell hinter einem einzigen Schlüssel, und sind damit bestens bedient. Aber sobald ein Produkt über ein Modell hinauswächst oder eigene Hardware anfasst, kommt es auf einer Schicht zu stehen, von der seine Nutzer nie gehört haben: der, die im Moment nach dem Drücken von Enter entscheidet, welches Modell ihnen antwortet, auf welchem Stück Hardware, und was passiert, wenn diese Hardware gesättigt oder weg ist. Niemand bedankt sich bei dieser Schicht, wenn sie funktioniert. Sie bekommt genau eine Anforderung, und die ist nicht verhandelbar: Sie darf die Menschen nicht im Stich lassen, die nicht einmal wissen, dass sie sie benutzen.

Und es geht längst nicht mehr nur um die Produkte, die wir ausliefern, sondern auch darum, wie wir sie bauen. Die Coding-Tools auf meinem Rechner machen dieselben Züge, um die es in diesem Artikel geht: Claude Code gibt leichte Teilaufgaben an ein kleines, schnelles Modell und behält das harte Denken auf einem großen, Cursor routet je nach Aktion zwischen eigenen Modellen und Frontier-APIs, und Anthropics Bericht über ihr Multi-Agenten-Recherchesystem beschreibt genau diese Form: Ein Opus-Leitagent orchestriert günstigere Sonnet-Subagenten. Modell-pro-Aufgabe-Routing wird leise zu der Art, wie Software gemacht wird, nicht nur, wie sie ausgeliefert wird.

Währenddessen schließen überall Teams Verträge mit Frontier-API-Anbietern (OpenAI, Anthropic, Google, xAI, MiniMax), während sie zugleich Open-Weights-Familien wie Llama, Qwen, DeepSeek oder Mistral auf den GPUs hosten, die sie ergattern konnten, und jemand muss all diese Modelle mit der verfügbaren Hardware orchestrieren. "Wie gut kennst du Kubernetes" ist eine Frage von 2019. Die Frage von 2026 lautet: Kannst du aus einem festen Pool von GPUs, einer Reihe von Open-Weights-Modellen, die du selbst hosten willst, und einer Reihe von Frontier-Modellen, die du nur über eine API mieten kannst, einen einzigen langweiligen Endpoint machen? Ich habe einen funktionierenden Proof of Concept gebaut, der die Frage beantwortet: einen Modell-Router, eine Reihe GPU-bewusster Deployments und eine Demo, die auf einem Laptop läuft. Dieser Post ist die Begründung dahinter.

Die Ökonomie schreit: Auslastung

Etwas Kontext, warum genau diese Fähigkeit plötzlich zählt. Die Friday Charts von Blockworks haben diese Woche nachgerechnet: Hyperscaler und Neoclouds werden bis Jahresende rund 2 Billionen Dollar Capex ausgegeben haben, ein einziger GPU-Schrank in Nvidias neuen Vera-Rubin-Pods wird auf 9 Millionen Dollar geschätzt, und der Newsletter beginnt mit Jensen Huangs Erklärung, "das Rechenzentrum ist die neue Einheit des Computings". Dieselbe Ausgabe merkt an, dass ein Gigawatt Rechenzentrumskapazität inzwischen fast 500 Billionen Tokens produziert; 2023 war diese Zahl noch ungefähr null.

Du bist wahrscheinlich kein Hyperscaler, aber die Zahlen tun auch auf Straßenniveau weh: Eine einzige High-End-Karte kann deutlich über 40.000€ kosten, bevor es überhaupt einen Server gibt, in den man sie steckt, und die Frage, wie man diese eine Karte aufteilt (ganz, halbiert, in Siebtel geschnitten), ist eine Planungsübung für sich, weshalb die GPU-Hersteller Sizing-Guides und Konfigurationsmatrizen mitliefern, die Käufer durch die Optionen führen.

Um klar zu sein: Nicht jedes Team muss sich darum kümmern, und es ist keine binäre Wahl zwischen einer API und dem eigenen Cluster. Es ist eine Leiter, und jede Sprosse nach unten gibt dir mehr Kontrolle über das, was unter dem Endpoint liegt:

  • Komplett API. Ein OpenAI- oder Anthropic-Modell hinter einem Schlüssel, oder ein Azure-OpenAI-Deployment mit seinem festen Modellkatalog und Token-pro-Minute-Quoten. Deine Hebel sind Quoten, Caching und das richtige Modell-Tier pro Aufgabe.
  • Gemietete Inferenz. Hugging Face Inference Endpoints, RunPod und ihre Peers, von denen ich einige selbst genutzt habe. Du wählst Modell und Karte, jemand anderes betreibt die Maschinen, und der Zähler rechnet pro GPU-Stunde ab, ob du beschäftigt bist oder nicht.
  • Cloud-GPUs, die du selbst betreibst. GPU-VMs im Stundentakt auf GCP, AWS oder Azure (sobald dir der Anbieter Quota gewährt, denn die Knappheit ist real), oder GPU-Node-Pools an einem verwalteten Kubernetes-Cluster wie GKE, EKS oder AKS, oft zu Spot-Preisen, wenn dein Workload Verdrängung verträgt. Der Zähler sieht aus wie auf der Sprosse darüber; der Unterschied ist, dass die Orchestrierung unterhalb des Endpoints jetzt dir gehört.
  • Metall, das dir gehört. Karten, gekauft und on-prem oder in Colocation ins Rack geschraubt: die 40.000€-Karte von vorhin, abgeschrieben statt gemessen. Das lohnt sich bei anhaltender Auslastung oder harten Datenresidenz-Auflagen, und Kubernetes läuft auch auf Bare Metal, also ziehen dieselben Manifeste mit dir dorthin.

Ab der zweiten Sprosse abwärts steht der Zähler nie still, und der größte Hebel, den ein normales Team vollständig kontrolliert, wird der ungenutzte Anteil der GPU-Stunde, die es bereits bezahlt. Orchestrierung ist keine Plattform-Nettigkeit; sie ist der Unterschied zwischen einer Flotte, die ihre Kosten verdient, und einer sehr teuren Heizung. Kubernetes ist nicht der einzige Weg dorthin: Slurm gehört nach wie vor die HPC-Welt, Ray bringt seinen eigenen Scheduler mit, und jede Cloud verkauft dir gern einen Managed Endpoint, der das Problem hinter einer Marge versteckt. Aber wenn dein Produkt bereits auf Kubernetes läuft, ist der pragmatische Zug, dieselbe Plattform auch die Modelle bin-packen zu lassen: heterogene Workloads deklarativ auf heterogene Maschinen, neben den Workloads, die sie ohnehin schon ausführt.

Drei Kräfte, ein Design

Jedes Multi-Modell-Setup, das ich gesehen habe, wird von denselben drei Kräften auseinandergezogen:

  • Modelle sind heterogen. Manche laufen auf deinen GPUs (Qwen, Llama und der Rest der Open-Weights-Welt), manche existieren nur hinter einer API (die Frontier-Tiers, egal bei wem du unterschreibst). Sie unterscheiden sich in Kontextfenster, Tool-Unterstützung, Kosten, Latenz und Datenresidenz-Implikationen.
  • Hardware ist heterogen und endlich. Eine echte Flotte ist, was du bekommen konntest: ein paar A100, ein Pool L4, H100 nächstes Quartal, wenn die Bestellgötter lächeln. Jedes Modell hat ein oder zwei Platzierungen, die ökonomisch Sinn ergeben; der Rest ist Verschwendung.
  • Produktcode will von alldem nichts wissen. Er will einen OpenAI-kompatiblen Endpoint und einen Modellnamen, und er soll weiterlaufen, wenn ein Anbieter getauscht, ein Vertrag unterschrieben oder eine Karte durchbrennt.

Der PoC teilt die Verantwortung entlang dieser Linien auf. Kubernetes besitzt die Platzierung: welcher Pod auf welcher GPU landet, ausgedrückt in der Sprache, die es bereits spricht, also Resource Requests, Node Selectors, Taints. Ein kleiner Router besitzt die Auswahl: welches Backend diese Anfrage bedient, mit Failover. Eine Registry besitzt die Policy: was existiert, was es kann, was es kostet. Sie lebt in einer ConfigMap, hot-reloaded, sodass "wir haben mit einem neuen Anbieter unterschrieben" ein kubectl apply ist, kein Produkt-Deploy.

Die Platzierung ist, wo das Geld liegt

Der Teil des PoC, den ich am härtesten verteidigen würde, ist sein GPU-Playbook, denn hier müssen Kubernetes-Wissen und GPU-Wissen zusammenkommen:

Das GPU-Playbook des PoC: jede Workload, die Hardware, auf der sie landet, und die Technik, die sie passend macht.
Workload Hardware Technik
Qwen3-32B 2x A100-80GB Tensor-Parallelismus, TP=2, NVLink auf einem Knoten
Qwen3-8B 1x L4-24GB AWQ-4-Bit-Quantisierung, um auf die günstige Karte zu passen
Embeddings Ein Siebtel einer A100 MIG-Slice, 1g.10gb, hardware-isoliert
Dev-Experimente Geteilte GPUs Time-Slicing, vier Workloads pro Karte
Frontier-Modelle Niemandes GPUs Externe API hinter demselben Endpoint

Jede Zeile ist ein kleines Argument. Das 32B-Modell passt nicht mit Luft zum Atmen auf eine 80GB-Karte (allein die Gewichte sind ~64GB in bf16), also wird es über zwei Karten geshardet, und das Manifest pinnt das exakte GPU-Produkt per nodeSelector, denn "zwei GPUs irgendwo im Cluster" und "zwei NVLink-verbundene A100 auf einem Knoten" sind völlig verschiedene Maschinen. Das 8B-Modell geht den umgekehrten Weg: quantisieren, bis es auf die Karte passt, die du tatsächlich kaufen kannst, und dann horizontal skalieren. Der durch Quantisierung freigewordene Speicher ist nicht nur Passform, er ist KV-Cache, und KV-Cache ist Parallelität. Quantisierung kauft dir oft Durchsatz, nicht nur kleinere Gewichte.

Die Embeddings-Zeile ist meine liebste, denn dort stirbt in den meisten Clustern leise das Geld. Ein Embedding-Modell braucht etwa 2GB, und dieselbe Rechnung gilt für Reranker, Guardrail-Klassifikatoren und Transkriptionsmodelle der Whisper-Klasse, die kleinen Always-on-Diener, die jedes KI-Produkt ansammelt. Gib irgendeinem davon eine ganze A100, und ~97% einer sehr teuren Karte stehen für immer still. MIG, Multi-Instance GPU, zerteilt das Silizium in sieben isolierte Slices; das Deployment fordert nvidia.com/mig-1g.10gb: 1 an wie jede andere Ressource, und eine Karte bedient sieben davon, jeder hardware-isoliert von seinen Nachbarn. Time-Slicing macht Ähnliches für Dev-Cluster, ganz ohne Isolation: Ein gieriger Prozess kann den übrigen ein OOM bescheren, und genau deshalb ist diese Konfiguration im PoC als dev-only markiert.

Nichts davon ist exotisch. Es ist gewöhnliches Kubernetes (Resource Requests, Labels, Probes, Disruption Budgets), gerichtet auf eine ungewohnte Ressource. Das ist die eigentliche Bedeutung von "Kubernetes im KI-Zeitalter nutzen": Die Plattform hat sich nicht geändert, der Workload schon.

Auch die Serving-Engine ist eine Entscheidung

Alles im Cluster wird hier von vLLM bedient, und das ist eine getestete Präferenz, kein Default. Die Eigenschaft, die dafür spricht, ist kontraintuitiv: Es wird unter Druck besser. Continuous Batching bedeutet: Je mehr gleichzeitige Anfragen du auf eine Instanz wirfst, desto mehr Tokens pro GPU-Stunde produziert sie; wenig Traffic ist der pathologische Fall, nicht die Stoßzeit. Ich habe unterwegs auch Ollama ausprobiert, und der Vergleich lehrte dieselbe Lektion wie die Platzierungstabelle: Werkzeuge kodieren Annahmen über ihren Workload. Ollama ist hervorragend für das, wofür es gebaut wurde: eine Person, eine Maschine. Aber unter gleichzeitiger Last war es in meinen Tests deutlich langsamer, und schnelles Wechseln zwischen verschiedenen Modellen machte ihm echte Probleme, weil jeder Wechsel ein Modell entlädt und ein anderes lädt. Eine vLLM-Instanz hat dieses Problem nie, weil sie sich nie umzieht: ein Modell pro Server für seine gesamte Lebenszeit, und der Router darüber entscheidet, welchen Server eine Anfrage verdient.

Routing ist eine Policy, kein If-Statement

Der Router spricht /v1/chat/completions und löst Namen in zwei bewusst verschiedenen Modi auf. Ein exakter Modellname (qwen3-32b) folgt einer expliziten, vom Operator geschriebenen Failover-Kette: zuerst das vLLM im Cluster, eine Frontier-API als Überlauf. Keine Cleverness. Ein Alias (chat-default, chat-long-context) ist stattdessen ein Satz von Anforderungen, etwa minimales Kontextfenster oder Tool-Unterstützung, plus eine Strategie: cheapest-healthy sortiert alle qualifizierten Backends nach Mischkosten; prefer-cluster hält den Traffic auf eigener Hardware, außer nichts im Cluster qualifiziert sich, was zugleich der Datenresidenz-Schalter ist.

Die Aliase sind die Hälfte, die die Arbeitsweise eines Teams verändert. Produktcode fragt nach einem Ergebnis, nicht nach einem Vendor, also werden "das 8B schafft diesen Traffic inzwischen" oder "route langen Kontext zu dem Anbieter, der ihn am besten bepreist" zu Registry-Änderungen, die in Sekunden live sind. Ein Circuit Breaker pro Backend überspringt kranke Backends, jede Antwort trägt einen x-router-backend-Header, damit du immer weißt, wer geantwortet hat, und Streaming failt nur vor dem ersten Byte über, denn sobald Tokens fließen, würde ein unsichtbares Neu-Generieren die Kosten verdoppeln und zwei verschiedene Antworten zusammenkleben.

Eines verweigert der Router: Queuing. vLLM führt bereits die ehrliche Warteschlange, und sie hinter einer zweiten zu verstecken würde das beste Autoscaling-Signal des Systems zerstören. Womit wir bei der Falle wären.

Die GPU-Auslastungsfalle

Wenn du ein operatives Detail aus diesem Post mitnimmst, dann dieses: GPU-Auslastung ist ein irreführendes Autoscaling-Signal für LLM-Serving. Continuous Batching hält die Karte beschäftigt, egal ob sie zwei Anfragen bequem bedient oder unter zweihundert ertrinkt; beides liest sich als ~100%. Skalierst du nach Auslastung, skalierst du nach Rauschen.

Die Zahl, die abbildet, was Nutzer spüren, ist die Warteschlangentiefe: vLLMs num_requests_waiting, angenommene Anfragen, die noch nicht in den Batch eingeplant sind. Der PoC skaliert den 8B-Arbeitstier-Tier über KEDA genau auf dieser Metrik: schnell nach oben, weil Warteschlangen sich aufschaukeln; langsam und Pod für Pod nach unten, weil jede neue Replika Minuten an Modell-Ladezeit kostet und Flapping teuer ist. Und wenn der ganze L4-Pool erschöpft ist? Das ist kein Autoscaling-Problem mehr. Die Failover-Kette läuft zu den externen APIs über, dem elastischen Tier, das du genau deshalb mietest, weil du es nicht besitzt.

Und auch die Anwendungsschicht darf nichts davon wegignorieren: Ein GPU-Engpass taucht irgendwann im Produkt auf, und die ehrlichen Antworten leben dort: Backpressure, Timeouts, ein degradierter Modus. Ein kleineres Modell mit ehrlichem Etikett schlägt einen Spinner, der bei sechzig Sekunden stirbt.

Was der PoC beweist und was noch nicht

Das Ganze läuft ohne eine einzige GPU: Ein local-Overlay ersetzt jedes vLLM-Deployment durch einen Mock, der dieselbe API spricht und dieselben Metriken exportiert, sodass Routing, Failover und die Skalierungssignale auf einem Laptop-kind-Cluster demonstriert werden können. Kill das 32B-Deployment mitten in der Demo und sieh zu, wie dieselbe Anfrage vom "externen" Tier beantwortet zurückkommt. Die Routing-Logik ist pur und unit-getestet; die Manifeste rendern per kustomize in der CI.

Es bleibt ein PoC, und das sage ich lieber selbst, bevor du es entdeckst: keine Authentifizierung, kein Rate Limiting, keine Budgets pro Tenant, und die externen Schlüssel sind schlichte Secrets. Das ist die erste Produktionsiteration, und nichts davon ändert die Architektur. Die Plattformkonzepte übertragen sich fast unverändert zwischen Distributionen und Clouds; über Erfolg entscheidet, ob die Ökonomie des Workloads verstanden wurde, bevor das YAML geschrieben war.

Die Anwendungsschicht ist immer noch der Ort, an dem ich die meisten meiner Tage verbringe, und immer noch der Ort, an dem Produkte gewonnen werden. Aber unter den KI-Produkten, die über ein Modell hinauswachsen, liegt inzwischen eine Schicht, die den härtesten Infrastrukturjob des Jahrzehnts macht: eine Beschränkung aus der 2-Billionen-Capex-Ära, einen Modell-Zoo und einen festen Haufen Hardware nehmen und sie sich wie einen zuverlässigen, austauschbaren, langweiligen Endpoint verhalten lassen, für Menschen, die nie erfahren werden, dass es sie gibt. Sie muss funktionieren. Sie darf sie nicht im Stich lassen. Der PoC ist meine Art zu zeigen, dass sie das auch nicht muss.

Fragen, die dieser Artikel beantwortet

  • Brauche ich eigene GPUs für ein KI-Produkt, oder reicht ein API-Schlüssel?

    Ein Schlüssel reicht für viele Produkte, und die Wahl ist nicht binär. Es ist eine Leiter mit vier Sprossen: komplett API hinter einem einzigen Anbieter, gemietete Inferenz bei Diensten wie Hugging Face Inference Endpoints oder RunPod, Cloud-GPUs, die du selbst betreibst, und Metall, das dir gehört und das du selbst ins Rack schraubst. Jede Sprosse nach unten gibt dir mehr Kontrolle über das, was unter dem Endpoint liegt, und ab der zweiten Sprosse zahlst du pro GPU-Stunde, ob du beschäftigt bist oder nicht.

  • Ist Kubernetes nicht überdimensioniert, nur um ein paar Modelle zu betreiben?

    Es ist nicht der einzige Weg, und die Alternativen sind real: Slurm gehört nach wie vor die HPC-Welt, Ray bringt seinen eigenen Scheduler mit, und jede Cloud verkauft dir einen Managed Endpoint, der das Problem hinter einer Marge versteckt. Das pragmatische Argument für Kubernetes ist enger als der Hype: Wenn dein Produkt ohnehin darauf läuft, lass dieselbe Plattform die Modelle neben den Workloads bin-packen, die sie schon ausführt. Was der PoC benutzt, ist gewöhnliches Kubernetes, also Resource Requests, Labels, Probes und Disruption Budgets, gerichtet auf eine ungewohnte Ressource.

  • Warum vLLM im Cluster statt Ollama?

    vLLM wird unter Druck besser, und genau diese Eigenschaft spricht dafür. Continuous Batching bedeutet, dass mehr gleichzeitige Anfragen mehr Tokens pro GPU-Stunde erzeugen, also ist wenig Traffic der pathologische Fall und nicht die Stoßzeit. Ollama ist hervorragend für eine Person und eine Maschine, aber unter gleichzeitiger Last war es in meinen Tests deutlich langsamer, und der Wechsel zwischen Modellen machte ihm echte Probleme, weil jeder Wechsel ein Modell entlädt und ein anderes lädt. Ein vLLM-Server hält ein Modell für seine gesamte Lebenszeit, und der Router darüber entscheidet, auf welchen Server eine Anfrage geht.

  • Was passiert, wenn alle GPUs schon ausgelastet sind?

    Das ist dann kein Autoscaling-Problem mehr, sondern ein Failover-Problem: Die Kette läuft zu den externen APIs über, dem elastischen Tier, das du genau deshalb mietest, weil du es nicht besitzt. Unterhalb dieses Punktes skaliert der PoC den 8B-Tier über KEDA auf vLLMs num_requests_waiting, schnell nach oben, weil Warteschlangen sich aufschaukeln, und langsam nach unten, weil jede neue Replika Minuten an Modell-Ladezeit kostet. Die Anwendungsschicht muss den Rest trotzdem beantworten, mit Backpressure, Timeouts und einem degradierten Modus, denn ein kleineres Modell mit ehrlichem Etikett schlägt einen Spinner, der bei sechzig Sekunden stirbt.