Best practice per l'inferenza batch su GKE

Questo documento fornisce le best practice per l'esecuzione di workload di inferenza batch su Google Kubernetes Engine (GKE). L' inferenza batch è il processo di utilizzo di un modello di machine learning per generare previsioni su set di dati di grandi dimensioni, dando la priorità a un throughput elevato e all'efficienza dei costi rispetto a risposte immediate a bassa latenza.

Questa guida distingue l'inferenza batch dal batching delle richieste (o batching dinamico), una tecnica lato server in motori come vLLM o SGLang che raggruppa le richieste in tempo reale simultanee per ottimizzare l'efficienza dell'acceleratore. Puoi applicare il batching delle richieste ai workload di inferenza batch.

Le best practice in questa guida riguardano due tipi comuni di pattern di inferenza batch:

  • Inferenza asincrona: elabora i dati in blocchi poco dopo la generazione. Con una latenza tipica che va da secondi a minuti, questo approccio bilancia la necessità di dati aggiornati con l'efficienza dell'elaborazione simultanea di più elementi. L'inferenza asincrona viene a volte definita inferenza in tempo reale.
  • Inferenza batch: elabora grandi volumi di dati accumulati a intervalli pianificati (ad esempio, ogni notte o ogni settimana). La latenza in genere varia da ore a giorni, poiché questi job vengono spesso pianificati durante le ore non di punta per massimizzare la disponibilità delle risorse.

Questi consigli sono un livello di ottimizzazione specializzato basato sui fondamenti descritti nella panoramica delle best practice di inferenza su GKE. Prima di ottimizzare i workload batch, assicurati di aver seguito le best practice principali per la selezione del modello, la quantizzazione e la scelta dell'acceleratore.

Scegliere un pattern architetturale per l'elaborazione dell'inferenza batch

La selezione del pattern architetturale corretto è la decisione più critica per il deployment dei workload di inferenza batch, perché influisce sui compromessi tra latenza, throughput e costi. Per mantenere l'efficienza, assicurati che il throughput di inferenza superi la frequenza delle query in entrata durante le ore non di punta per evitare che le code crescano all'infinito.

Utilizzare l'inferenza asincrona per il lavoro burst

L'inferenza asincrona è adatta ai casi d'uso che richiedono aggiornamenti incrementali frequenti, ad esempio:

  • Aggiornamento dei profili di suggerimenti degli utenti ogni pochi minuti in base alle interazioni recenti.
  • Elaborazione delle menzioni sui social media a intervalli di un minuto per il monitoraggio in tempo reale.
  • Rilevamento di segnali che influenzano il mercato dai flussi di dati finanziari ad alta frequenza.
  • Esecuzione dell'analisi del sentiment sul feedback dei clienti in entrata o sui feed di notizie.

Scegli questo pattern se il tuo workload può tollerare una latenza che va da diversi secondi a pochi minuti.

Quando implementi l'inferenza asincrona, tieni presente le seguenti caratteristiche:

  • Latenza: puoi prevedere un tempo al primo token che va da decine di secondi a minuti.
  • Origini dati: in genere elabori set di dati che vanno da megabyte a gigabyte, ad esempio messaggi da Pub/Sub o file da Cloud Storage accumulati in un breve periodo di tempo.
  • Pattern di calcolo: la tua infrastruttura deve supportare un servizio continuo che gestisce frequenti burst di lavoro.
  • Ottimizzazione dei costi: questo pattern offre un equilibrio tra inferenza in tempo reale a bassa latenza ed elaborazione batch ad alto throughput.

Utilizzare l'inferenza batch per set di dati di grandi dimensioni

L'inferenza batch è ideale per job episodici su larga scala che possono tollerare ritardi di ore o giorni, ad esempio:

  • Generazione di report di valutazione del rischio notturni basati sulle transazioni finanziarie del giorno precedente.
  • Creazione di incorporamenti di prodotti per un intero catalogo per alimentare i sistemi di ricerca e suggerimenti downstream.
  • Etichettatura di set di dati di immagini di grandi dimensioni per l'addestramento del modello o la categorizzazione dell'archivio.

Scegli questo pattern se elabori grandi volumi di dati e puoi tollerare latenze che vanno da ore a diversi giorni.

Quando implementi l'inferenza batch, tieni presente le seguenti caratteristiche:

  • Latenza: la latenza di avvio del workload in genere varia da minuti a giorni perché i job vengono spesso pianificati durante le ore non di punta.
  • Origini dati: elabori set di dati di grandi dimensioni da gigabyte a petabyte, in genere archiviati in tabelle di Cloud Storage o BigQuery.
  • Pattern di calcolo: utilizzi job episodici e burst che inizializzano, elaborano i dati e poi terminano.
  • Ottimizzazione dei costi: questo pattern è altamente ottimizzabile con un modello pay-per-use Poiché i job batch hanno finestre di completamento flessibili, ti consigliamo di utilizzare le VM spot per ridurre i costi.

