Práticas recomendadas para inferência em lote no GKE

Neste documento, você vai conhecer as práticas recomendadas para executar cargas de trabalho de inferência em lote no Google Kubernetes Engine (GKE). A inferência em lote é o processo de usar um modelo de machine learning para gerar previsões em grandes conjuntos de dados, priorizando alta capacidade de processamento e eficiência de custos em vez de respostas imediatas e de baixa latência.

Este guia distingue a inferência em lote do agrupamento de solicitações (ou agrupamento dinâmico), uma técnica do lado do servidor em mecanismos como vLLM ou SGLang que agrupa solicitações simultâneas em tempo real para otimizar a eficiência do acelerador. É possível aplicar o agrupamento de solicitações a cargas de trabalho de inferência em lote.

As práticas recomendadas neste guia abrangem dois tipos comuns de padrões de inferência em lote:

  • Inferência em lote quase em tempo real: processa dados em blocos logo após a geração. Com uma latência típica de segundos a minutos, essa abordagem equilibra a necessidade de dados atualizados com a eficiência do processamento simultâneo de vários itens.
  • Inferência em lote off-line: processa grandes volumes de dados acumulados em intervalos programados (por exemplo, noturnos ou semanais). A latência normalmente varia de horas a dias, já que esses jobs costumam ser programados durante horários de menor movimento para maximizar a disponibilidade de recursos.

Essas recomendações são uma camada especializada de otimização criada com base nos fundamentos descritos na Visão geral das práticas recomendadas de inferência no GKE. Antes de otimizar para cargas de trabalho em lote, siga as principais práticas recomendadas para seleção de modelos, quantização e escolha de aceleradores.

Escolher um padrão arquitetônico para o processamento de inferência em lote

Selecionar o padrão arquitetônico correto é a decisão mais importante para implantar cargas de trabalho de inferência em lote, porque afeta as compensações entre latência, capacidade de processamento e custo. Para manter a eficiência, verifique se a taxa de transferência de inferência excede a taxa de consultas recebidas durante os horários de menor movimento para evitar que as filas cresçam indefinidamente.

Usar a inferência em lote quase em tempo real para trabalho intermitente

O agrupamento quase em tempo real funciona bem para casos de uso que exigem atualizações incrementais e frequentes, como:

  • Atualizar os perfis de recomendação de usuários a cada poucos minutos com base nas interações recentes.
  • Processamento de menções em redes sociais em intervalos de um minuto para monitoramento em tempo real.
  • Detectar sinais que movem o mercado em fluxos de dados financeiros de alta frequência.
  • Realizar análises de sentimento em feedbacks de clientes ou feeds de notícias recebidos.

Escolha esse padrão se a carga de trabalho puder tolerar latência que varia de vários segundos a alguns minutos.

Ao implementar a inferência em lote quase em tempo real, considere as seguintes características:

  • Latência: o tempo até o primeiro token varia de dezenas de segundos a minutos.
  • Fontes de dados: normalmente, você processa conjuntos de dados que variam de megabytes a gigabytes, como mensagens do Pub/Sub ou arquivos do Cloud Storage acumulados em um curto período.
  • Padrão de computação: sua infraestrutura precisa oferecer suporte a um serviço contínuo que lide com picos frequentes de trabalho.
  • Otimização de custos: esse padrão oferece um equilíbrio entre inferência em tempo real de baixa latência e processamento off-line de alta capacidade.

Usar a inferência em lote off-line para conjuntos de dados enormes

O processamento em lote off-line é ideal para jobs episódicos em grande escala que podem tolerar atrasos de horas ou dias, como:

  • Gerar relatórios noturnos de avaliação de risco com base nas transações financeiras do dia anterior.
  • Criar incorporações de produtos para um catálogo inteiro e potencializar sistemas de pesquisa e recomendação downstream.
  • Rotular grandes conjuntos de dados de imagens para treinamento de modelo ou categorização de arquivos.

Escolha esse padrão se você estiver processando grandes volumes de dados e puder tolerar latências que variam de horas a vários dias.

Ao implementar a inferência em lote off-line, considere as seguintes características:

  • Latência: a latência de início da carga de trabalho geralmente varia de minutos a dias, porque os jobs costumam ser programados durante horários de menor movimento.
  • Fontes de dados: você processa grandes conjuntos de dados de gigabytes a petabytes, geralmente armazenados em tabelas do Cloud Storage ou do BigQuery.
  • Padrão de computação: você usa jobs episódicos e intermitentes que inicializam, processam os dados e depois são encerrados.
  • Otimização de custos: esse padrão é altamente otimizável com um modelo de pagamento conforme o uso. Como os jobs off-line têm janelas de conclusão flexíveis, recomendamos usar VMs spot para reduzir custos.

