Dimensionamento automático (autoscaling) de aplicações
1. Fundamentos do Autoscaling
O dimensionamento automático, ou autoscaling, é uma técnica essencial para manter aplicações modernas responsivas, econômicas e resilientes. Em vez de provisionar recursos fixos para o pico de carga — o que gera desperdício em horários de baixa demanda — o autoscaling ajusta dinamicamente a capacidade computacional com base em métricas em tempo real.
As principais métricas utilizadas para decisões de escala incluem:
- CPU: utilização percentual média dos núcleos
- Memória: consumo de RAM em relação ao limite definido
- Latência: tempo de resposta de requisições (p95, p99)
- Filas: profundidade de filas de mensagens ou jobs pendentes
Existem duas abordagens fundamentais:
- Escalabilidade vertical (scale-up): aumenta os recursos de uma instância existente (mais CPU, mais RAM). Tem limites físicos e geralmente exige reinicialização.
- Escalabilidade horizontal (scale-out): adiciona ou remove instâncias completas. É a abordagem preferida em arquiteturas de microsserviços e contêineres, pois oferece elasticidade praticamente ilimitada.
2. Estratégias de Dimensionamento Horizontal (HPA)
No ecossistema Kubernetes, o Horizontal Pod Autoscaler (HPA) é a implementação padrão para scale-out automático. O HPA monitora métricas coletadas pelo Metrics Server e ajusta o número de réplicas de um Deployment, StatefulSet ou ReplicaSet.
Exemplo de manifesto HPA baseado em CPU:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-gateway-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-gateway
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60
O algoritmo do HPA calcula o número desejado de réplicas pela fórmula:
desiredReplicas = ceil[currentReplicas * (currentMetricValue / desiredMetricValue)]
Para evitar oscilações (thrashing), o HPA implementa histerese: após uma decisão de escala, ele aguarda um período de estabilização antes de recalcular. No exemplo acima, stabilizationWindowSeconds: 300 impede reduções abruptas.
Métricas customizadas podem ser expostas via Custom Metrics API, permitindo escalar com base em requisições por segundo, latência ou qualquer métrica de negócio.
3. Dimensionamento Vertical (VPA) e Ajuste de Recursos
Enquanto o HPA ajusta o número de pods, o Vertical Pod Autoscaler (VPA) ajusta os limites de CPU e memória de cada pod individualmente. O VPA opera em três modos:
- Off: apenas recomenda valores ideais, sem aplicar alterações
- Auto: atualiza automaticamente os recursos e reinicia os pods
- Initial: aplica recomendações apenas na criação do pod
Exemplo de VPA em modo de recomendação:
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: recomendador-vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: recomendador
updatePolicy:
updateMode: "Auto"
resourcePolicy:
containerPolicies:
- containerName: '*'
minAllowed:
cpu: 100m
memory: 128Mi
maxAllowed:
cpu: 2
memory: 4Gi
A principal limitação do VPA é que alterações nos limites de recursos exigem reinicialização do pod, o que pode causar downtime se não houver réplicas suficientes. Por isso, recomenda-se usar VPA em conjunto com HPA: o VPA ajusta os limites individualmente, enquanto o HPA gerencia a quantidade de réplicas.
4. Dimensionamento Baseado em Eventos e Fila
Para aplicações orientadas a eventos, o Kubernetes Event-Driven Autoscaling (KEDA) oferece uma abordagem mais refinada. KEDA permite escalar deployments com base em métricas de fontes externas como filas de mensagens, streams e bancos de dados.
Exemplo de ScaledObject para Kafka:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: processador-pedidos
spec:
scaleTargetRef:
name: processador-pedidos
minReplicaCount: 1
maxReplicaCount: 50
triggers:
- type: kafka
metadata:
bootstrapServers: kafka-cluster:9092
topic: pedidos-novos
lagThreshold: "10"
consumerGroup: processadores
allowIdleConsumers: "false"
KEDA suporta dezenas de gatilhos: Kafka, RabbitMQ, AWS SQS, Azure Queue Storage, Redis Streams, Prometheus, CronJobs e muitos outros. Cada trigger define uma métrica (como profundidade de fila) e um limiar que dispara o escalonamento.
5. Políticas de Escalonamento e Controle de Pico
Para evitar oscilações indesejadas, é crucial configurar políticas de scale-up e scale-down adequadas. As principais estratégias incluem:
- Scale-up rápido: aumentar réplicas agressivamente quando a métrica ultrapassa o limiar, pois picos repentinos precisam de resposta imediata.
- Scale-down gradual: reduzir réplicas lentamente para evitar "gangorra" (thrashing) quando a carga oscila.
- Cooldown: período mínimo entre decisões de escala consecutivas.
- Limites mínimos e máximos: garantir disponibilidade mínima e evitar custos excessivos.
Exemplo de política no HPA:
behavior:
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
- type: Pods
value: 4
periodSeconds: 15
selectPolicy: Max
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60
Aqui, o scale-up pode adicionar até 100% das réplicas atuais ou 4 pods (o que for maior) a cada 15 segundos, enquanto o scale-down reduz no máximo 10% a cada 60 segundos, com janela de estabilização de 5 minutos.
6. Monitoramento e Métricas para Decisões de Escala
A qualidade do autoscaling depende diretamente da qualidade das métricas coletadas. O ecossistema padrão inclui:
- Metrics Server: coleta uso de CPU e memória de cada pod e nó
- Prometheus: armazena métricas customizadas e históricas
- Custom Metrics API: expõe métricas arbitrárias para o HPA
Para orientar decisões de escala, é fundamental definir SLIs (Service Level Indicators) e SLOs (Service Level Objectives). Exemplos:
- SLI: latência p99 de requisições na API
- SLO: 99% das requisições devem ter latência < 200ms
- Gatilho de escala: quando a latência p99 ultrapassa 150ms, aumentar réplicas
Exemplo de métrica customizada via Prometheus Adapter:
apiVersion: v1
kind: ConfigMap
metadata:
name: adapter-config
namespace: custom-metrics
data:
config.yaml: |
rules:
- seriesQuery: 'http_requests_total{namespace!="",pod!=""}'
resources:
overrides:
namespace: {resource: "namespace"}
pod: {resource: "pod"}
name:
matches: "http_requests_total"
as: "requests_per_second"
metricsQuery: 'rate(http_requests_total{<<.LabelMatchers>>}[2m])'
Dashboards no Grafana e alertas no Prometheus Alertmanager complementam a visibilidade, permitindo detectar comportamentos anômalos no escalonamento.
7. Desafios e Boas Práticas em Produção
Implementar autoscaling em produção exige atenção a diversos desafios:
Inicialização lenta (startup): pods novos podem levar segundos ou minutos para ficar prontos. Durante esse período, a carga pode não ser absorvida. Soluções incluem:
- Usar probes de readiness e liveness bem configuradas
- Pré-aquecer pools de conexão e caches
- Considerar scale-up antecipado com base em previsão de carga
Picos frios (cold starts): quando o sistema fica ocioso por muito tempo e de repente recebe carga, as primeiras requisições podem sofrer latência alta. Estratégias:
- Manter um número mínimo de réplicas sempre ativas
- Usar escalonamento baseado em schedule para horários de pico conhecidos
Dependências externas: bancos de dados, filas e APIs externas podem se tornar gargalos. Escalar a aplicação sem escalar os recursos de dependência pode piorar a situação. Monitorar a capacidade dos sistemas downstream é essencial.
Testes de resiliência: simular picos de carga com ferramentas como Locust, k6 ou vegeta ajuda a validar as políticas de escala. Testes de caos (Chaos Engineering) como os do Chaos Mesh podem revelar pontos fracos na configuração.
Exemplo de teste de carga simples com k6:
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 100 },
{ duration: '5m', target: 100 },
{ duration: '2m', target: 0 },
],
};
export default function () {
const res = http.get('https://api.exemplo.com/health');
check(res, { 'status 200': (r) => r.status === 200 });
sleep(1);
}
Referências
- Kubernetes Horizontal Pod Autoscaling - Documentação Oficial — Guia completo sobre configuração e funcionamento do HPA no Kubernetes.
- KEDA - Documentação Oficial — Referência completa sobre event-driven autoscaling com KEDA, incluindo todos os triggers suportados.
- Vertical Pod Autoscaler - GitHub — Repositório oficial do VPA com exemplos, modos de operação e boas práticas.
- Prometheus Adapter para Métricas Customizadas — Projeto que expõe métricas do Prometheus para o HPA via Custom Metrics API.
- k6 - Teste de Carga Open Source — Ferramenta para simulação de carga e validação de políticas de autoscaling em aplicações.