Best Practices für die Batchinferenz in GKE

In diesem Dokument werden Best Practices zum Ausführen von Batch-Inferenz-Arbeitslasten in Google Kubernetes Engine (GKE) beschrieben. Die Batch-Inferenz ist der Prozess, bei dem ein Machine-Learning-Modell verwendet wird, um Vorhersagen für große Datasets zu generieren. Dabei werden hoher Durchsatz und Kosteneffizienz gegenüber sofortigen Antworten mit niedriger Latenz priorisiert.

In diesem Leitfaden wird zwischen Batch-Inferenz und Request Batching (oder dynamischem Batching) unterschieden. Request Batching ist eine serverseitige Technik in Engines wie vLLM oder SGLang, bei der gleichzeitige Echtzeitanfragen gruppiert werden, um die Effizienz des Beschleunigers zu optimieren. Sie können Batching von Anfragen auf Batchinferenz-Arbeitslasten anwenden.

Die Best Practices in diesem Leitfaden beziehen sich auf zwei gängige Arten von Batchinferenzmustern:

  • Batch-Inferenz nahezu in Echtzeit: Daten werden kurz nach der Generierung in Blöcken verarbeitet. Bei einer typischen Latenz von Sekunden bis Minuten wird bei diesem Ansatz der Bedarf an aktuellen Daten mit der Effizienz der gleichzeitigen Verarbeitung mehrerer Elemente in Einklang gebracht.
  • Offline-Batchinferenz: Verarbeitet große Mengen an gesammelten Daten in geplanten Intervallen (z. B. täglich oder wöchentlich). Die Latenz liegt in der Regel zwischen Stunden und Tagen, da diese Jobs oft in Nebenzeiten geplant werden, um die Ressourcenverfügbarkeit zu maximieren.

Diese Empfehlungen sind eine spezielle Optimierungsebene, die auf den Grundlagen basiert, die im Überblick über Best Practices für Inferenz in GKE beschrieben werden. Bevor Sie Batcharbeitslasten optimieren, sollten Sie die wichtigsten Best Practices für die Modellauswahl, die Quantisierung und die Auswahl von Beschleunigern befolgt haben.

Architekturmuster für die Batchinferenzverarbeitung auswählen

Die Auswahl des richtigen Architekturmusters ist die wichtigste Entscheidung für die Bereitstellung Ihrer Batch-Inferenz-Arbeitslasten, da sie sich auf die Kompromisse zwischen Latenz, Durchsatz und Kosten auswirkt. Um die Effizienz aufrechtzuerhalten, muss der Inferenzdurchsatz während der Nebenzeiten die Rate eingehender Anfragen übersteigen, damit die Warteschlangen nicht unendlich lang werden.

Batchinferenz nahezu in Echtzeit für burstartige Arbeitslasten verwenden

Die Batchverarbeitung in Echtzeit eignet sich gut für Anwendungsfälle, die häufige, inkrementelle Aktualisierungen erfordern, z. B.:

  • Nutzerempfehlungsprofile werden alle paar Minuten auf Grundlage der letzten Interaktionen aktualisiert.
  • Verarbeitung von Erwähnungen in sozialen Medien in Abständen von einer Minute für die Echtzeitüberwachung.
  • Marktbewegende Signale aus hochfrequenten Finanzdatenstreams erkennen
  • Sentimentanalyse von eingehendem Kundenfeedback oder Newsfeeds

Wählen Sie dieses Muster aus, wenn Ihre Arbeitslast Latenzzeiten von einigen Sekunden bis zu einigen Minuten tolerieren kann.

Berücksichtigen Sie bei der Implementierung von Batchinferenz nahezu in Echtzeit die folgenden Eigenschaften:

  • Latenz: Die Zeit bis zum ersten Token kann zwischen einigen Sekunden und mehreren Minuten liegen.
  • Datenquellen: In der Regel verarbeiten Sie Datasets mit einer Größe von Megabyte bis Gigabyte, z. B. Nachrichten aus Pub/Sub oder Dateien aus Cloud Storage, die über einen kurzen Zeitraum hinweg erfasst wurden.
  • Compute-Muster: Ihre Infrastruktur sollte einen kontinuierlichen Dienst unterstützen, der häufige Arbeitslastspitzen bewältigt.
  • Kostenoptimierung: Dieses Muster bietet ein Gleichgewicht zwischen Echtzeitinferenz mit niedriger Latenz und Offlineverarbeitung mit hohem Durchsatz.

Offline-Batchinferenz für große Datasets verwenden