Otimizar a capacidade e a economia

As cargas de trabalho de inferência em lote são adequadas para infraestruturas econômicas que podem envolver interrupções.

Usar VMs spot para reduzir os custos de computação

Use os descontos das VMs spot para jobs em lote. Como as cargas de trabalho de inferência em lote geralmente são tolerantes a latência e interrupções, elas são boas candidatas aos preços reduzidos da capacidade spot.

Verifique se o código de inferência em lote implementa checkpointing para processar possíveis eventos de substituição. Se uma VM spot for interrompida, crie um novo nó e retome a carga de trabalho do último lote processado em vez de reiniciar do zero.

Ajuste o tamanho do lote de carga de trabalho e de solicitação

Para evitar disputas de recursos e tempos limite de jobs, verifique se o número de itens enviados ao seu mecanismo (lote de carga de trabalho) é pelo menos tão grande quanto as solicitações simultâneas que o servidor pode processar (lote de solicitações) para evitar a subutilização de aceleradores.

Ajustar o tamanho do lote da carga de trabalho

O tamanho do lote de carga de trabalho é o número total de itens enviados ao seu mecanismo de inferência em uma única unidade de trabalho. Configure isso na lógica de envio do cliente ou na configuração do job do Kubernetes ao fragmentar os dados ou agrupar vários itens em uma única solicitação.

Para determinar o tamanho ideal do lote de carga de trabalho, use os seguintes limites:

  • Calcule o tamanho mínimo do lote: verifique se o tamanho do lote da sua carga de trabalho é pelo menos tão grande quanto o tamanho do lote de solicitações. Por exemplo, enviar um item para um servidor que pode processar 256 itens simultaneamente resulta em subutilização significativa. Para encontrar o tamanho mínimo, verifique a configuração do servidor de inferência, como o argumento max_num_seqs no vLLM. É possível configurar a lógica do cliente para agrupar vários itens em uma única solicitação ou fragmentar os dados para que cada job receba uma quantidade mínima de dados que atenda ou exceda o tamanho do lote de solicitações.
  • Calcule o tamanho máximo do lote: verifique se o tamanho do lote da sua carga de trabalho permite que o pod termine antes de atingir o tempo limite de activeDeadlineSeconds definido no job do Kubernetes. Estime o tempo necessário para processar um lote de solicitações e defina o tamanho da carga de trabalho para que o pod seja concluído dentro do prazo. Por exemplo, se o activeDeadlineSeconds for de 3.600 segundos e a sobrecarga de inicialização for de 600 segundos, verifique se o tempo máximo de execução permite que o pod termine em menos de 3.000 segundos.

Se o tamanho do lote da carga de trabalho for muito pequeno, o job vai perder tempo com a sobrecarga de inicialização do pod (download de pesos, provisionamento, inicialização do acelerador). Se for muito grande, o job poderá ser encerrado pelo GKE devido ao tempo limite de activeDeadlineSeconds, causando falha e perda de progresso.

Ajustar o tamanho do lote de solicitações

O tamanho do lote de solicitações é o número de solicitações simultâneas que o servidor de inferência processa simultaneamente no acelerador. Para otimizar esse parâmetro, ajuste flags específicas do servidor na configuração do servidor de inferência (por exemplo, a flag --max-num-seqs para vLLM).

Seu objetivo é maximizar a utilização da GPU sem acionar erros de memória insuficiente (OOM). Se o tamanho do lote de solicitações não estiver calibrado, o sistema vai subutilizar o acelerador ou falhar no servidor de modelo. Para vLLM, você pode usar ferramentas como o script vLLM auto_tune para encontrar os melhores valores para as configurações max_num_seqs e max_num_batched_tokens do seu hardware específico. Para mais informações, consulte Otimizar a configuração do servidor de inferência no guia Visão geral das práticas recomendadas de inferência no GKE.

Implementar componentes assíncronos para agrupamento em lote quase em tempo real

Para o agrupamento em lote quase em tempo real, recomendamos o uso de buffers de mensagens para desacoplar a camada de ingestão da camada de inferência.

