Karpenter: como reduzir custos de Kubernetes sem sacrificar performance
Introdução
Um dos maiores desafios em ambientes Kubernetes não é apenas escalar aplicações, mas garantir que a infraestrutura cresça e diminua de forma eficiente.
Em um cluster Kubernetes tradicional executado na AWS, é comum encontrar uma arquitetura baseada em Amazon EKS (Elastic Kubernetes Service) com Managed Node Groups ou Auto Scaling Groups (ASGs).
O problema aparece quando a demanda das aplicações varia.
Imagine um cluster com:
- APIs;
- workers;
- consumidores de RabbitMQ ou Kafka;
- pipelines de CI/CD;
- aplicações internas;
- workloads de processamento;
- componentes de observabilidade.
Durante o horário comercial, o cluster pode precisar de 20 nós. Durante a madrugada, talvez apenas seis.
Se os Node Groups forem dimensionados para suportar o pico, boa parte da infraestrutura ficará ociosa durante várias horas.
Uma abordagem tradicional seria utilizar:
Pods
↓
Horizontal Pod Autoscaler
↓
Pods Pending
↓
Cluster Autoscaler
↓
Auto Scaling Group
↓
EC2O Cluster Autoscaler resolve parte do problema, mas trabalha principalmente ajustando grupos de nós previamente definidos.
O Karpenter adota outra abordagem.
Em vez de perguntar:
"Qual Node Group deve crescer?"
ele trabalha mais próximo de:
"Quais recursos esses pods realmente precisam e qual capacidade de infraestrutura atende essa demanda?"
A partir disso, o Karpenter pode selecionar diferentes famílias, tamanhos, zonas e modelos de capacidade EC2.
Essa flexibilidade permite combinar:
- Spot Instances;
- On-Demand;
- múltiplos tipos de EC2;
- consolidação de nós;
- escalabilidade orientada aos pods;
- workloads com requisitos diferentes.
O resultado pode ser uma infraestrutura significativamente mais eficiente sem necessariamente reduzir a performance das aplicações.
O que é Karpenter
Karpenter é um projeto open source de autoscaling de nós Kubernetes criado originalmente pela AWS.
Seu objetivo é provisionar infraestrutura computacional dinamicamente de acordo com os requisitos dos workloads.
Em vez de depender exclusivamente de grupos fixos de instâncias, o Karpenter observa pods que não conseguem ser agendados e determina qual infraestrutura pode executá-los.
Simplificando:
Deployment
↓
Pods
↓
Kubernetes Scheduler
↓
Pods Pending
↓
Karpenter
↓
Análise dos requisitos
↓
EC2 Fleet / AWS APIs
↓
Nova EC2
↓
Node entra no cluster
↓
Pods são agendadosO Karpenter considera fatores como:
- CPU;
- memória;
- arquitetura;
- zona de disponibilidade;
- afinidade;
- anti-affinity;
- taints;
- tolerations;
- topology spread;
- instance type;
- capacidade Spot;
- capacidade On-Demand.
Nos releases atuais, a configuração gira principalmente em torno de objetos como NodePool, NodeClaim e, no provider AWS, EC2NodeClass.
Principais conceitos
NodePool
O NodePool determina quais tipos de nós o Karpenter pode criar.
Ele define restrições como:
arquitetura
sistema operacional
família EC2
tamanho
zona
modelo de capacidade
taints
labels
limites
políticas de disruptionUm cluster pode possuir vários NodePools.
Por exemplo:
NodePool: applications
→ Spot
→ c/m/r
→ amd64
NodePool: databases
→ On-Demand
→ r-family
→ amd64
NodePool: monitoring
→ Spot + On-Demand
→ m/r
NodePool: cicd
→ Spot
→ c/mSeparar os workloads dessa forma permite controlar custo e disponibilidade individualmente.
A documentação recomenda que NodePools sejam, quando possível, mutuamente exclusivos, reduzindo ambiguidades sobre qual pool deve atender determinado workload.
EC2NodeClass
O EC2NodeClass contém configurações específicas da AWS.
Por exemplo:
- AMI;
- subnets;
- Security Groups;
- IAM Role;
- discos EBS;
- configuração do kubelet;
- tags EC2.
Enquanto o NodePool responde:
"Que tipo de capacidade posso utilizar?"
o EC2NodeClass responde:
"Como essa instância deve ser criada na AWS?"
Um mesmo EC2NodeClass pode ser utilizado por múltiplos NodePools.
NodeClaim
O NodeClaim representa uma solicitação concreta de capacidade criada pelo Karpenter.
Podemos visualizar assim:
NodePool
↓
template permitido
↓
Karpenter identifica demanda
↓
NodeClaim
↓
EC2
↓
Kubernetes NodeO NodeClaim contém os requisitos resultantes da combinação entre:
- NodePool;
- requisitos dos pods;
- recursos necessários;
- disponibilidade do cloud provider.
Normalmente não é um objeto que o operador cria manualmente.
Como o Karpenter funciona
Imagine três pods pendentes:
api-1 500m CPU 512Mi
api-2 500m CPU 512Mi
worker-1 2000m CPU 2GiO Karpenter observa que não existe capacidade suficiente no cluster.
Ele analisa:
CPU total
Memória total
nodeSelector
affinity
topologySpreadConstraints
taints/tolerations
architecture
zones
capacity-typeDepois procura ofertas EC2 capazes de executar esses pods.
Ao contrário de uma estratégia que determina antecipadamente apenas:
t3.xlargeé possível fornecer flexibilidade para utilizar:
c6a.large
c6a.xlarge
c7a.large
m6a.large
m6a.xlarge
m7a.large
t3a.xlarge
...Desde que essas instâncias satisfaçam os requisitos definidos.
Essa flexibilidade é particularmente importante para Spot.
Quanto mais restrições desnecessárias forem aplicadas, menor será o conjunto de capacidade disponível. A AWS recomenda evitar restringir excessivamente tipos de instância para workloads que podem ser flexíveis.
Onde realmente está a economia
Karpenter sozinho não torna um cluster barato.
A redução de custo normalmente vem da combinação de várias estratégias:
Karpenter
+
Spot
+
instâncias flexíveis
+
requests corretos
+
consolidação
+
autoscaling de pods
+
remoção de capacidade ociosaAntes
Imagine cinco nós:
5 × m6i.xlargeCada um possui aproximadamente:
4 vCPU
16 GiB RAMCapacidade:
20 vCPU
80 GiB RAMMas os workloads estão utilizando aproximadamente:
8 vCPU
32 GiB RAMExiste bastante infraestrutura ociosa.
Depois
Com Karpenter, pods podem ser reorganizados e nós subutilizados podem se tornar candidatos à consolidação.
O cluster pode chegar, por exemplo, a:
2 × m6a.xlarge
1 × c6a.xlargeou outra combinação que satisfaça os requisitos de scheduling.
A escolha real dependerá de:
- preço;
- disponibilidade;
- requests;
- arquitetura;
- constraints;
- modelo de capacidade.
O objetivo não deveria ser escolher manualmente a EC2 mais barata.
O objetivo é oferecer flexibilidade suficiente para o Karpenter escolher capacidade eficiente.
Exemplo completo: EC2NodeClass
Um exemplo próximo de produção:
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
name: production
spec:
amiSelectorTerms:
- alias: al2023@v20250808
role: KarpenterNodeRole-production
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: production
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: production
blockDeviceMappings:
- deviceName: /dev/xvda
ebs:
volumeSize: 80Gi
volumeType: gp3
encrypted: true
deleteOnTermination: true
tags:
Environment: production
ManagedBy: karpenterO que essa configuração faz
O EC2NodeClass define como os nós serão criados na AWS.
amiSelectorTerms:determina a AMI utilizada.
Em produção, é recomendável fixar uma versão testada da AMI em vez de utilizar automaticamente @latest, evitando que uma nova imagem seja introduzida no cluster sem validação prévia. Essa recomendação é explicitamente feita pela documentação de boas práticas do EKS para Karpenter.
role:determina a IAM Role utilizada pelas instâncias.
subnetSelectorTerms:permite descobrir as subnets através de tags.
securityGroupSelectorTerms:seleciona os Security Groups utilizados pelos nós.
volumeType: gp3utiliza volumes EBS GP3.
E:
encrypted: truegarante criptografia do volume.
Os nomes da role, versão da AMI, tamanho do disco, tags, subnets e Security Groups devem ser adaptados para o ambiente.
NodePool Spot para aplicações
Agora podemos criar um NodePool.
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: prod-app
spec:
template:
metadata:
labels:
workload-type: application
spec:
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: production
taints:
- key: prod-app
value: "true"
effect: NoSchedule
requirements:
- key: kubernetes.io/arch
operator: In
values:
- amd64
- key: kubernetes.io/os
operator: In
values:
- linux
- key: karpenter.sh/capacity-type
operator: In
values:
- spot
- key: karpenter.k8s.aws/instance-category
operator: In
values:
- c
- m
- key: karpenter.k8s.aws/instance-generation
operator: Gte
values:
- "6"
expireAfter: 720h
terminationGracePeriod: 2m
limits:
cpu: "200"
memory: 400Gi
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 5m
budgets:
- nodes: "20%"Esse NodePool permite que o Karpenter escolha instâncias:
c*
m*a partir da geração seis, desde que sejam compatíveis com os requisitos.
Em vez de definir:
values:
- m6a.xlargeoferecemos várias possibilidades.
Isso melhora as chances de encontrar capacidade Spot disponível.
O Karpenter suporta restrições por arquitetura, sistema operacional, tipo de capacidade, categoria, geração e outras características das instâncias.
Taint
O NodePool cria:
prod-app=true:NoScheduleConsequentemente, somente workloads com a toleration correspondente poderão ser executados nesses nós.
Isso evita que componentes aleatórios do cluster sejam enviados para capacidade destinada às aplicações.
Limits
limits:
cpu: "200"
memory: 400Giestabelece um limite de crescimento do pool.
Sem spec.limits, o crescimento máximo pode acabar sendo limitado basicamente pelas quotas do cloud provider, portanto definir limites é uma proteção operacional e financeira importante.
Deployment utilizando o NodePool
apiVersion: apps/v1
kind: Deployment
metadata:
name: orders-api
namespace: production
spec:
replicas: 4
selector:
matchLabels:
app: orders-api
template:
metadata:
labels:
app: orders-api
spec:
terminationGracePeriodSeconds: 30
tolerations:
- key: prod-app
operator: Equal
value: "true"
effect: NoSchedule
nodeSelector:
workload-type: application
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: orders-api
containers:
- name: api
image: registry.example.com/orders-api:2.8.1
ports:
- containerPort: 8080
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 15
periodSeconds: 20Aqui existe um ponto fundamental:
resources:
requests:
cpu: 500m
memory: 512MiO Karpenter utiliza os requests para entender quanto recurso os pods precisam.
Se os requests estiverem completamente superdimensionados, o Karpenter concluirá que precisa de mais infraestrutura do que realmente é necessário.
Por exemplo:
Uso real CPU: 150m
CPU request: 2000mPara o Kubernetes, aquele pod reserva:
2 CPUsmesmo utilizando apenas uma pequena fração disso.
Consequentemente, FinOps em Kubernetes começa pelo correto dimensionamento dos workloads.
PodDisruptionBudget
Se utilizarmos consolidação, precisamos considerar disponibilidade.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: orders-api
namespace: production
spec:
minAvailable: 3
selector:
matchLabels:
app: orders-apiCom quatro réplicas:
replicas = 4
minAvailable = 3uma interrupção voluntária deve preservar pelo menos três pods disponíveis.
O PodDisruptionBudget (PDB) ajuda a proteger a aplicação durante operações como drain e consolidação.
Porém PDBs excessivamente restritivos também podem impedir o Karpenter de consolidar nós. A própria documentação mostra PDBs como uma possível causa de nós marcados como Unconsolidatable.
Portanto:
PDB fraco demais
→ risco de indisponibilidade
PDB restritivo demais
→ infraestrutura difícil de consolidar
PDB correto
→ equilíbrio entre disponibilidade e eficiênciaExemplo do dia a dia
Considere uma plataforma SaaS com:
EKS
├── APIs Node.js
├── APIs Go
├── workers
├── RabbitMQ
├── Redis
├── PostgreSQL
├── Prometheus
├── Grafana
└── pipelines CI/CDDurante horário comercial:
CPU solicitada: ~45 vCPUÀ noite:
CPU solicitada: ~15 vCPUAntes do Karpenter, a empresa mantém:
12 × m6a.xlarge24 horas por dia.
O cluster precisa dessa capacidade durante picos, mas mantém boa parte dela ociosa durante várias horas.
Aplicando Karpenter
A arquitetura passa para:
Kubernetes
│
┌───────────┴───────────┐
│ │
APIs/Workers Infra crítica
│ │
▼ ▼
NodePool prod-app NodePool critical
│ │
Spot On-Demand
│ │
c/m families m/r familyAssim:
APIs e workers
Utilizam:
Spotporque são:
- replicados;
- stateless;
- tolerantes a interrupções;
- inicializam rapidamente.
Bancos ou componentes extremamente sensíveis
Podem permanecer:
On-Demandou em Managed Node Groups dedicados.
O objetivo não é executar 100% do cluster em Spot.
É utilizar Spot onde a arquitetura suporta interrupções.
Spot não significa simplesmente infraestrutura barata
Spot possui uma característica importante:
a capacidade pode ser interrompida.
Na AWS, determinadas interrupções Spot fornecem aproximadamente dois minutos de antecedência. O Karpenter possui integração para tratamento desses eventos e pode iniciar substituição enquanto executa cordon/drain do nó afetado.
Para isso, é importante configurar o mecanismo de interruption handling, incluindo a fila SQS utilizada pelo Karpenter.
Arquiteturalmente:
AWS interruption event
↓
EventBridge
↓
SQS
↓
Karpenter
↓
cordon / taint / drain
↓
novo NodeClaim
↓
nova EC2Spot deve ser utilizado principalmente em workloads preparados para desaparecer e retornar.
Estratégia Spot + On-Demand
Uma alternativa é permitir os dois modelos:
- key: karpenter.sh/capacity-type
operator: In
values:
- spot
- on-demandNos NodePools compatíveis com múltiplos modelos de capacidade, o Karpenter pode recorrer a alternativas conforme disponibilidade. A documentação atual também contempla capacidade reserved para reservas de capacidade específicas; isso não deve ser confundido com Reserved Instances tradicionais.
Outra estratégia, mais controlada, é possuir NodePools independentes:
prod-app-spot
prod-app-ondemande utilizar:
- labels;
- weights;
- affinities;
- topology spread.
A escolha depende da política de confiabilidade da organização.
Consolidação
Uma das funcionalidades mais importantes do Karpenter para FinOps é a consolidação.
Imagine:
Node A → 20% utilizado
Node B → 25% utilizado
Node C → 20% utilizadoTalvez seja possível mover os workloads para:
Node D → 70% utilizadoe remover os três anteriores.
O Karpenter pode avaliar:
Node A ──┐
Node B ──┼──► Node mais eficiente
Node C ──┘A documentação descreve consolidação através de operações de remoção ou substituição e avalia se workloads podem ser executados em capacidade existente ou em uma alternativa mais eficiente.
consolidateAfter
Não é necessário consolidar imediatamente.
Por exemplo:
consolidateAfter: 5msignifica que o Karpenter espera um período de estabilidade antes de considerar determinadas consolidações.
Isso é útil para workloads com comportamento variável.
Imagine:
10:00 → escala
10:02 → reduz
10:04 → escala
10:06 → reduzConsolidação muito agressiva pode gerar:
cria node
↓
remove node
↓
cria node
↓
remove nodeEsse comportamento é conhecido informalmente como node churn.
Um consolidateAfter adequado reduz movimentações desnecessárias.
Disruption Budgets
Uma proteção importante para produção é controlar quantos nodes o Karpenter pode interromper simultaneamente.
Exemplo:
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 5m
budgets:
- nodes: "10%"Assim, o Karpenter não executa indiscriminadamente grandes quantidades de interrupções voluntárias simultâneas.
NodePool Disruption Budgets limitam ações voluntárias relacionadas a mecanismos como consolidação e drift.
Essa proteção é especialmente importante quando existem dezenas ou centenas de nodes.
Produção: separando workloads
Uma arquitetura madura pode possuir:
EKS
│
┌───────────────┼────────────────┐
│ │ │
prod-app monitoring database
│ │ │
Spot Spot/OnDemand On-Demand
│ │ │
c/m m/r rE ainda:
ci-cd
│
Spot
│
c/mprod-app
Aplicações:
Node.js
Go
PHP
JavaPodem utilizar Spot quando possuem:
- múltiplas réplicas;
- readinessProbe;
- PDB;
- shutdown adequado;
- distribuição entre AZs.
CI/CD
Agents de build são ótimos candidatos para Spot quando jobs podem ser reexecutados.
Observabilidade
Pode utilizar estratégia híbrida.
Bancos
Stateful workloads exigem análise mais cuidadosa.
Karpenter pode executar workloads stateful, mas isso não significa automaticamente que um banco crítico deve ser colocado em capacidade Spot.
FinOps: requests são fundamentais
Considere:
resources:
requests:
cpu: "4"
memory: 8GiMas observabilidade mostra:
CPU P95: 700m
RAM P95: 2.5GiSe existirem dez pods:
Kubernetes enxerga:
40 vCPU
80 GiBmesmo que o consumo real esteja muito abaixo.
Karpenter provisionará capacidade considerando o que o scheduler precisa reservar.
Por isso, a sequência correta de otimização normalmente é:
Métricas
↓
Rightsizing
↓
Requests adequados
↓
Karpenter
↓
Consolidation
↓
SpotNão:
requests errados
↓
Karpenter
↓
"por que não economizou?"Observabilidade
Karpenter precisa ser monitorado como qualquer componente crítico da plataforma.
Ele disponibiliza métricas em formato Prometheus relacionadas a provisioning, NodeClaims, cloud provider, reconciliações e consolidação.
Uma stack típica:
Karpenter
│
├── metrics ──► Prometheus ──► Grafana
│
└── logs ─────► Loki ────────► GrafanaEntre os pontos importantes para acompanhar estão:
tempo para criação do node
NodeClaims pendentes
erros do cloud provider
falhas de provisioning
nodes NotReady
pods Pending
quantidade Spot x On-Demand
consolidações
interrupções
custo por NodePool
utilização CPU/memória
requests x utilizaçãoAlgumas métricas disponíveis incluem dados sobre erros e duração das operações do cloud provider, além de métricas relacionadas à consolidação.
Logs
Logs do controller podem ser analisados com:
kubectl logs \
-n kube-system \
-l app.kubernetes.io/name=karpenter \
--tail=200Para acompanhar em tempo real:
kubectl logs \
-n kube-system \
-l app.kubernetes.io/name=karpenter \
-fNo Loki, pode-se centralizar esses logs e criar painéis ou alertas para eventos como:
failed scheduling
insufficient capacity
NodeClaim failed
cloud provider error
unconsolidatableTroubleshooting
Pods permanecem Pending
Primeiro:
kubectl get pods -A --field-selector=status.phase=PendingDepois:
kubectl describe pod <pod>Observe a seção:
EventsErros comuns:
Insufficient cpu
Insufficient memory
untolerated taint
node affinity conflict
topology constraintsVerificar NodePools
kubectl get nodepoolDetalhes:
kubectl describe nodepool prod-appVerifique:
requirements
limits
conditions
disruptionVerificar NodeClaims
kubectl get nodeclaimsDepois:
kubectl describe nodeclaim <nodeclaim>Isso ajuda a identificar problemas entre:
Pod
↓
NodePool
↓
NodeClaim
↓
EC2Verificar EC2NodeClass
kubectl get ec2nodeclassE:
kubectl describe ec2nodeclass productionProblemas podem incluir:
subnet não encontrada
security group incorreto
AMI inválida
IAM insuficiente
capacity indisponívelEventos Kubernetes
Um dos comandos mais úteis:
kubectl get events -A \
--sort-by=.metadata.creationTimestampTambém:
kubectl get nodes \
-L karpenter.sh/nodepool,karpenter.sh/capacity-type,node.kubernetes.io/instance-typeIsso permite visualizar algo semelhante a:
NODE NODEPOOL CAPACITY INSTANCE
node-01 prod-app spot c6a.xlarge
node-02 prod-app spot m6a.large
node-03 critical on-demand m6i.xlargeSegurança
IAM
O Karpenter precisa criar e remover infraestrutura AWS.
Portanto, permissões IAM precisam seguir o princípio do menor privilégio.
Evite políticas como:
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}As permissões do controller e das instâncias devem possuir responsabilidades claramente separadas.
ServiceAccounts
Em EKS, prefira mecanismos de identidade específicos para workloads, como EKS Pod Identity ou IRSA conforme a arquitetura e versão adotadas.
Evite credenciais AWS hard coded em:
env:
AWS_ACCESS_KEY_ID: ...ou:
Secret:
access-key: ...quando identidade federada puder ser utilizada.
Isolamento de workloads
Use:
taints
tolerations
labels
nodeAffinity
NetworkPolicies
namespaces
RBACpara impedir que workloads sejam executados onde não deveriam.
Por exemplo:
database → nodes database
monitoring → nodes monitoring
applications → nodes prod-app
CI/CD → nodes ci-cdO Karpenter não substitui políticas de isolamento Kubernetes.
Disponibilidade
Economia não pode destruir disponibilidade.
Uma aplicação em Spot deveria considerar:
Deployment
│
├── replica 1 → AZ A
├── replica 2 → AZ B
└── replica 3 → AZ A/BUtilize:
topologySpreadConstraints:ou mecanismos equivalentes para evitar concentração desnecessária das réplicas.
Também considere:
PDB
readinessProbe
livenessProbe
startupProbe
terminationGracePeriodSeconds
graceful shutdownO que evitar
Fixar apenas um instance type
Evite:
- key: node.kubernetes.io/instance-type
operator: In
values:
- m6a.xlargesem uma necessidade real.
Isso reduz consideravelmente a flexibilidade.
Melhor trabalhar com requisitos:
- key: karpenter.k8s.aws/instance-category
operator: In
values:
- c
- mquando o workload permitir.
Colocar tudo em Spot
Não faça:
Kubernetes inteiro
↓
100% Spotapenas para maximizar a economia.
Classifique workloads por tolerância a interrupção.
Por exemplo:
| Workload | Estratégia possível |
|---|---|
| API stateless | Spot |
| Worker idempotente | Spot |
| CI/CD Agent | Spot |
| Processamento batch | Spot |
| Observabilidade | híbrido |
| Banco crítico | On-Demand |
| Componentes essenciais | On-Demand ou estratégia híbrida |
Essas são decisões arquiteturais, não regras absolutas.
Não ignorar requests
Evite:
resources: {}Em clusters com autoscaling, isso prejudica:
- scheduling;
- capacidade;
- bin packing;
- previsibilidade;
- FinOps.
Não utilizar AMI @latest indiscriminadamente em produção
Exemplo:
amiSelectorTerms:
- alias: al2023@latesté conveniente em ambientes de teste.
Em produção, uma atualização inesperada pode introduzir alterações em:
kernel
container runtime
kubelet
drivers
configuraçãoA recomendação da AWS é utilizar AMIs conhecidas e testadas em produção e validar novas versões em ambientes não produtivos antes da atualização.
Desenvolvimento x produção
Desenvolvimento
Pode ser aceitável:
100% Spot
consolidação agressiva
poucas réplicas
NodePools genéricos
expireAfter menorO principal objetivo pode ser custo.
Produção
Uma estratégia adequada tende a incluir:
Spot + On-Demand
múltiplas AZs
PDB
topology spread
interruption handling
AMIs fixadas
observabilidade
NodePool limits
disruption budgets
requests revisados
isolamento por workloadProdução deve priorizar equilíbrio entre:
custo
+
resiliência
+
performance
+
previsibilidadeExemplo de arquitetura de produção
AWS
│
EKS
│
┌────────────┼────────────┐
│ │ │
API Workers Database
│ │ │
HPA KEDA Stateful
│ │ │
└──────┬─────┘ │
│ │
▼ ▼
Pending Pods Critical Pods
│ │
▼ ▼
Karpenter NodePool
│ On-Demand
┌──────────┴──────────┐
│ │
Spot On-Demand
│ │
c/m families m/r familiesNesse modelo:
HPA
Escala APIs por métricas.
CPU
memory
custom metricsKEDA
Pode escalar workers através de:
RabbitMQ
Kafka
SQS
PrometheusKarpenter
Escala a infraestrutura quando os pods deixam de caber nos nodes existentes.
Portanto:
HPA/KEDA
↓
mais Pods
↓
Pods Pending
↓
Karpenter
↓
mais NodesE na redução:
demanda cai
↓
HPA/KEDA reduz Pods
↓
nodes ficam subutilizados
↓
Karpenter consolida
↓
EC2 removidaEssa interação é uma das formas mais eficientes de elasticidade Kubernetes.
Estratégia FinOps completa
Karpenter deve fazer parte de uma estratégia maior.
Uma arquitetura madura pode utilizar:
Prometheus
↓
análise utilização
↓
VPA recommendations / rightsizing
↓
requests corretos
↓
HPA/KEDA
↓
Karpenter
↓
Spot
↓
consolidation
↓
OpenCostO OpenCost pode complementar o ambiente mostrando custos por:
namespace
deployment
pod
label
NodeAssim é possível descobrir situações como:
Namespace A
requests CPU: 30 cores
CPU real: 6 coresNesse caso, trocar On-Demand por Spot pode reduzir custo, mas o problema estrutural continua existindo:
requests superdimensionados.FinOps eficiente começa pela demanda, não apenas pelo preço da infraestrutura.
Quando utilizar Karpenter
Karpenter é especialmente interessante quando existem:
- clusters EKS com demanda variável;
- muitos workloads;
- aplicações stateless;
- autoscaling frequente;
- utilização de Spot;
- necessidade de múltiplos tipos de EC2;
- workloads com requisitos diferentes;
- preocupação significativa com custos;
- crescimento imprevisível;
- ambientes com HPA ou KEDA.
Quanto maior a variação da demanda, maior tende a ser o benefício potencial.
Quando Karpenter pode não ser necessário
Nem todo cluster precisa de Karpenter.
Imagine:
3 nodes
3 aplicações
carga praticamente constanteSe o ambiente funciona continuamente com:
3 × m6i.largee raramente muda, um Managed Node Group pode ser suficiente.
Adicionar:
Karpenter
NodePools
NodeClaims
EC2NodeClasses
interruption handling
políticas de disruption
monitoramentoaumenta a complexidade operacional.
Nesse cenário, a economia talvez não justifique essa complexidade.
Karpenter tende a trazer maior valor para ambientes dinâmicos.
Checklist antes de utilizar Karpenter em produção
Antes da migração, valide:
- requests e limits revisados;
- aplicações replicadas;
- readiness e liveness probes configuradas;
- graceful shutdown;
- PodDisruptionBudgets;
- distribuição entre AZs;
- NodePools separados por responsabilidade;
- limites de CPU e memória;
- estratégia Spot/On-Demand;
- interruption handling;
- IAM seguindo menor privilégio;
- discos criptografados;
- AMIs controladas;
- métricas Prometheus;
- dashboards Grafana;
- logs centralizados;
- alertas para falha de provisioning;
- testes reais de interrupção de nodes.
Resumo prático
O Karpenter muda a lógica tradicional de autoscaling de infraestrutura Kubernetes.
Em vez de pensar apenas:
preciso aumentar meu grupo de t3.xlargepodemos pensar:
tenho estes pods
com estes requisitos
e preciso da capacidade mais adequada disponívelEssa diferença permite aproveitar melhor:
Spot
+
múltiplos instance types
+
múltiplas famílias
+
consolidação
+
autoscaling dinâmicoO fluxo final pode ser representado como:
Demanda aumenta
↓
HPA / KEDA
↓
Novos Pods
↓
Pods Pending
↓
Karpenter
↓
NodeClaim
↓
EC2 Spot / On-Demand
↓
Node Kubernetes
↓
Pods executandoQuando a demanda diminui:
menos Pods
↓
nodes subutilizados
↓
consolidação
↓
drain
↓
terminação EC2
↓
redução de custoConclusão
Karpenter não deve ser tratado apenas como uma ferramenta para criar EC2 automaticamente.
Seu maior benefício aparece quando ele faz parte de uma arquitetura Kubernetes projetada para elasticidade.
A redução de custo surge da combinação entre:
rightsizing
+
autoscaling de aplicações
+
provisionamento dinâmico
+
Spot
+
flexibilidade de instance types
+
consolidação
+
observabilidadeUm cluster que possui requests superdimensionados, aplicações incapazes de tolerar interrupções e NodePools excessivamente restritivos continuará sendo ineficiente mesmo utilizando Karpenter.
Por outro lado, quando aplicações são corretamente dimensionadas e arquitetadas para resiliência, o Karpenter permite que a infraestrutura acompanhe muito mais de perto a demanda real.
Para Platform Engineering, isso significa disponibilizar uma camada de infraestrutura capaz de oferecer diferentes perfis de capacidade:
applications → Spot
critical → On-Demand
batch → Spot
ci-cd → Spot
database → capacidade estávelPara SRE, significa combinar elasticidade com controles de disponibilidade, observabilidade e disruption.
Para FinOps, significa reduzir o intervalo entre capacidade provisionada e capacidade realmente necessária.
E esse é o ponto central do Karpenter: não reduzir custos simplesmente escolhendo máquinas mais baratas, mas permitir que o Kubernetes utilize a quantidade e o tipo de infraestrutura adequados para cada momento, mantendo performance, disponibilidade e confiabilidade como requisitos da arquitetura.
Artigos relacionados
Quer aplicar isso no seu ambiente?
Agende uma conversa de 30 minutos sem compromisso. Eu olho o seu cenário e digo por onde começar.
Agendar uma conversaReceba os novos artigos por e-mail
Um e-mail quando sair artigo novo sobre infraestrutura, backend e IA. Sem spam, cancele quando quiser.