Die Offline-Batchverarbeitung eignet sich ideal für umfangreiche, episodische Jobs, bei denen Verzögerungen von Stunden oder Tagen toleriert werden können, z. B. für:

  • Erstellung nächtlicher Risikobewertungsberichte auf Grundlage der Finanztransaktionen des Vortags.
  • Produkteinbettungen für einen gesamten Katalog erstellen, um nachgelagerte Such- und Empfehlungssysteme zu unterstützen.
  • Große Datasets mit Bildern für das Modelltraining oder die Archivkategorisierung labeln.

Wählen Sie dieses Muster aus, wenn Sie große Datenmengen verarbeiten und Latenzen von Stunden bis zu mehreren Tagen tolerieren können.

Berücksichtigen Sie bei der Implementierung der Offline-Batchinferenz die folgenden Merkmale:

  • Latenz: Die Latenz beim Start von Arbeitslasten liegt in der Regel zwischen Minuten und Tagen, da Jobs oft außerhalb der Spitzenzeiten geplant werden.
  • Datenquellen: Sie verarbeiten große Datasets von Gigabyte bis Petabyte, die in der Regel in Cloud Storage- oder BigQuery-Tabellen gespeichert sind.
  • Berechnungsmuster: Sie verwenden episodische, burstartige Jobs, die initialisiert werden, die Daten verarbeiten und dann beendet werden.
  • Kostenoptimierung: Dieses Muster lässt sich mit einem nutzungsbasierten Modell sehr gut optimieren. Da Offlinejobs flexible Abschlusszeiträume haben, empfehlen wir, Spot-VMs zu verwenden, um die Kosten zu senken.

Durchsatz und Kosteneffizienz optimieren

Batch-Inferenz-Arbeitslasten eignen sich besonders für kostensparende Infrastruktur, die Unterbrechungen beinhalten kann.

Mit Spot-VMs Rechenkosten senken

Nutzen Sie die Rabatte von Spot-VMs für Batch-Jobs. Da Batchinferenz-Arbeitslasten in der Regel Latenz und Unterbrechungen tolerieren, eignen sie sich gut für die reduzierten Preise von Spot-Kapazität.

Achten Sie darauf, dass in Ihrem Code für die Batchinferenz Checkpointing implementiert ist, um potenzielle Unterbrechungen zu verarbeiten. Wenn eine Spot-VM vorzeitig beendet wird, können Sie einen neuen Knoten erstellen und Ihre Arbeitslast mit dem zuletzt verarbeiteten Batch fortsetzen, anstatt von vorn zu beginnen.

Batchgröße für Arbeitslast und Anfragebatchgröße optimieren

Um Ressourcenkonflikte und Job-Timeouts zu vermeiden, muss die Anzahl der Elemente, die an Ihre Engine gesendet werden (Arbeitslast-Batch), mindestens so groß sein wie die Anzahl der gleichzeitigen Anfragen, die der Server verarbeiten kann (Anfrage-Batch), um eine Unterauslastung von Beschleunigern zu vermeiden.

Batchgröße der Arbeitslast anpassen

Die Batchgröße der Arbeitslast ist die Gesamtzahl der Elemente, die in einer einzelnen Arbeitseinheit an die Inferenz-Engine gesendet werden. Sie konfigurieren dies in Ihrer Client-Übermittlungslogik oder Kubernetes-Jobkonfiguration, indem Sie Ihre Daten aufteilen oder mehrere Elemente in einer einzelnen Anfrage gruppieren.

Verwenden Sie die folgenden Grenzwerte, um die optimale Batchgröße für die Arbeitslast zu ermitteln:

  • Mindest-Batchgröße berechnen: Die Batchgröße Ihrer Arbeitslast muss mindestens so groß sein wie die Batchgröße Ihrer Anfrage. Wenn Sie beispielsweise ein einzelnes Element an einen Server senden, der 256 Elemente gleichzeitig verarbeiten kann, wird der Server nicht ausreichend genutzt. Die Mindestgröße finden Sie in der Konfiguration Ihres Inferenzservers, z. B. im max_num_seqs-Argument in vLLM. Sie können Ihre Clientlogik so konfigurieren, dass mehrere Elemente in einer einzelnen Anfrage gruppiert werden, oder Ihre Daten so aufteilen, dass jeder Job eine Mindestmenge an Daten erhält, die der Batchgröße der Anfrage entspricht oder diese überschreitet.
  • Maximale Batchgröße berechnen: Achten Sie darauf, dass die Batchgröße Ihrer Arbeitslast es dem Pod ermöglicht, die Ausführung vor Erreichen des in Ihrem Kubernetes-Job definierten activeDeadlineSeconds-Time-outs abzuschließen. Schätzen Sie die Zeit, die für die Verarbeitung eines Anfrage-Batch erforderlich ist, und legen Sie die Arbeitslastgröße so fest, dass der Pod die Frist deutlich einhält. Wenn Ihr activeDeadlineSeconds beispielsweise 3.600 Sekunden und Ihr Start-Overhead 600 Sekunden beträgt, muss die maximale Ausführungszeit so festgelegt sein, dass der Pod in weniger als 3.000 Sekunden abgeschlossen werden kann.

