Migrar cargas para instâncias spot
Instâncias spot custam uma fração do preço sob demanda porque o provedor pode retomá-las com aviso curto (na ordem de dois minutos na AWS, trinta segundos no GCP). A troca é explícita: você paga menos e assume a interrupção. Este guia mostra como assumir isso sem transformar economia em incidente.
1. Escolha as cargas certas
Seção intitulada “1. Escolha as cargas certas”Boas candidatas — toleram morrer e voltar:
- Workers de fila com mensagens idempotentes.
- Runners de CI e jobs de build.
- Processamento em lote com ponto de retomada.
- Ambientes de desenvolvimento e homologação.
- Parte da capacidade de serviços web sem estado, acima de uma base garantida.
Nunca em spot: banco de dados, control plane do cluster, serviço com estado em memória sem replicação, job longo sem retomada, e a última réplica de qualquer coisa.
O teste rápido: se esta instância sumir em dois minutos, o que o usuário percebe? Se a resposta for “nada” ou “alguns segundos a mais”, pode ir.
2. Diversifique tipos e zonas
Seção intitulada “2. Diversifique tipos e zonas”Interrupção costuma atingir um tipo de instância específico numa zona específica. Pedir “10
instâncias m5.large na zona A” é o jeito mais fácil de ser interrompido em bloco.
# Karpenter: deixe o provisionador escolher entre muitas famíliasapiVersion: karpenter.sh/v1kind: NodePoolmetadata: { name: spot-diversificado }spec: template: spec: requirements: - { key: karpenter.sh/capacity-type, operator: In, values: [spot] } - { key: kubernetes.io/arch, operator: In, values: [amd64, arm64] } - { key: karpenter.k8s.aws/instance-family, operator: In, values: [m6i, m6a, m7i, c6i, c6a] } - { key: topology.kubernetes.io/zone, operator: In, values: [sa-east-1a, sa-east-1b, sa-east-1c] } disruption: consolidationPolicy: WhenEmptyOrUnderutilized limits: { cpu: 200 } # teto explícito: seu limite de faturaQuanto mais famílias e zonas aceitáveis, menor a chance de interrupção simultânea.
3. Misture spot com sob demanda
Seção intitulada “3. Misture spot com sob demanda”A base do serviço em capacidade garantida; o excedente em spot.
# ASG / Node group com política mistamixedInstancesPolicy: instancesDistribution: onDemandBaseCapacity: 2 # sempre 2 sob demanda onDemandPercentageAboveBaseCapacity: 20 # acima disso, 80% spot spotAllocationStrategy: price-capacity-optimizedprice-capacity-optimized escolhe pools com capacidade disponível, e não apenas os mais
baratos — o que reduz bastante a taxa de interrupção na prática.
Distribua as réplicas para não concentrar tudo no mesmo tipo de capacidade:
topologySpreadConstraints: - maxSkew: 1 topologyKey: karpenter.sh/capacity-type whenUnsatisfiable: ScheduleAnyway labelSelector: { matchLabels: { app: loja } }4. Trate o aviso de interrupção
Seção intitulada “4. Trate o aviso de interrupção”O aviso só ajuda se alguém agir. Em Kubernetes, use um handler que drena o nó ao receber a notificação (o Karpenter e o NTH fazem isso), e garanta que a aplicação encerre com graça:
spec: terminationGracePeriodSeconds: 60 # menor que a janela de aviso containers: - name: worker lifecycle: preStop: exec: { command: ["sh","-c","sleep 10"] } # sai do endpoint antes de encerrar# impeça que a drenagem derrube o serviço inteiroapiVersion: policy/v1kind: PodDisruptionBudgetmetadata: { name: loja, namespace: prod }spec: minAvailable: 2 selector: { matchLabels: { app: loja } }Fora do Kubernetes, o padrão é o mesmo: escute o endpoint de metadados que anuncia a interrupção, pare de aceitar trabalho novo, finalize o que está em andamento, e devolva a mensagem para a fila se não der tempo.
5. Torne o trabalho retomável
Seção intitulada “5. Torne o trabalho retomável”Spot expõe o que já era frágil. Duas exigências:
- Idempotência: reprocessar a mesma mensagem não pode duplicar efeito.
- Ponto de retomada: job longo salva progresso; ao voltar, continua de onde parou.
Em fila, garanta que o visibility timeout seja maior que o tempo de processamento — assim a mensagem volta para a fila se o worker morrer, sem ser processada duas vezes ao mesmo tempo.
6. Meça a economia real e a interrupção
Seção intitulada “6. Meça a economia real e a interrupção”# proporção de capacidade em spot, no clusterkubectl get nodes -L karpenter.sh/capacity-type
# custo por tipo de capacidadeaws ce get-cost-and-usage --time-period Start=2026-08-01,End=2026-09-01 \ --granularity MONTHLY --metrics UnblendedCost \ --group-by Type=DIMENSION,Key=PURCHASE_TYPEAcompanhe também a frequência de interrupção e o efeito nas métricas de sintoma. Se a latência p95 piora a cada interrupção, o problema não é o spot: é a drenagem ou o tempo de partida da aplicação.
Se der errado
Seção intitulada “Se der errado”| Sintoma | Causa provável |
|---|---|
| Interrupções frequentes | Pouca diversificação de tipo e zona |
Pods Pending após interrupção |
Sem capacidade sob demanda de reserva, ou limites baixos |
| Erros 5xx a cada interrupção | Sem PDB, sem preStop, ou grace period curto |
| Mensagens processadas duas vezes | Falta de idempotência; visibility timeout curto |
| Economia menor que a esperada | Base sob demanda alta demais, ou pouca carga em spot |
| Job longo nunca termina | Sem ponto de retomada — não é carga para spot |
Checklist de pronto
Seção intitulada “Checklist de pronto”- Cargas classificadas por tolerância a interrupção.
- Diversificação de famílias, arquiteturas e zonas.
- Base sob demanda garantida para o tráfego crítico.
- Handler de interrupção drenando o nó.
-
PodDisruptionBudget,preStope grace period configurados. - Trabalho idempotente e retomável.
- Economia e taxa de interrupção medidas, com métricas de sintoma estáveis.