Joao Brietzke Blog

← voltar

Tipos de instância EC2: escolher é responder qual é o seu gargalo

Escolher tipo de instância parece uma tabela de preços com centenas de linhas. Na prática são poucas perguntas, e a primeira delas não é sobre a AWS, é sobre a sua aplicação.


O nome da instância é um formulário preenchido

Todo tipo EC2 segue o mesmo padrão. Decompondo m7i.2xlarge:

m        7        i        .  2xlarge
│        │        │           │
família  geração  atributo    tamanho

Família (a letra): a categoria de workload. É a única decisão que realmente exige análise.

Geração (o número): hardware mais novo. Geração maior costuma custar menos por unidade de performance, então subir de geração (m6i para m7i) é quase sempre ganho puro, desde que a região tenha o tipo disponível.

Atributos (as letras depois do número): modificadores, e podem se combinar, como em c7gn ou m6idn.

Letra Significa
i processador Intel
a processador AMD
g AWS Graviton (ARM)
d tem disco NVMe local (instance store)
n rede reforçada
e memória ou armazenamento extra
z CPU de alta frequência
b banda de EBS reforçada
flex versão mais barata que não sustenta 100% de CPU o tempo todo

Tamanho: nano, micro, small, medium, large, xlarge, 2xlarge, e daí em diante até 48xlarge, além de metal. A partir de large, cada degrau dobra vCPU e memória, e dobra o preço junto. Dentro de uma mesma família, a relação preço/recurso é linear: uma 4xlarge custa o mesmo que duas 2xlarge.

As famílias: tudo gira em torno da razão vCPU:memória

Essa é a ideia central do assunto. Uma família é, no fundo, um ponto fixo na razão entre CPU e memória.

Família GiB por vCPU Para quê
C (compute) 2 CPU é o gargalo: encoding, batch, game server, build
M (general) 4 Balanceado. O default de quem ainda não mediu
R (memory) 8 Cache, banco de dados, processamento em memória
X 16 a 32 In-memory pesado, como Redis grande
U (high memory) de 3 a 24 TiB de RAM SAP HANA e afins
I (storage) 4, com NVMe local IOPS local: NoSQL, data warehouse, cache em disco
D / H HDD densa Muitos TB sequenciais, Hadoop, log
T (burstable) 2 a 4 Carga baixa e intermitente
P / G / Inf / Trn GPU ou chip acelerador Treino, inferência, gráficos
Hpc varia HPC com rede EFA e cluster acoplado
Mac fixo Build de iOS e macOS, só em Dedicated Host

A tradução prática é essa: escolher família é responder "meu gargalo é CPU, memória, disco ou rede?". Se a resposta não existe, o certo é começar na M, porque a M é o ponto de partida que permite medir.

Como descobrir qual é o gargalo

A frase acima só é útil se você souber respondê-la. E responder não é chutar pelo tipo de aplicação, é olhar métrica.

O gargalo é sempre um só. Num dado momento, um recurso satura antes dos outros, e é ele que define o teto. Os demais ficam ociosos por causa dele. Isso tem uma consequência forte: recurso ocioso não é desperdício, é sintoma. Uma máquina com CPU em 15% e disco em 100% não está superdimensionada em CPU, ela está esperando o disco.

Por isso a pergunta certa nunca foi "está usando muito?". É o que satura primeiro quando eu aumento a carga?

O que cada gargalo parece:

Gargalo Sintoma Onde olhar
CPU Latência sobe junto com o RPS, de forma proporcional. Load average acima do número de vCPUs CloudWatch CPUUtilization, top (%us), uptime
Memória Swap ativo, GC rodando o tempo todo, processo morto sem log nenhum free -m, vmstat 1 (colunas si/so), dmesg com OOM killer
Disco %iowait alto com CPU baixa, e tudo lento ao mesmo tempo iostat -x 1 (await, %util), CloudWatch VolumeQueueLength
Rede Latência alta com CPU e disco baixos. Retransmissão de TCP CloudWatch NetworkIn/Out, ss -s, iftop
Externo Nada satura na máquina e ainda assim está lento APM, tempo de query, latência da API que você chama

A última linha é a que mais engana. Latência alta com tudo ocioso não é problema de instância, é banco, API de terceiro ou lock na aplicação. Trocar de família não muda nada, e é aí que se gasta o dinheiro mais inútil em EC2.

A pegadinha do CloudWatch. Por padrão, o CloudWatch enxerga a instância de fora, do lado do hypervisor. Ele entrega CPU, rede e I/O de disco de graça, mas não entrega memória nem espaço em disco, porque isso só existe dentro do sistema operacional convidado.

A consequência é direta: se você nunca instalou o CloudWatch Agent, o seu gráfico de memória não existe, e a família R foi descartada por falta de evidência, não por análise. Instalar o agent é o primeiro passo de qualquer dimensionamento sério.

As métricas de saldo denunciam o futuro. Existem contadores que não mostram saturação atual, mostram que você está vivendo de crédito. Todos caem devagar, e o problema só aparece quando zeram:

  • CPUCreditBalance (família T)
  • BurstBalance (volumes EBS gp2)
  • EBSIOBalance% e EBSByteBalance% (instâncias com EBS anunciado como "up to")

Saldo caindo em linha reta é aviso de que a carga já passou do que a instância sustenta, mesmo com todos os outros gráficos parecendo saudáveis.