Wenn die Batchgröße Ihrer Arbeitslast zu klein ist, verschwendet Ihr Job Zeit mit dem Pod-Start-Overhead (Gewichte herunterladen, bereitstellen, Beschleuniger initialisieren). Wenn sie zu groß ist, besteht das Risiko, dass der Job von GKE aufgrund des Zeitlimits für activeDeadlineSeconds beendet wird. In diesem Fall schlägt der Job fehl und der Fortschritt geht verloren.

Batchgröße für Anfragen optimieren

Die Batchgröße für Anfragen ist die Anzahl der gleichzeitigen Anfragen, die der Inferenzserver gleichzeitig auf dem Accelerator verarbeitet. Sie optimieren diesen Parameter, indem Sie serverspezifische Flags in der Konfiguration des Inferenzservers anpassen, z. B. das Flag --max-num-seqs für vLLM.

Ihr Ziel ist es, die GPU-Auslastung zu maximieren, ohne Fehler aufgrund mangelnden Arbeitsspeichers (Out-of-memory, OOM) auszulösen. Wenn die Batchgröße der Anfrage nicht kalibriert ist, wird der Beschleuniger in Ihrem System entweder nicht ausreichend genutzt oder der Modellserver stürzt ab. Für vLLM können Sie Tools wie das vLLM-Script „auto_tune“ verwenden, um die besten Werte für die Einstellungen max_num_seqs und max_num_batched_tokens für Ihre spezifische Hardware zu ermitteln. Weitere Informationen finden Sie im Leitfaden Best Practices für Inferenz in GKE unter Konfiguration des Inferenzservers optimieren.

Asynchrone Komponenten für das Batching in beinahe Echtzeit implementieren

Für das Batching in Echtzeit empfehlen wir, Messaging-Puffer zu verwenden, um die Erfassungs- von der Inferenzebene zu entkoppeln.

Das folgende Architekturdiagramm zeigt ein Beispiel für eine Batch-Inferenzplattform in Echtzeit. Diese Architektur schützt Inferenzserver vor Trafficspitzen, verwaltet Arbeitsrückstände und sorgt für eine hohe Auslastung der Beschleuniger.

Das Diagramm zeigt den Fluss von Pub/Sub zu Abonnenten, einem Inferenz-Gateway und einem Inferenzserver. Die Ergebnisse werden in AlloyDB gespeichert und fehlgeschlagene Nachrichten werden an ein Dead-Letter-Thema gesendet.

Batch-Inferenzplattform in GKE in Echtzeit.

Die Architektur besteht aus den folgenden Komponenten:

  • Pub/Sub-Thema:Dient als dauerhafter Puffer für eingehende Clientnachrichten mit einer Aufbewahrungsdauer von 7 bis 31 Tagen.
  • Abonnent:Eine Komponente, die Nachrichten-Batches liest, Anfragen an den Inferenzserver sendet und die Verarbeitung bestätigt.
  • Subscriber-HPA:Das Subscriber-Deployment wird basierend auf dem Messwert num_undelivered_messages (Anzahl der unbestätigten Nachrichten) skaliert.
  • Speicher:Inferenz-Ergebnisse mit einer Datenbank (z. B. AlloyDB) oder einem Objektspeicher (z. B. Cloud Storage) speichern.
  • Inference Gateway:Stellt die Inferenzarbeitslasten für den Abonnenten bereit.
  • Inference Server:Verarbeitet die Batch-Inferenzanfragen (z. B. vLLM).
  • Server-HPA:Die Inferenz-Engine wird anhand von enginespezifischen Messwerten wie vllm:num_requests_waiting skaliert.
  • Thema für unzustellbare Nachrichten:Erfasst Nachrichten, deren Verarbeitung nach einer festgelegten Anzahl von Wiederholungen mit exponentiellem Backoff fehlschlägt.

Weitere Informationen finden Sie in der Referenzimplementierung auf GitHub.

Anfragen puffern und zusammenfassen