O diagrama de arquitetura a seguir ilustra um exemplo de plataforma de inferência em lote quase em tempo real. Essa arquitetura protege os servidores de inferência contra picos de tráfego, gerencia os backlogs de trabalho e garante alta utilização do acelerador.

O diagrama mostra o fluxo do Pub/Sub para assinantes, um gateway de inferência e um servidor de inferência, com resultados persistidos no AlloyDB e mensagens com falha enviadas para um tópico de dead-letter.

Plataforma de inferência em lote quase em tempo real no GKE.

A arquitetura consiste nos seguintes componentes:

  • Tópico do Pub/Sub:atua como um buffer persistente para mensagens de clientes recebidas, com um período de armazenamento de 7 a 31 dias.
  • Assinante:um componente que lê lotes de mensagens, envia solicitações ao servidor de inferência e confirma o processamento.
  • HPA do assinante:escalona a implantação do assinante com base na métrica num_undelivered_messages (o número de mensagens não confirmadas).
  • Armazenamento:persista os resultados da inferência usando um banco de dados (como o AlloyDB) ou armazenamento de objetos (como o Cloud Storage) .
  • Gateway de inferência:expõe as cargas de trabalho de inferência ao assinante.
  • Servidor de inferência:processa as solicitações de inferência em lote (por exemplo, vLLM).
  • HPA do servidor:escalona o mecanismo de inferência com base em métricas específicas do mecanismo, como vllm:num_requests_waiting.
  • Tópico de mensagens inativas:captura mensagens que falham no processamento após um número definido de novas tentativas de espera exponencial.

Para mais informações, consulte a implementação de referência no GitHub.

Armazenar em buffer e agregar solicitações

Para gerenciar o fluxo de solicitações, faça o seguinte:

  • Use o Pub/Sub como um buffer durável:implemente o Pub/Sub para armazenar solicitações de inferência de forma durável. Essa configuração funciona como um buffer FIFO que retém solicitações até que um consumidor tenha capacidade para processá-las, evitando a sobrecarga do servidor durante o tráfego intermitente.
  • Use assinaturas de extração com controle de fluxo do lado do cliente:configure um modelo de assinatura Pull. Isso permite que o aplicativo assinante solicite mensagens explicitamente apenas quando tiver capacidade para processá-las, concedendo controle total sobre a taxa de consumo.
  • Agregue mensagens para preencher o tamanho do lote do servidor:evite enviar uma mensagem do Pub/Sub como uma solicitação de inferência. Em vez disso, o assinante precisa agrupar várias mensagens em uma única solicitação em lote que se alinhe ao tamanho ideal do lote do servidor de inferência (por exemplo, correspondendo às configurações max_num_seqs no vLLM). Essa abordagem ajuda a garantir que os aceleradores estejam totalmente saturados e maximiza a taxa de transferência. Especificamente, configure a configuração de extração max_messages do assinante para um múltiplo de max_num_seqs para garantir que cada transmissão direta do modelo esteja totalmente saturada.

Escalonamento automático de assinantes e servidores

Para uma inferência em lote eficaz, é necessário escalonar os assinantes (vinculados à CPU) de maneira diferente dos servidores de inferência (vinculados à GPU ou TPU).

  • Escalonar assinantes com base no backlog de trabalho:configure o HorizontalPodAutoscaler (HPA) para a implantação de assinantes com base na métrica num_undelivered_messages do Pub/Sub. Para mais informações, consulte Otimizar o escalonamento automático de pods com base em métricas. Calcule as réplicas que você quer usar com a seguinte equação:

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

  • Respeite as cotas de infraestrutura:limite explicitamente o número máximo de réplicas dos assinantes configurando a definição maxReplicas no HPA. Não dimensione assinantes além do que a cota de GPU ou TPU dos servidores de inferência pode oferecer suporte. O provisionamento excessivo de assinantes vai mudar o gargalo para o servidor de inferência, aumentando a disputa de recursos sem aumentar a capacidade de transferência.

  • Escalonar servidores de inferência com base em métricas do mecanismo:escalone a implantação do servidor de inferência com base em métricas exportadas diretamente pelo mecanismo de inferência (não apenas por CPU/memória). Por exemplo, use a configuração vllm:num_requests_waiting para vLLM, que mede diretamente o backlog de processamento no nível do servidor de modelo. Para mais informações, consulte Escalonar automaticamente seus pods.

Tratar erros e tempos limite

