# Cold start de LLM no HyperPod: de 27 minutos a segundos com model caching

O SageMaker HyperPod passou a pré-carregar pesos de modelo em NVMe local e imagens de contêiner nos nós, com cerca de 60% de scale-out mais rápido em modelos de 57 a 145 GB. Conto a migração de um endpoint de análise de documentos passo a passo — do baseline de 27 minutos até o primeiro token à reescrita da política de autoscaling que só existia para conviver com o cold start.

- URL: https://fernando.moretes.com/blog/cold-start-de-llm-no-hyperpod-de-27-minutos-a-segundos-com-model-cachi-amazon-sagem

- Markdown: https://fernando.moretes.com/blog/cold-start-de-llm-no-hyperpod-de-27-minutos-a-segundos-com-model-cachi-amazon-sagem/article.md?lang=pt

- Published: 2026-09-14T10:15:14.951Z

- Category: IA & Agentes

- Tags: SageMaker HyperPod, LLM inference, cold start, KEDA, Karpenter, NVMe, EKS, autoscaling

- Reading time: 7 min

- Source: [Amazon SageMaker HyperPod now supports model caching for faster inference autoscaling and reduced cold starts](https://aws.amazon.com/about-aws/whats-new/2026/09/sgm-hyperpod-model-caching-inf/)

---

Um pod de inferência com um modelo de 145 GB levava mais de 20 minutos entre o scheduler aceitar o pod e o primeiro token sair — e o autoscaler só pedia esse pod quando a fila já estava cheia. Este artigo conta a migração de um endpoint LLM no SageMaker HyperPod para o model caching que a AWS liberou em 11 de setembro de 2026: pesos em NVMe local, imagem pré-puxada, e a parte que nenhum anúncio menciona — reescrever a política de autoscaling que tinha sido calibrada para conviver com o cold start.

## O ponto de partida: 27 minutos até o primeiro token

O endpoint que serve de fio condutor aqui é um pipeline de análise de documentos — contratos e extratos, tráfego em rajada na abertura do expediente e no fechamento do dia. Modelo com cerca de 145 GB de pesos no S3, vLLM num contêiner próprio de 12 GB no ECR, instâncias `p5.48xlarge`, `InferenceEndpointConfig` com `autoScalingSpec` disparado por invocações no CloudWatch.

Medido no `kubectl describe pod`, o tempo entre `Scheduled` e `Ready` se dividia em três fatias: 5 a 7 minutos puxando a imagem do ECR, mais de 20 minutos baixando os pesos do S3 para o disco do nó, e cerca de 90 segundos carregando pesos na HBM. As duas primeiras fatias são rede e não têm nada a ver com o modelo — são o mesmo download repetido em cada pod, em cada scale-out, em cada reposição de nó pelo health check do HyperPod.

A consequência operacional não era o tempo em si; era o que fizemos para conviver com ele. Mantínhamos duas réplicas de folga permanentes, `scaleUpStabilizationTime` em zero e `targetValue` baixo para o autoscaler antecipar a rajada. Duas `p5.48xlarge` paradas 24 horas por dia são 1.440 instance-hours por mês compradas só para esconder um download. A pergunta certa não era "como fazer o pod subir mais rápido?" — era "quanto da folga existe só porque o pod demora?".

## A jornada, na ordem em que foi feita

1. **Medir o baseline por fatia** — Antes de mudar qualquer coisa, gravamos por pod o tempo de pull de imagem, de download de pesos e de load em GPU, a partir dos eventos do pod e dos logs do vLLM. Sem essa divisão não dá para saber se o ganho veio do cache ou do acaso.

2. **Confirmar NVMe no tipo de instância** — `weightsCache` exige NVMe local no `hostPath` (padrão `/opt/dlami/nvme`). Instância só com EBS não avisa: o cache nunca aquece e o pod cai no fallback para o S3 em silêncio. A `p5.48xlarge` traz 8 × 3,84 TB.

3. **Atualizar o add-on do Inference Operator** — Se o `modelCacheConfig` não é reconhecido, o operator é antigo. Atualizamos o add-on do EKS num cluster de homologação primeiro, porque o mesmo add-on traz o KEDA e o ALB controller — a atualização mexe em mais do que o cache.

4. **Ligar só o `imageCache`** — Primeira mudança em produção: `imageCache.enabled: true`. Não altera o pod de inferência, só cria um DaemonSet que pré-puxa a imagem. Ganho medido: os 5 a 7 minutos de pull viraram segundos, na linha dos 97% que a AWS reporta.

5. **Ligar o `weightsCache` e observar os rótulos** — `weightsCache.enabled: true`, mesmo `hostPath`. Acompanhamos `kubectl get nodes --show-labels | grep cache-ready` até todos os nós do grupo estarem rotulados antes de tocar no autoscaler.

6. **Versionar o caminho do modelo** — O cache não detecta mudança na fonte. Passamos a publicar pesos em `s3://.../modelo/v2026-09-11/` e a alterar o `spec` a cada release — a mudança de caminho é o que força um novo aquecimento.

7. **Reescrever o autoscaling** — Com scale-out em segundos, retiramos as duas réplicas de folga, subimos `targetValue` e mantivemos `scaleUpStabilizationTime: 0`. O cold start deixou de ser um parâmetro escondido dentro da política.

## Dois caches, um rótulo de nó e uma afinidade preferida

O desenho tem três peças que valem entender antes de confiar nele.

**Aquecimento por nó:** o operator baixa os pesos da fonte (S3, FSx for Lustre, Hugging Face Hub ou JumpStart) para um diretório isolado por deployment sob o `hostPath`, em cada nó que satisfaz as restrições de scheduling do deployment. Quando termina, aplica ao nó um rótulo com prefixo `inference.sagemaker.aws.amazon.com/weights-cache-ready.` seguido do UID da configuração; o cache de imagem faz o mesmo com `image-cache-ready.` via DaemonSet.

**Afinidade preferida, não obrigatória:** o deployment usa *preferred* node affinity nesses rótulos. Um pod vai para um nó quente quando existe um; se não existe, é agendado do mesmo jeito e baixa do S3 e do ECR como sempre fez. Isso é o que torna a migração reversível: o pior caso é o comportamento antigo, nunca um pod preso em `Pending`.

**Montagem read-only:** o pod monta o diretório do cache como volume `hostPath` somente leitura e o vLLM lê do NVMe local — a AWS cita cerca de 7 GB/s — em vez de percorrer a rede. O mesmo modelo de 145 GB que levava mais de 20 minutos para chegar ao disco agora está lá antes de o pod existir.

O que isso não faz: não replica cache entre nós, não detecta pesos novos na mesma chave e não sobrevive à troca do nó. Nó reposto pelo health check nasce frio, e o operator precisa aquecê-lo de novo enquanto os nós quentes seguem servindo.

## Ciclo de vida do model caching no HyperPod Inference

Do apply do spec ao pod pronto: quem aquece, quem rotula, onde o fallback entra e por que um nó novo do Karpenter continua frio.

### 🔧 HyperPod Inference Operator

- Controller reads InferenceEndpointConfig (compute)
- Weights warmer per-node download (compute)
- Image DaemonSet pre-pull per node (ci)

### 🟧 AWS — remote sources

- S3 / FSx for Lustre weights v2026-09-11 (storage)
- ECR vLLM image 12 GB (storage)

### 🖥 GPU node — p5.48xlarge

- Local NVMe /opt/dlami/nvme (8 × 3.84 TB) (storage)
- Node label cache-ready.<uid> (network)
- vLLM pod mounts cache read-only (ai)

### 📈 Autoscaling — two layers

- KEDA autoScalingSpec (pods) (compute)
- Karpenter new node = cold cache (compute)

### Fluxos

- ops -> ctrl: 1. apply do spec
- ctrl -> warmer: 2. weightsCache.enabled
- ctrl -> ds: 2. imageCache.enabled
- warmer -> s3: 3. download 1× por nó
- ds -> ecr: 3. pull 1× por nó
- warmer -> nvme: 4. grava pesos
- warmer -> lbl: 5. rotula nó quente
- keda -> pod: 6. scale-out
- lbl -> pod: 7. preferred affinity
- nvme -> pod: 8. leitura local ~7 GB/s
- pod -> s3: fallback em nó frio
- karp -> pod: nó novo → caminho lento

## O que muda no autoscaling quando o cold start some

O autoscaling no HyperPod tem duas camadas, e o cache só age numa delas.

**Camada de pod (KEDA):** o `autoScalingSpec` lê CloudWatch ou Amazon Managed Prometheus a cada `pollingInterval` (padrão 30 s; o HPA consulta o KEDA a cada 15 s) e decide réplicas. Com cold start de 27 minutos, uma política honesta precisa disparar cedo e manter folga; por isso tínhamos `targetValue` conservador e réplicas paradas. Com scale-out em segundos num nó quente, a mesma política passa a oscilar — sobe pods que ficam prontos antes de a janela padrão de 300 s de `metricCollectionPeriod` refletir a rajada. Reduzimos `metricCollectionPeriod` para 60 s, subimos `targetValue` e deixamos `scaleDownStabilizationTime` em 300 s para não devolver capacidade no primeiro vale.

**Camada de nó (Karpenter):** aqui o cache não ajuda. Um nó que o Karpenter acabou de provisionar não tem pesos nem imagem; o primeiro pod nele cai no fallback e paga os 27 minutos inteiros. Se o seu pico exige nós novos, o model caching encurta o segundo pod naquele nó, não o primeiro. A decisão que tomamos foi manter um piso de nós GPU quentes — o cache aquece neles fora do horário de pico — e usar o Karpenter só para o excedente raro.

Scale-to-zero no nível de nó e model caching resolvem problemas diferentes. Escolher entre eles é escolher o que você prefere pagar: instância ociosa com cache pronto, ou download completo no minuto de maior demanda.

## Antes e depois, nos números que a AWS publicou e nos que medimos

- **~60%** — scale-out mais rápido. Benchmark da AWS com weights cache em modelos de 57 a 145 GB; o ganho cresce com o tamanho do modelo.
- **97%** — menos tempo de image pull. Mais de 2 minutos eliminados por pod com o image cache — a mudança de menor risco da migração.
- **1.440 h** — instance-hours de folga por mês. Duas p5.48xlarge ociosas que existiam só para esconder o download e saíram da conta.

## O custo que fica: disco, versão e vizinhança de nó

Ligar o cache leva cinco linhas de YAML. Mantê-lo é o que custa, e três coisas viraram rotina de operação.

**Capacidade de NVMe:** cada deployment com cache ocupa o próprio diretório no mesmo disco local do nó. Dois modelos de 145 GB mais um de 57 GB num `p5.48xlarge` cabem folgados em 30 TB — mas o time que consolida seis endpoints num grupo de instâncias menor descobre a pressão de disco só quando o aquecimento para de acontecer. A documentação é explícita: separe grupos de instâncias ou reduza deployments cacheados por nó.

**Invalidação por versão:** como a fonte não é monitorada, a chave do S3 é a versão. Publicar pesos novos sobrescrevendo `latest/` deixa o nó quente servindo o modelo antigo por tempo indefinido, sem erro algum. Um sufixo de versão no caminho e um `kubectl apply` novo é o único gatilho confiável; o operator limpa os arquivos antigos quando o deployment antigo é deletado.

**Sinal de fallback:** um pod que sobe sem o volume `hostPath` está no caminho lento e ninguém é avisado. Criamos um alerta simples: contagem de nós com rótulo `cache-ready` menor que o número de nós do grupo por mais de 10 minutos, mais o tempo `Scheduled → Ready` por pod exportado para o Prometheus. Sem isso, a regressão para o comportamento antigo passa despercebida até o próximo pico — e o próximo pico é exatamente quando você não quer descobrir.

> **Os riscos que gerenciamos no caminho:** Três falhas silenciosas concentram o risco desta migração. **Instância sem NVMe no `hostPath`:** o cache nunca aquece, nada quebra e o cold start continua igual. **Pesos sobrescritos na mesma chave:** nós quentes servem o modelo antigo até alguém mudar o `spec`. **Nó novo do Karpenter no pico:** o primeiro pod paga o download inteiro; o cache só protege o segundo. Nenhuma das três gera erro — todas geram latência que parece normal.

## Anti-padrões que vi surgir na primeira semana

- **Ligar os dois caches e o autoscaler novo no mesmo apply**: quando o tempo de `Ready` cai e a política oscila no mesmo dia, ninguém sabe qual mudança causou o quê. Uma mudança por vez, com o baseline medido.
- **Tratar o cache como réplica**: ele é por nó, não por cluster. Consolidar deployments num grupo pequeno de instâncias para 'compartilhar' o cache só compartilha o disco — e a pressão dele.
- **Scale-to-zero de nó com model caching como se fossem complementares**: quando o Karpenter remove o último nó, remove o cache junto. O primeiro pod do dia volta aos 27 minutos.

> **Nota do curador:** Eu ligaria o `imageCache` em toda deployment de inferência no HyperPod já na próxima janela de mudança — é um DaemonSet, não toca no pod, e devolve dois minutos por scale-out sem pedir nada em troca. O `weightsCache` eu só ligo depois de confirmar NVMe no tipo de instância e de colocar versão no caminho do S3, porque as duas falhas que ele introduz são mudas. A lição que fica de 16 anos operando plataforma: toda otimização de cold start muda a política de autoscaling que foi escrita para conviver com o cold start antigo — se você não reescreve a política, fica pagando a folga velha com o problema novo resolvido.

## Veredito

Use o model caching do HyperPod quando: o modelo passa de 50 GB, o tráfego tem rajada previsível, a frota tem NVMe local e você consegue manter um piso de nós quentes fora do pico. Nesse cenário ele retira do autoscaler a folga que existia só para esconder o download — e é aí que o dinheiro aparece, não no benchmark. Não espere ganho quando o pico exige nós novos do Karpenter, quando a frota é só EBS, ou quando o modelo cabe em um pull de poucos minutos: nesses três casos o custo de manter versão de caminho e alerta de fallback supera o que o cache devolve. Ligue o `imageCache` sempre; ligue o `weightsCache` com as condições acima.

**Rating:** Recomendado com condições / Recommended 

## Referências

- [Amazon SageMaker HyperPod now supports model caching for faster inference autoscaling and reduced cold starts (AWS What'](https://aws.amazon.com/about-aws/whats-new/2026/09/sgm-hyperpod-model-caching-inf/)
- [Reduce inference cold starts on Amazon SageMaker HyperPod with model caching (AWS ML Blog, 10 Sep 2026)](https://aws.amazon.com/blogs/machine-learning/reduce-inference-cold-starts-on-amazon-sagemaker-hyperpod-with-model-caching/)
- [Model weights caching and image caching — SageMaker AI Developer Guide](https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-hyperpod-model-deployment-model-caching.html)
- [Autoscaling policies for your HyperPod inference model deployment — SageMaker AI Developer Guide](https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-hyperpod-model-deployment-autoscaling.html)
- [KV caching and intelligent routing — SageMaker AI Developer Guide](https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-hyperpod-model-deployment-caching-routing.html)
- [Amazon SageMaker HyperPod — Generative AI inference architecture and best practices on AWS (Prescriptive Guidance)](https://docs.aws.amazon.com/prescriptive-guidance/latest/gen-ai-inference-architecture-and-best-practices-on-aws/amazon-sage-maker-hyper-pod.html)
- [Unlock efficient model deployment: Simplified Inference Operator setup on Amazon SageMaker HyperPod (AWS Architecture Bl](https://aws.amazon.com/blogs/architecture/unlock-efficient-model-deployment-simplified-inference-operator-setup-on-amazon-sagemaker-hyperpod)
- [Amazon EC2 P5 instances — specifications (NVMe, HBM, EFA)](https://aws.amazon.com/ec2/instance-types/p5/)