Ottimizzare per throughput ed efficienza dei costi

I workload di inferenza batch sono particolarmente adatti per un'infrastruttura a risparmio sui costi che potrebbe comportare interruzioni.

Utilizzare le VM spot per ridurre i costi di calcolo

Utilizza gli sconti delle VM spot per i job batch. Poiché i workload di inferenza batch in genere tollerano la latenza e le interruzioni, sono buoni candidati per i prezzi ridotti della capacità spot.

Assicurati che il codice di inferenza batch implementi il checkpointing per gestire i potenziali eventi di prerilascio. Se una VM spot viene sottoposta a prerilascio, puoi creare un nuovo nodo e riprendere il workload dall'ultimo batch elaborato anziché riavviare da zero.

Ottimizzare le dimensioni del batch del workload e le dimensioni del batch delle richieste

Per evitare la contesa delle risorse e i timeout dei job, assicurati che il numero di elementi inviati al motore (batch del workload) sia almeno pari alle richieste simultanee che il server può elaborare (batch delle richieste) per evitare di utilizzare in modo insufficiente gli acceleratori.

Ottimizzare le dimensioni del batch del workload

Le dimensioni del batch del workload sono il numero totale di elementi inviati al motore di inferenza in una singola unità di lavoro. Puoi configurarlo nella logica di invio del client o nella configurazione del job Kubernetes suddividendo i dati o raggruppando più elementi in una singola richiesta.

Per determinare le dimensioni ottimali del batch del workload, utilizza i seguenti limiti:

  • Calcola le dimensioni minime del batch: assicurati che le dimensioni del batch del workload siano almeno pari alle dimensioni del batch delle richieste. Ad esempio, l'invio di un elemento a un server che può elaborare 256 elementi contemporaneamente comporta un utilizzo insufficiente significativo. Per trovare le dimensioni minime, controlla la configurazione del server di inferenza, ad esempio l'argomento max_num_seqs in vLLM. Puoi configurare la logica del client per raggruppare più elementi in una singola richiesta oppure puoi suddividere i dati in modo che ogni job riceva una quantità minima di dati che soddisfi o superi le dimensioni del batch delle richieste.
  • Calcola le dimensioni massime del batch: assicurati che le dimensioni del batch del workload consentano al pod di terminare prima di raggiungere il activeDeadlineSeconds timeout definito nel job Kubernetes. Stima il tempo necessario per elaborare un batch di richieste e imposta le dimensioni del workload in modo che il pod venga completato entro la scadenza. Ad esempio, se activeDeadlineSeconds è 3600 secondi e il sovraccarico di avvio è di 600 secondi, assicurati che il tempo di esecuzione massimo consenta al pod di terminare entro 3000 secondi.