Para processar erros e tempos limite, faça o seguinte:

  • Estenda proativamente os prazos de confirmação:configure seu assinante para estender proativamente o prazo de confirmação do Pub/Sub para mensagens em processamento e evitar loops de reenvio e processamento duplicado. Essa abordagem é necessária porque as tarefas de inferência geralmente levam mais tempo do que as janelas de tempo limite padrão. Como regra geral, defina o período de extensão para ser maior do que o tempo de inferência em lote no pior caso.
  • Isolar falhas com um tópico de mensagens inativas:ative um tópico de mensagens inativas para isolar automaticamente mensagens malformadas que falham na entrega repetidamente. Essa abordagem impede que mensagens de "pílula venenosa" bloqueiem a fila e interrompam todo o pipeline.
  • Implemente estratégias de espera:se o servidor de inferência retornar erros 429 (muitas solicitações) ou 503 (serviço indisponível), o assinante precisará capturar esses erros e implementar uma estratégia de espera exponencial, pausando temporariamente o consumo do Pub/Sub até que o servidor seja recuperado.

Orquestrar jobs em lote off-line em grande escala

Siga estas práticas recomendadas para maximizar a capacidade de processamento, garantir a eficiência de custos, implementar a rastreabilidade abrangente para auditoria e aplicar o gerenciamento avançado de cotas e a priorização de jobs ao processar conjuntos de dados grandes.

Usar o JobSet para inferência distribuída em vários nós

Recomendamos usar o recurso JobSet do Kubernetes para orquestrar cargas de trabalho de inferência distribuídas que exigem a cooperação de vários nós, como modelos grandes executados em pods de TPU ou clusters de GPU de vários nós. Os jobs padrão do Kubernetes não podem garantir que todos os pods necessários sejam iniciados simultaneamente, o que pode levar a impasses em cargas de trabalho distribuídas.

O JobSet é uma API nativa do Kubernetes que gerencia grupos de jobs como uma unidade e oferece os seguintes benefícios para a inferência em lote:

  • Programação em grupo:ajuda a garantir que todos os recursos necessários, como frações de TPU ou nós de GPU, estejam disponíveis antes de iniciar a carga de trabalho para evitar impasses.
  • Posicionamento exclusivo:ajuda a garantir que um único JobSet tenha acesso exclusivo à topologia de rede (por exemplo, uma fração de TPU) para maximizar o desempenho da interconexão.
  • Recuperação de falhas:permite reiniciar jobs replicados específicos ou o conjunto inteiro se um worker falhar, dependendo da sua configuração.

Usar jobs indexados para fragmentação de dados

Ao usar JobSet, configure o ReplicatedJob para usar a configuração completionMode: Indexed. Essa configuração injeta automaticamente uma variável de ambiente JOB_COMPLETION_INDEX em cada pod. Seu código de inferência pode usar esse índice para selecionar de forma determinista um fragmento exclusivo de dados para processar.

Por exemplo, se você tiver um bucket do Cloud Storage com 100.000 imagens e implantar um JobSet com paralelismo de 10, cada um dos 10 pods vai ler o índice (0 a 9) na inicialização. O pod 0 pode calcular que precisa processar as imagens de 0 a 9.999, enquanto o pod 1 processa de 10.000 a 19.999. Essa abordagem reduz a necessidade de um serviço de fila de tarefas separado.

Usar o padrão sidecar para saturação do servidor

Para maximizar a utilização do acelerador, configure os pods do JobSet com dois contêineres usando o padrão sidecar:

  • Servidor de inferência:um servidor otimizado (como o vLLM) que se concentra totalmente na computação de GPU ou TPU.
  • Driver do cliente:um contêiner lógico que envia de forma assíncrona um grande volume de solicitações ao servidor em localhost.

Esse desacoplamento garante que a GPU ou TPU permaneça ocupada e nunca fique inativa enquanto aguarda E/S de rede ou pré-processamento de dados. Sem essa abordagem, os modelos que carregam dados sequencialmente podem fazer com que o acelerador espere a conclusão das operações de E/S, levando ao subaproveitamento. Por exemplo, em vez de esperar que os dados sejam processados, o driver do cliente pode pré-buscar dados e enviar continuamente solicitações assíncronas ao servidor de inferência, garantindo que a fila de solicitações do acelerador permaneça saturada.

Lista de verificação resumida

Categoria Prática recomendada
Padrões de arquitetura
Custo e capacidade
Mensagens e escalonamento
Orquestração