# Treino e inferência na mesma GPU: quatro formas de partilhar 10 mil aceleradores

O China Merchants Bank venceu o CNCF End User Case Study Contest ao colocar treino, fine-tuning e inferência num único plano de controle Kubernetes sobre quase 10 mil aceleradores heterogêneos: utilização média de 35% para mais de 60% e custo por milhão de tokens cortado em mais de 60%. Analiso o que faz esse número acontecer — fila com quota, autoscaling por métrica, virtualização de GPU e cache de dados — e comparo quatro formas de reproduzi-lo na AWS, com a matriz de decisão que eu usaria num banco.

- URL: https://fernando.moretes.com/blog/treino-e-inferencia-na-mesma-gpu-quatro-formas-de-partilhar-10-mil-ace

- Markdown: https://fernando.moretes.com/blog/treino-e-inferencia-na-mesma-gpu-quatro-formas-de-partilhar-10-mil-ace/article.md?lang=pt

- Published: 2026-09-15T10:15:23.382Z

- Category: IA & Agentes

- Tags: kubernetes, eks, gpu, kueue, keda, finops, sagemaker-hyperpod, ai-platform

- Reading time: 7 min

- Source: [China Merchants Bank Wins CNCF End User Case Study Contest for Unifying AI Training and Inference on Kubernetes](https://www.cncf.io/announcements/2026/09/07/china-merchants-bank-wins-cncf-end-user-case-study-contest-for-unifying-ai-training-and-inference-on-kubernetes/)

---

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:

1. **Ninguém arbitra:** pools separados, fronteira fixa no provisionamento.
2. **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.
3. **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.
4. **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.

### 🎛 Plano de controle — admissão e escala

- Kueue ClusterQueue + cohort + preempção (compute)
- KEDA ScaledObject (prometheus) (compute)
- HAMi gpumem / gpucores (ai)
- Prometheus utilização, fila, tokens/s (data)

### 🟧 AWS — frota heterogênea (EKS)

- p5 / p6 ResourceFlavor: treino (compute)
- g6e / g7e ResourceFlavor: inferência, MIG (compute)

### 📦 Dados — cache antes da GPU

- Fluid Dataset + warm-up (storage)
- S3 modelos, checkpoints (storage)

### Fluxos

- train -> kueue: LocalQueue
- lora -> kueue: LocalQueue
- infer -> keda: réplicas
- kueue -> hami: admite quando quota fecha
- keda -> hami: escala por métrica
- hami -> p5: cartão inteiro
- hami -> g6e: fração / MIG
- prom -> keda: query
- p5 -> prom: DCGM
- g6e -> prom: DCGM
- s3 -> fluid: prefetch
- fluid -> p5: checkpoint local
- fluid -> g6e: pesos locais

## Quatro formas de partilhar a frota
| Critério | 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

**Pros**
- 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

**Cons**
- Paga dois picos; 35% é o resultado esperado, não um acidente
- Treino espera fila enquanto a inferência tem GPU parada de madrugada

**Verdict:** Só até ~40 cartões ou enquanto não houver time de plataforma

### B. EKS + Kueue/KEDA/HAMi/Fluid

**Pros**
- 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

**Cons**
- 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

**Verdict:** Acima de ~500 cartões com time de plataforma dedicado de 4+ pessoas

### C. HyperPod + task governance

**Pros**
- Kueue, quota, fair-share (peso 0-100) e empréstimo (até 10.000% da quota) como política, não como YAML
- `IdleResourceSharing` reconcilia capacidade não alocada sozinho
- Observabilidade de fila e espera por time já vem no painel

**Cons**
- 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)

**Verdict:** De 50 a 2.000 cartões, 100% AWS, time de plataforma pequeno

### D. Bedrock + jobs SageMaker

**Pros**
- Zero GPU parada na sua conta
- Guardrails, trilha e isolamento vêm do serviço

**Cons**
- 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

**Verdict:** 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 memory` no outro — e a trilha de auditoria não explica.
- **Quota igual ao tamanho da frota:** se a soma dos `nominalQuota` bate 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.

> **Nota do curador:** 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

- [CNCF — China Merchants Bank wins End User Case Study Contest (Sep 8, 2026)](https://www.cncf.io/announcements/2026/09/07/china-merchants-bank-wins-cncf-end-user-case-study-contest-for-unifying-ai-training-and-inference-on-kubernetes/)
- [Kueue — Concepts: ClusterQueue, LocalQueue, ResourceFlavor, cohort, preemption](https://kueue.sigs.k8s.io/docs/concepts/)
- [HAMi — Heterogeneous AI Computing Virtualization Middleware (CNCF Incubating)](https://github.com/Project-HAMi/HAMi)
- [Fluid — Cloud-native data access platform (CNCF Incubating)](https://fluid-cloudnative.github.io/)
- [KEDA 2.18 — Prometheus scaler](https://keda.sh/docs/2.18/scalers/prometheus/)
- [Amazon EKS — Manage NVIDIA GPUs: DRA, device plugin, MIG and time-slicing](https://docs.aws.amazon.com/eks/latest/userguide/device-management-nvidia.html)
- [SageMaker HyperPod task governance — Policies (fair-share, borrowing, preemption)](https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-hyperpod-eks-operate-console-ui-governance-policies.html)
- [CNCF blog — Building a reliable cloud native foundation for distributed AI training (Atlassian, Sep 11, 2026)](https://www.cncf.io/blog/2026/09/11/building-a-reliable-cloud-native-foundation-for-distributed-ai-training/)

## 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.

**Rating:** C for most banks on AWS; B above ~2,000 