So verwalten Sie den Fluss von Anfragen:

  • Pub/Sub als dauerhaften Puffer verwenden:Implementieren Sie Pub/Sub, um Inferencing-Anfragen dauerhaft zu speichern. Diese Einrichtung fungiert als FIFO-Puffer, in dem Anfragen gespeichert werden, bis ein Consumer sie verarbeiten kann. So wird eine Serverüberlastung bei Trafficspitzen verhindert.
  • Pull-Abos mit clientseitiger Ablaufsteuerung verwenden:Konfigurieren Sie ein Pull-Abomodell. So kann Ihre Abonnentenanwendung Nachrichten nur dann explizit anfordern, wenn sie die Kapazität hat, sie zu verarbeiten. Sie haben also die volle Kontrolle über die Aufnahmerate.
  • Nachrichten aggregieren, um die Server-Batchgröße zu erreichen:Senden Sie nicht eine Pub/Sub-Nachricht als eine Inferenzanfrage. Stattdessen sollte der Abonnent mehrere Nachrichten in einer einzelnen Batchanfrage bündeln, die der optimalen Batchgröße des Inferenzservers entspricht (z. B. passend zu den max_num_seqs-Einstellungen in vLLM). So wird sichergestellt, dass die Beschleuniger vollständig ausgelastet sind, und der Durchsatz wird maximiert. Konfigurieren Sie die Pull-Einstellung max_messages Ihres Abonnenten auf ein Vielfaches von max_num_seqs, damit jeder Forward-Pass des Modells vollständig gesättigt ist.

Abonnenten und Server automatisch skalieren

Für eine effektive Batch-Inferenz müssen die Abonnenten (CPU-gebunden) anders skaliert werden als die Inferenzserver (GPU- oder TPU-gebunden).

  • Abonnenten basierend auf dem Arbeitsrückstand skalieren:Konfigurieren Sie den HorizontalPodAutoscaler (HPA) für Ihr Abonnentendeployment basierend auf dem num_undelivered_messages-Messwert von Pub/Sub. Weitere Informationen finden Sie unter Pod-Autoscaling anhand von Messwerten optimieren. Berechnen Sie die Anzahl der Replikate, die Sie verwenden möchten, mit der folgenden Gleichung:

    \[ desiredReplicas = \frac{num\_undelivered\_messages}{target\_latency\_seconds \times throughput\_per\_replica} \]

  • Infrastrukturkontingente einhalten:Beschränken Sie die maximale Anzahl von Replikaten Ihrer Abonnenten explizit, indem Sie die Einstellung maxReplicas in Ihrem HPA konfigurieren. Skalieren Sie die Anzahl der Abonnenten nicht über das hinaus, was das GPU- oder TPU-Kontingent Ihrer Inferenzserver unterstützt. Wenn du zu viele Abonnenten bereitstellst, wird der Engpass auf den Inferenzserver verlagert, was zu einer erhöhten Ressourcenkonkurrenz führt, ohne den Durchsatz zu erhöhen.

  • Inferenzserver anhand von Messwerten der Engine skalieren:Sie können das Deployment von Inferenzservern anhand von Messwerten skalieren, die direkt von der Inferenz-Engine exportiert werden (nicht nur über CPU/Arbeitsspeicher). Verwenden Sie beispielsweise die Einstellung vllm:num_requests_waiting für vLLM, mit der der Verarbeitungsrückstand direkt auf der Ebene des Modellservers gemessen wird. Weitere Informationen finden Sie unter Pods automatisch skalieren.

Fehler und Zeitüberschreitungen behandeln

So gehen Sie mit Fehlern und Zeitüberschreitungen um:

  • Bestätigungsfristen proaktiv verlängern:Konfigurieren Sie Ihren Abonnenten so, dass die Pub/Sub-Bestätigungsfrist (ack) für Nachrichten, die verarbeitet werden, proaktiv verlängert wird, um erneute Zustellungsschleifen und doppelte Verarbeitung zu vermeiden. Dieser Ansatz ist erforderlich, da Inferenzaufgaben oft länger dauern als die standardmäßigen Zeitüberschreitungszeiträume. In der Regel sollte der Verlängerungszeitraum länger als die Batchinferenzzeit im Worst-Case-Szenario sein.
  • Fehler mit einem Thema für unzustellbare Nachrichten isolieren:Aktivieren Sie ein Thema für unzustellbare Nachrichten, um automatisch fehlerhafte Nachrichten zu isolieren, bei denen die Zustellung wiederholt fehlgeschlagen ist. So wird verhindert, dass „Poison Pill“-Nachrichten die Warteschlange blockieren und die gesamte Pipeline zum Stillstand bringen.
  • Backoff-Strategien implementieren:Wenn der Inferenzserver Fehler vom Typ 429 (Too Many Requests) oder 503 (Service Unavailable) zurückgibt, muss der Abonnent diese abfangen und eine exponentielle Backoff-Strategie implementieren, um die Nutzung von Pub/Sub vorübergehend zu pausieren, bis der Server wieder verfügbar ist.

