Treino e inferência na mesma GPU: quatro formas de partilhar 10 mil aceleradores
Depois de 16 anos operando plataformas financeiras sobre AWS, a pergunta que mais me fizeram sobre GPU nos últimos dois anos não foi "qual instância?", foi "por que a fatura cresce e o nvidia-smi mostra 30%?". O caso do China Merchants Bank, vencedor do CNCF End User Case Study Contest anunciado em 8 de setembro de 2026, responde com número: 99% de quase 10 mil aceleradores heterogêneos sob um único plano de controle Kubernetes, utilização média de 35% para mais de 60%, custo por milhão de tokens cortado em mais de 60%. Nenhum desses ganhos veio de um chip novo. Vieram de fila, quota, preempção e cache, decisões de arquitetura que qualquer time com EKS consegue tomar, e que quatro desenhos diferentes tomam de jeitos bem distintos.
A escolha real: quem decide onde a próxima GPU vai
O erro comum é enquadrar o problema como "treino versus inferência". Não é uma disputa entre workloads, é uma disputa entre dois regimes de demanda que nunca se alinham no tempo. Treino é batch: chega em lote, quer 64 cartões por dias, tolera esperar na fila e tolera ser interrompido se houver checkpoint. Inferência é online: chega em curva de tráfego, quer latência p99 previsível, não tolera esperar e não tolera preempção. Quando cada regime ganha o próprio pool, cada pool é dimensionado para o próprio pico, e a soma dos dois picos é a fatura. Os 35% do CMB antes da mudança são exatamente essa soma: metade da capacidade parada esperando o pico do vizinho.
A decisão de arquitetura, portanto, é quem arbitra a fronteira entre os dois regimes: e com que granularidade. Há quatro respostas honestas:
- Ninguém arbitra: pools separados, fronteira fixa no provisionamento.
- Um scheduler aberto arbitra no cluster: Kueue para admissão com quota, KEDA para escalar inferência por métrica, HAMi para fatiar o cartão, Fluid para o dado chegar antes da GPU ficar ociosa. É o desenho do CMB.
- A AWS arbitra com o mesmo scheduler, embalado: SageMaker HyperPod com orquestração EKS e task governance, que instala Kueue e expõe quota, fair-share e empréstimo como política de console.
- Serviço gerenciado arbitra fora do cluster: inferência no Bedrock ou em endpoints SageMaker, treino em jobs SageMaker, sem frota própria para partilhar.
O resto do artigo compara as quatro. A resposta não é a mesma para um banco com 10 mil cartões e para uma fintech com 40.
O que o CMB montou, peça por peça
O anúncio nomeia cinco projetos e atribui um papel a cada um. Vale traduzir cada papel para o parâmetro que ele controla, porque é aí que a utilização é ganha ou perdida.
Kueue, admissão com quota: cada time submete a uma LocalQueue no próprio namespace; a LocalQueue aponta para uma ClusterQueue com nominalQuota por ResourceFlavor (um flavor por tipo de cartão, é assim que 10 mil aceleradores heterogêneos viram uma fila só). ClusterQueues no mesmo cohort emprestam quota ociosa entre si até o borrowingLimit, e spec.preemption.reclaimWithinCohort: Any devolve o emprestado quando o dono precisa. O ganho principal é o que o anúncio chama de "não reservar capacidade prematuramente": o job só cria pods quando toda a quota está garantida, então 64 cartões não ficam meio alocados esperando os outros 32.
KEDA, inferência escala por sinal de demanda: um ScaledObject com o scaler prometheus (serverAddress, query, threshold, activationThreshold) escala réplicas de servidor de modelo por profundidade de fila ou tokens por segundo, não por CPU. Quando o tráfego cai, as réplicas somem e a quota volta ao cohort para o treino.
HAMi, o cartão vira recurso fracionário: projeto CNCF Incubating que expõe nvidia.com/gpumem (em MB) e nvidia.com/gpucores além de nvidia.com/gpu, com suporte a NVIDIA, Ascend, Cambricon e outros. É o que deixa cinco tenants de LoRA dividirem uma instância do modelo base, o Twinkle, framework interno do banco, faz isso por padrão e reporta 80% menos aceleradores para o mesmo trabalho.
Fluid, dado antes da GPU: projeto CNCF Incubating com Dataset + runtime de cache (Alluxio, JuiceFS) e warm-up. Checkpoint e modelo já estão no nó quando o pod sobe. GPU esperando o object storage é utilização zero que aparece como 100% "alocado".
Prometheus fecha o ciclo: é a fonte de verdade tanto para o KEDA quanto para o painel que provou os 60%.
Plano de controle unificado: quem decide onde a próxima GPU vai
Os três regimes entram por filas diferentes, mas disputam a mesma quota; a métrica fecha o ciclo e o cache tira a espera por dados do caminho crítico.
- Kueue · ClusterQueue + cohort + preempção
- KEDA · ScaledObject (prometheus)
- HAMi · gpumem / gpucores
- Prometheus · utilização, fila, tokens/s
- p5 / p6 · ResourceFlavor: treino
- g6e / g7e · ResourceFlavor: inferência, MIG
- Fluid · Dataset + warm-up
- S3 · modelos, checkpoints
Quatro formas de partilhar a frota
| Quem arbitra | Granularidade | Isolamento | Custo de manter | Utilização típica | |
|---|---|---|---|---|---|
| A. Pools separados | Ninguém, fronteira fixa no provisionamento | Node group inteiro | Total, por construção | Baixo em software, alto em capacidade parada | 30-40% (soma de dois picos) |
| B. EKS + Kueue/KEDA/HAMi/Fluid | Seu time de plataforma, com scheduler aberto | Fração de cartão (gpumem/gpucores) ou MIG | Software (HAMi) ou hardware (MIG), você escolhe por flavor | Alto: 5 projetos, versões, CRDs, upgrades de driver | 60%+ (o número do CMB) |
| C. SageMaker HyperPod + task governance | AWS, com Kueue embalado como política | Instância, acelerador ou partição MIG por time | Namespace por time + MIG; sem fração por software | Médio: add-on gerenciado, política em console/CLI | 50-60% se o fair-share for bem calibrado |
| D. Bedrock + jobs SageMaker | AWS, fora do seu cluster | Token / job-hora | Total, por serviço | Mínimo em software; o preço já embute a ociosidade de outro | Não é sua métrica, você paga por uso |
Isolamento: o custo escondido da utilização
Todo ponto percentual de utilização acima de 50% é comprado com isolamento a menos. Num banco, isso não é detalhe, é o que o auditor pergunta primeiro.
Há três mecanismos para colocar dois pods no mesmo cartão, e eles não são equivalentes:
Time-slicing: o device plugin ou o driver DRA anuncia N slots por GPU e o scheduler CUDA reveza. Funciona em toda instância NVIDIA da AWS, não exige hardware, e a documentação da EKS é explícita: não oferece isolamento de memória nem de compute entre os pods. Um tenant com leak de memória derruba o vizinho com CUDA out of memory. Serve para dev e para inferência esparsa; não serve para dois tenants de negócio distintos.
MIG: partição em hardware, até 7 instâncias por A100/H100/H200/B200 (menos nos g7/g7e Blackwell), cada uma com memória, compute e largura de banda dedicados. É o único dos três com isolamento de falha em hardware, o que PCI-DSS ou uma trilha de BACEN 4.893 aceita sem discussão. O preço: a partição é estática por nó (nvidia.com/mig.config.state precisa estar success) e só existe na família P e nos Blackwell G.
HAMi: virtualização em software com limite de memória e de cores por pod, imposta onde o backend do dispositivo permite. Mais fino que MIG (qualquer fração, não só 7 perfis), mais seguro que time-slicing (o limite existe), e o único dos três que fala com aceleradores não NVIDIA, relevante para o CMB com frota heterogênea, menos para quem está 100% em EC2. Isolamento de falha continua sendo de software.
A regra que uso: MIG para inferência multi-tenant com SLO, HAMi ou time-slicing para fine-tuning LoRA e experimentação, cartão inteiro para pré-treino. A opção B deixa você escolher por ResourceFlavor; a opção C só oferece MIG ou cartão inteiro. Se o seu caso é "cinco tenants de LoRA numa base", isso decide.
Matriz de decisão
A. Pools separados
- Isolamento total sem configurar nada
- Blast radius de um upgrade de driver fica em um pool
- Chargeback trivial: o node group é o centro de custo
- Paga dois picos; 35% é o resultado esperado, não um acidente
- Treino espera fila enquanto a inferência tem GPU parada de madrugada
Só até ~40 cartões ou enquanto não houver time de plataforma
B. EKS + Kueue/KEDA/HAMi/Fluid
- Granularidade máxima: fração, MIG ou cartão inteiro por flavor
- Funciona com acelerador heterogêneo, inclusive fora da AWS
- 60%+ de utilização provados em 10 mil cartões
- Cinco projetos para versionar; DRA só recomendado a partir do K8s 1.34 e não roda em EKS Auto Mode
- Preempção mal calibrada mata treino sem checkpoint
- Isolamento por software exige justificativa em auditoria
Acima de ~500 cartões com time de plataforma dedicado de 4+ pessoas
C. HyperPod + task governance
- Kueue, quota, fair-share (peso 0-100) e empréstimo (até 10.000% da quota) como política, não como YAML
IdleResourceSharingreconcilia capacidade não alocada sozinho- Observabilidade de fila e espera por time já vem no painel
- Só tipos de instância suportados pelo HyperPod; nada de frota mista fora da AWS
- Fração de GPU só via MIG, sem equivalente ao HAMi
- Inferência elástica por métrica ainda é problema seu (KEDA continua necessário)
De 50 a 2.000 cartões, 100% AWS, time de plataforma pequeno
D. Bedrock + jobs SageMaker
- Zero GPU parada na sua conta
- Guardrails, trilha e isolamento vêm do serviço
- Sem controle de placement, cache ou fatiamento, o preço por token já embute a ociosidade de outro
- Modelo próprio com regra de residência de dados pode não caber
Padrão para quem não tem modelo próprio nem frota
Preempção e frescor: os dois modos de falha que ninguém desenha
Os diagramas de plataforma unificada mostram o caminho feliz. Os dois incidentes que eu já vi acontecer ficam fora do desenho.
Preempção sem checkpoint. Quando a inferência escala às 9h e o cohort reclama a quota emprestada, o Kueue evicta o job de treino que estava usando GPU alheia. Se o job não grava checkpoint a cada N passos, ele perde tudo desde o início, e no dia seguinte alguém vai pedir para "desligar essa preempção", o que devolve a plataforma aos 35%. A configuração que evita isso não é no Kueue; é no job: checkpoint em intervalo curto o bastante para que o retrabalho médio fique abaixo de 15 minutos, gravado no cache do Fluid com write-back para S3. No Kueue, withinClusterQueue: LowerPriority protege o treino de ser interrompido por outro treino do mesmo time; reclaimWithinCohort: Any diz que quota emprestada pode voltar a qualquer momento. As duas linhas juntas são o contrato.
Escala que oscila. KEDA com threshold justo e cooldownPeriod curto produz flapping: réplicas sobem, o treino é preemptado, o tráfego cai, réplicas descem, o treino reinicia do checkpoint, o tráfego sobe. Cada ciclo custa um cold start de modelo (dezenas de segundos para pesos de 30 GB sem cache) e um retrabalho de treino. Use activationThreshold acima de zero para não acordar réplica com ruído, e cooldownPeriod da ordem de minutos, não segundos.
Dado frio. Sem Fluid ou equivalente, o pod de inferência nasce, pede 30 GB de pesos ao S3 e fica dois minutos alocado com 0% de uso. No painel isso conta como "GPU em uso". É a diferença entre utilização alocada e utilização real, meça DCGM_FI_DEV_GPU_UTIL, não o requests do pod.
A aritmética que justifica o projeto
De 35% para 60% na mesma frota é 1,7x de trabalho pelo mesmo custo fixo, ou, em 10 mil cartões, o equivalente a 2.500 aceleradores que você não precisa comprar. Numa frota de 200 cartões p5 o mesmo salto vale 50 cartões. Antes de escolher entre B e C, calcule esse número na sua escala: se ele for menor que o custo anual de um engenheiro de plataforma, a resposta é C ou D, não B.
Anti-padrões que devolvem a frota aos 35%
- Time-slicing para tenants de negócio distintos: sem isolamento de memória, um leak num tenant vira
CUDA out of memoryno outro, e a trilha de auditoria não explica. - Quota igual ao tamanho da frota: se a soma dos
nominalQuotabate no total, ninguém tem o que emprestar e o cohort vira decoração. - Preempção ligada, checkpoint desligado: o primeiro treino perdido gera o pedido de desligar a preempção, e a plataforma volta ao ponto de partida.
- Medir utilização por
requests: GPU alocada esperando pesos do S3 aparece como 100% e esconde exatamente o problema que o Fluid resolve.
Se eu tivesse que montar isso amanhã num banco 100% AWS, começaria pela opção C: HyperPod com task governance me dá Kueue, fair-share e empréstimo sem eu manter os CRDs, e o painel de espera por time é o argumento que convence o CFO de que a fila existe. Só migraria para a opção B no dia em que precisasse de fração por software para LoRA multi-tenant ou de acelerador fora da família P/G, e nesse dia contrataria antes de instalar. A lição dura por trás disso: o custo do plano de controle unificado não é o de subir cinco Helm charts, é o de manter cinco ciclos de release, um driver NVIDIA que muda a cada AMI e uma política de preempção que precisa ser reexplicada a cada time novo, por anos.
Referências
Veredito
Use pools separados (A) enquanto tiver menos de ~40 cartões ou nenhum time de plataforma, o desperdício custa menos que a operação. Use HyperPod com task governance (C) de 50 a ~2.000 cartões, 100% AWS, quando fair-share, empréstimo e MIG resolverem o seu multi-tenant: é o mesmo Kueue do CMB com o custo de manutenção transferido para a AWS. Use EKS com Kueue, KEDA, HAMi e Fluid (B) acima disso, ou quando precisar de fração por software e frota heterogênea, e trate os cinco projetos como produto interno, com dono, release e runbook. Use Bedrock e jobs SageMaker (D) sempre que não tiver modelo próprio; frota compartilhada só faz sentido quando existe frota. Em todos os casos, meça DCGM_FI_DEV_GPU_UTIL, não requests, a utilização que o CMB reportou é a real, e é a única que vira redução de fatura.
Continue lendo
Deep dives de arquitetura, AWS, IA e mercado: direto no seu email. Grátis.
Sem spam · cancele quando quiser
Pergunte ao Fernando sobre isto
Uma resposta focada sobre este artigo, do meu assistente de IA, baseada no meu trabalho.
Participe da conversa
Entre para comentar
Confirme seu e-mail para participar, você também recebe a newsletter. Sem senha.