Duas armadilhas de leitura. A primeira: média esconde pico. Uma métrica em janela de 5 minutos com estatística Average transforma um pico de 100% em 30% de média. Dimensione olhando p95 ou Maximum.

A segunda: vCPU não é core. Fora do Graviton, 1 vCPU é uma thread de hyperthreading, não um núcleo físico. Duas threads disputando a mesma unidade de execução não entregam o dobro, e é por isso que benchmark sintético promete mais do que a aplicação real entrega.

Do gargalo para a decisão:

Gargalo Resposta
CPU Família C
Memória Família R, depois X
Disco, precisa de IOPS Família I, ou o sufixo d (NVMe local)
Disco, mas é EBS e não local Não é o tipo da instância: mude o volume (gp3, io2) ou use o sufixo b
Rede Sufixo n, ou um tamanho maior para sair do "up to"
Externo Nenhuma instância resolve

Medir uma vez não basta. O gargalo se move: você resolve o disco e a CPU vira o teto. Isso é esperado, e é sinal de que a etapa anterior funcionou.

T: a família que cobra por promessa, não por uso

A T é a única conceitualmente diferente das outras, e é onde a maior parte dos erros acontece.

Uma t3.medium não te dá 2 vCPUs inteiras. Ela te dá uma baseline, uma fração garantida de cada vCPU, e um sistema de créditos de CPU: quando você usa menos que a baseline, acumula crédito; quando precisa de mais, gasta crédito para rodar acima dela.

O que acontece quando os créditos acabam depende do modo:

  • Standard: você é limitado à baseline. A aplicação não cai, ela fica lenta. Esse é o problema, porque parece bug de código e não de dimensionamento.
  • Unlimited: a AWS deixa você continuar acima da baseline e cobra por hora de vCPU excedente. É o padrão da T3 e da T4g. A conta sobe sem alarme nenhum.

Regra prática: a T serve para carga que fica ociosa a maior parte do tempo. Se o gráfico de CPU mostra um platô em vez de picos, você está pagando caro por uma M pior.

Graviton: o desconto que cobra em portabilidade

t4g, m7g, c7g, r8g. São processadores ARM projetados pela própria AWS, e costumam entregar melhor performance por dólar que o equivalente x86 da mesma geração.

O que se paga por isso:

  • É outra arquitetura. Binário compilado para x86 não roda.
  • A imagem Docker precisa ser linux/arm64, ou multi-arch. Toda a cadeia de build muda.
  • Dependência nativa (extensão em C, binário baixado num postinstall) precisa ter build ARM.

Em Node, Python, Java ou Go, a migração costuma ser transparente. O risco mora nas dependências binárias e nas imagens base de terceiros, não na sua linguagem.

O tamanho escala mais coisas do que parece

Subir de large para 2xlarge não dobra só vCPU e memória. Sobem junto:

  • A banda de rede
  • A banda para o EBS, porque o disco não é local, é rede
  • O número de ENIs e de IPs

E aqui mora uma armadilha. Instâncias pequenas anunciam rede e EBS como "Up to 10 Gbps". Esse up to é burstable, exatamente como a CPU da T: sustentado, você tem bem menos. Um job que copia dados por 40 minutos nunca vê o número do datasheet.

Dois detalhes que ligam com o resto:

  • .metal entrega a máquina física, sem hypervisor. É o caso limite do que aparece em Virtualização: serve para quem precisa rodar o próprio hypervisor ou acessar recursos de CPU que a camada de virtualização esconde.
  • Instance store (o sufixo d) é disco físico preso ao host: rápido e efêmero. Sobrevive a reboot, mas some em stop ou terminate. O EBS é volume de rede, persistente e independente da instância. A diferença está detalhada em Armazenamento na AWS.

Como escolher, na ordem certa

  1. Comece na M da geração mais nova disponível na região.
  2. Meça por alguns dias: CPU, memória, IOPS e rede. O Compute Optimizer monta essa recomendação sozinho a partir do CloudWatch.
  3. Mova de lado, não para cima. CPU em 90% com memória em 20% não pede uma instância maior, pede uma C. O inverso pede uma R. Subir de tamanho para resolver desequilíbrio de razão é como se paga o dobro para usar metade.
  4. Só então suba de tamanho, se a razão já estiver certa e ainda faltar capacidade.

O erro mais comum é escolher por vCPU quando o gargalo real é banda de EBS ou de rede. O sintoma é CPU baixa com latência alta, e nenhuma troca de família resolve isso: o que resolve é n, b, ou um tamanho maior.

O que é ortogonal ao tipo

Vale separar, porque se mistura fácil na cabeça: como você paga não faz parte do tipo de instância. On-Demand, Savings Plans, Reserved e Spot são uma decisão independente, aplicada em cima do tipo que você já escolheu, detalhada em Opções de compra no EC2. O mesmo vale para tenancy, placement group e AMI.

Duas ideias que se combinam com isso:

Trade-offs

Ganha Paga
Família certa: mesma performance por menos dinheiro Precisa medir antes, e medir memória exige instalar o CloudWatch Agent
Família T: preço baixíssimo em carga ociosa Lentidão silenciosa (standard) ou conta silenciosa (unlimited)
Graviton: melhor performance por dólar Recompilação, imagem multi-arch, risco em dependência nativa
Instance store: a menor latência disponível Dado efêmero, some em stop ou terminate
Instância pequena: custo mínimo Rede e EBS "up to", que não se sustentam sob carga contínua

Referências