Offline-Batchjobs im großen Maßstab orchestrieren

Mit diesen Best Practices können Sie den Durchsatz maximieren, die Kosteneffizienz steigern, eine umfassende Rückverfolgbarkeit für Audits implementieren und ein erweitertes Kontingentmanagement und eine erweiterte Jobpriorisierung anwenden, wenn Sie umfangreiche Datasets verarbeiten.

JobSet für die verteilte Inferenz mit mehreren Knoten verwenden

Wir empfehlen, die Kubernetes-Ressource JobSet zu verwenden, um verteilte Inferenzarbeitslasten zu orchestrieren, für die mehrere Knoten zusammenarbeiten müssen, z. B. große Modelle, die auf TPU-Pods oder GPU-Clustern mit mehreren Knoten ausgeführt werden. Bei Standard-Kubernetes-Jobs kann nicht garantiert werden, dass alle erforderlichen Pods gleichzeitig gestartet werden. Dies kann bei verteilten Arbeitslasten zu Deadlocks führen.

JobSet ist eine Kubernetes-native API, mit der Gruppen von Jobs als Einheit verwaltet werden. Sie bietet die folgenden Vorteile für die Batchinferenz:

  • Gang-Scheduling:Damit wird sichergestellt, dass alle erforderlichen Ressourcen wie TPU-Slices oder GPU-Knoten verfügbar sind, bevor die Arbeitslast gestartet wird, um Deadlocks zu vermeiden.
  • Exklusive Platzierung:Damit wird sichergestellt, dass ein einzelnes JobSet exklusiven Zugriff auf die Netzwerk-Topologie (z. B. einen TPU-Slice) hat, um die Interconnect-Leistung zu maximieren.
  • Fehlerbehebung:Damit können Sie bestimmte replizierte Jobs oder den gesamten Satz neu starten, wenn ein Worker ausfällt. Das hängt von Ihrer Konfiguration ab.

Indexierte Jobs für das Sharding von Daten verwenden

Wenn Sie JobSet verwenden, konfigurieren Sie ReplicatedJob so, dass die Einstellung completionMode: Indexed verwendet wird. Mit dieser Einstellung wird automatisch eine Umgebungsvariable JOB_COMPLETION_INDEX in jeden Pod eingefügt. Ihr Inferenzcode kann diesen Index verwenden, um deterministisch einen eindeutigen Datenshard für die Verarbeitung auszuwählen.

Wenn Sie beispielsweise einen Cloud Storage-Bucket mit 100.000 Bildern haben und ein JobSet mit einem Parallelismus von 10 bereitstellen, liest jeder der 10 Pods beim Start seinen Index (0–9). Pod 0 kann dann berechnen, dass er die Bilder 0 bis 9.999 verarbeiten soll, während Pod 1 die Bilder 10.000 bis 19.999 verarbeitet. Dadurch ist kein separater Dienst für die Aufgabenwarteschlange erforderlich.

Sidecar-Muster für die Serversättigung verwenden

Um die Beschleunigernutzung zu maximieren, konfigurieren Sie Ihre JobSet-Pods mit zwei Containern, die das Sidecar-Muster verwenden:

  • Inferenzserver:Ein optimierter Server (z. B. vLLM), der sich ausschließlich auf GPU- oder TPU-Berechnungen konzentriert.
  • Client-Treiber:Ein Logikcontainer, der asynchron eine große Anzahl von Anfragen an den Server auf localhost sendet.

Durch diese Entkopplung wird sichergestellt, dass die GPU oder TPU immer ausgelastet ist und nie im Leerlauf ist, während auf Netzwerk-E/A oder die Datenvorverarbeitung gewartet wird. Ohne diesen Ansatz kann es bei Modellen, die Daten sequenziell laden, dazu kommen, dass der Beschleuniger auf den Abschluss von E/A-Vorgängen wartet, was zu einer Unterauslastung führt. Anstatt beispielsweise auf die Verarbeitung der Daten zu warten, kann der Clienttreiber Daten vorab abrufen und kontinuierlich asynchrone Anfragen an den Inferenzserver senden. So wird sichergestellt, dass die Anfragewarteschlange des Beschleunigers immer gefüllt ist.

Zusammenfassung der Checkliste

Kategorie Best Practice
Architekturmuster
Kosten und Durchsatz
Marketingbotschaften und Skalierung
Orchestrierung