Se le dimensioni del batch del workload sono troppo piccole, il job sprecherà tempo per il sovraccarico di avvio del pod (download dei pesi, provisioning, inizializzazione dell'acceleratore); se è troppo grande, rischi che il job venga terminato da GKE a causa del activeDeadlineSeconds timeout, causando l'errore del job e la perdita dei progressi.

Ottimizzare le dimensioni del batch delle richieste

Le dimensioni del batch delle richieste sono il numero di richieste simultanee che il server di inferenza elabora contemporaneamente sull'acceleratore. Puoi ottimizzare questo parametro modificando i flag specifici del server nella configurazione del server di inferenza (ad esempio, il flag --max-num-seqs per vLLM).

L'obiettivo è massimizzare l'utilizzo della GPU senza attivare errori di esaurimento della memoria (OOM). Se le dimensioni del batch delle richieste non sono calibrate, il sistema utilizzerà in modo insufficiente l'acceleratore o arresterà il server del modello. Per vLLM, puoi utilizzare strumenti come lo script di vLLM auto_tune per trovare i valori migliori per le impostazioni max_num_seqs e max_num_batched_tokens per l'hardware specifico. Per ulteriori informazioni, consulta Ottimizzare la configurazione del server di inferenza nella guida Panoramica delle best practice di inferenza su GKE.

Implementare componenti asincroni per l'inferenza asincrona

Per l'inferenza asincrona, ti consigliamo di utilizzare i buffer di messaggi per separare il livello di importazione dal livello di inferenza.

Il seguente diagramma dell'architettura illustra un esempio di piattaforma di inferenza asincrona. Questa architettura protegge i server di inferenza dai picchi di traffico, gestisce i backlog di lavoro e garantisce un elevato utilizzo dell'acceleratore.

Il diagramma mostra il flusso da Pub/Sub ai sottoscrittori, un gateway di inferenza e un server di inferenza, con i risultati persistenti in AlloyDB e i messaggi non riusciti inviati a un argomento messaggi non recapitabili.

Piattaforma di inferenza asincrona su GKE.

L'architettura è costituita dai seguenti componenti:

  • Argomento Pub/Sub:funge da buffer persistente per i messaggi client in entrata, con un periodo di conservazione da 7 a 31 giorni.
  • Sottoscrittore:un componente che legge i batch di messaggi, invia richieste al server di inferenza e conferma l'elaborazione.
  • HPA del sottoscrittore:scala il deployment del sottoscrittore in base alla metrica num_undelivered_messages (il numero di messaggi non confermati).
  • Archiviazione:rendi persistenti i risultati dell'inferenza utilizzando un database (ad esempio AlloyDB) o l'archiviazione di oggetti (ad esempio Cloud Storage) .
  • Gateway di inferenza:espone i workload di inferenza al sottoscrittore.
  • Server di inferenza:elabora le richieste di inferenza batch (ad esempio, vLLM).
  • HPA del server:scala il motore di inferenza in base a metriche specifiche del motore come vllm:num_requests_waiting.
  • Argomento messaggi non recapitabili:acquisisce i messaggi che non vengono elaborati dopo un numero impostato di tentativi con backoff esponenziale.

Per ulteriori informazioni, consulta l'implementazione di riferimento su GitHub.

Buffer e aggregazione delle richieste

Per gestire il flusso di richieste:

  • Utilizza Pub/Sub come buffer durevole:implementa Pub/Sub per archiviare le richieste di inferenza in modo durevole. Questa configurazione funge da buffer FIFO che contiene le richieste finché un consumer non ha la capacità di elaborarle, impedendo il sovraccarico del server durante il traffico burst.
  • Utilizza le sottoscrizioni pull con il controllo del flusso lato client: configura un modello di sottoscrizione pull. In questo modo, l'applicazione del sottoscrittore può richiedere esplicitamente i messaggi solo quando ha la capacità di elaborarli, garantendoti il controllo completo della frequenza di consumo.
  • Aggrega i messaggi per riempire le dimensioni del batch del server:evita di inviare un messaggio Pub/Sub come una richiesta di inferenza. Il sottoscrittore deve invece raggruppare più messaggi in una singola richiesta batch che si allinei alle dimensioni ottimali del batch del server di inferenza (ad esempio, corrispondenti alle impostazioni max_num_seqs in vLLM). Questo approccio contribuisce a garantire che gli acceleratori siano completamente saturi e massimizza il throughput. In particolare, configura l'impostazione pull max_messages del sottoscrittore su un multiplo di max_num_seqs per assicurarti che ogni passaggio di inoltro del modello sia completamente saturo.

Scalare automaticamente sottoscrittori e server

L'inferenza batch efficace richiede la scalabilità dei sottoscrittori (vincolati alla CPU) in modo diverso dai server di inferenza (vincolati alla GPU o alla TPU).

  • Scala i sottoscrittori in base al backlog di lavoro:configura HorizontalPodAutoscaler (HPA) per il deployment del sottoscrittore in base alla metrica num_undelivered_messages di Pub/Sub. Per ulteriori informazioni, consulta l'esempio di HPA di Pub/Sub. Calcola le repliche che vuoi utilizzare utilizzando la seguente equazione:

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

  • Rispetta le quote dell'infrastruttura:limita esplicitamente il numero massimo di repliche dei sottoscrittori configurando l'impostazione maxReplicas nell'HPA. Non scalare i sottoscrittori oltre la quota di GPU o TPU dei server di inferenza. Il provisioning eccessivo dei sottoscrittori sposterà il collo di bottiglia sul server di inferenza, aumentando la contesa delle risorse senza aumentare il throughput.

  • Scala i server di inferenza in base alle metriche del motore:scala il deployment del server di inferenza in base alle metriche esportate direttamente dal motore di inferenza (non solo tramite CPU/memoria). Ad esempio, utilizza l'impostazione vllm:num_requests_waiting per vLLM, che misura direttamente il backlog di elaborazione a livello del server del modello. Per ulteriori informazioni, consulta Scalare automaticamente i pod.

Gestire errori e timeout

Per gestire errori e timeout:

  • Estendi in modo proattivo le scadenze di conferma:configura il sottoscrittore in modo che estenda in modo proattivo la scadenza di conferma (ack) di Pub/Sub per i messaggi in fase di elaborazione per evitare loop di riconsegna ed elaborazione duplicata. Questo approccio è necessario perché le attività di inferenza spesso richiedono più tempo delle finestre di timeout predefinite. Come regola generale, imposta il periodo di estensione in modo che sia più lungo del tempo di inferenza batch nel caso peggiore.
  • Isola gli errori con un argomento messaggi non recapitabili:abilita un argomento messaggi non recapitabili per isolare automaticamente i messaggi non validi che non vengono consegnati ripetutamente. Questo approccio impedisce ai messaggi "poison pill" di bloccare la coda e interrompere l'intera pipeline.
  • Implementa strategie di backoff:se il server di inferenza restituisce errori 429 (Troppe richieste) o 503 (Servizio non disponibile), il sottoscrittore deve rilevarli e implementare una strategia di backoff esponenziale, mettendo in pausa temporaneamente il consumo da Pub/Sub fino al ripristino del server.

Orchestrare i job batch su larga scala

Segui queste best practice per massimizzare il throughput, garantire l'efficienza dei costi, implementare una tracciabilità completa per l'audit e applicare la gestione avanzata delle quote e la definizione delle priorità dei job durante l'elaborazione di set di dati di grandi dimensioni.

Utilizzare JobSet per l'inferenza distribuita su più nodi

Ti consigliamo di utilizzare la risorsa JobSet di Kubernetes per orchestrare i workload di inferenza distribuiti che richiedono la collaborazione di più nodi, ad esempio modelli di grandi dimensioni in esecuzione su pod TPU o cluster GPU multi-nodo. I job Kubernetes standard non possono garantire che tutti i pod richiesti vengano avviati contemporaneamente, il che può portare a deadlock nei workload distribuiti.

JobSet è un'API nativa di Kubernetes che gestisce i gruppi di job come un'unità e offre i seguenti vantaggi per l'inferenza batch:

  • Pianificazione di gruppo:consente di garantire che tutte le risorse richieste, come le slice TPU o i nodi GPU, siano disponibili prima di avviare il workload per evitare deadlock.
  • Posizionamento esclusivo:consente di garantire che un singolo JobSet abbia accesso esclusivo alla topologia di rete (ad esempio, una slice TPU) per massimizzare le prestazioni di interconnessione.
  • Ripristino in caso di errore:consente di riavviare job replicati specifici o l'intero set se un worker non funziona, a seconda della configurazione.

Utilizzare i job indicizzati per la suddivisione dei dati

Quando utilizzi JobSet, configura ReplicatedJob in modo che utilizzi l'impostazione completionMode: Indexed. Questa impostazione inserisce automaticamente una variabile di ambiente JOB_COMPLETION_INDEX in ogni pod. Il codice di inferenza può utilizzare questo indice per selezionare in modo deterministico una slice di dati univoca da elaborare.

Ad esempio, se hai un bucket Cloud Storage con 100.000 immagini ed esegui il deployment di un JobSet con un parallelismo di 10, ognuno dei 10 pod legge il proprio indice (0-9) all'avvio. Il pod 0 può quindi calcolare che deve elaborare le immagini 0-9999, mentre il pod 1 elabora 10.000-19.999. Questo approccio riduce la necessità di un servizio di coda di attività separato.

Utilizzare il pattern sidecar per la saturazione del server

Per massimizzare l'utilizzo dell'acceleratore, configura i pod JobSet con due container utilizzando il pattern sidecar:

  • Server di inferenza:un server ottimizzato (ad esempio vLLM) che si concentra interamente sul calcolo della GPU o della TPU.
  • Driver client:un container logico che invia in modo asincrono un volume elevato di richieste al server su localhost.

Questo disaccoppiamento contribuisce a garantire che la GPU o la TPU rimanga occupata e non sia mai inattiva durante l'attesa di I/O di rete o della pre-elaborazione dei dati. Senza questo approccio, i modelli che caricano i dati in sequenza possono far sì che l'acceleratore attenda il completamento delle operazioni di I/O, con conseguente utilizzo insufficiente. Ad esempio, anziché attendere l'elaborazione dei dati, il driver client può recuperare in anticipo i dati e inviare continuamente richieste asincrone al server di inferenza, il che contribuisce a garantire che la coda delle richieste dell'acceleratore rimanga satura.

Elenco di controllo di riepilogo

Categoria Best practice
Pattern architetturali
Costo e throughput
Messaggistica e scalabilità
Orchestrazione