Como configurar autoscaling baseado em métricas customizadas no Kubernetes

1. Fundamentos do Autoscaling no Kubernetes

O Kubernetes oferece três mecanismos principais de autoscaling: o Horizontal Pod Autoscaler (HPA), que ajusta o número de réplicas de pods; o Vertical Pod Autoscaler (VPA), que ajusta recursos de CPU e memória dos pods; e o Cluster Autoscaler, que dimensiona o número de nós do cluster.

O HPA padrão utiliza métricas de recursos como CPU e memória, coletadas pelo Metrics Server. No entanto, essas métricas são insuficientes para cenários onde o comportamento da aplicação depende de indicadores mais específicos, como:

  • Tamanho de filas de mensagens (RabbitMQ, Kafka)
  • Latência de requisições HTTP
  • Throughput de processamento por segundo
  • Número de conexões simultâneas ativas

Para esses casos, o Kubernetes permite o uso de métricas customizadas, que podem ser expostas pela própria aplicação ou por sistemas externos, e consumidas pelo HPA para decisões de escalonamento mais precisas.

2. Arquitetura de Métricas Customizadas

A arquitetura típica envolve três componentes principais:

  1. Metrics Server: Coleta métricas padrão de recursos (CPU/memória) de cada nó e pod.
  2. Prometheus Adapter: Implementa a API de métricas customizadas do Kubernetes, traduzindo consultas PromQL em métricas que o HPA pode consumir.
  3. KEDA (Kubernetes Event-driven Autoscaling): Alternativa que simplifica o autoscaling baseado em eventos de fontes como Kafka, RabbitMQ, AWS SQS, entre outras.

O fluxo de dados segue esta sequência:

Aplicação → Endpoint /metrics (Prometheus) → Prometheus Server → Prometheus Adapter → API de Métricas Customizadas → HPA

A API de métricas customizadas se divide em três tipos:

  • Resource Metrics: CPU e memória (Metrics Server)
  • Custom Metrics: Métricas da própria aplicação, acessíveis via custom.metrics.k8s.io
  • External Metrics: Métricas de sistemas externos ao cluster, via external.metrics.k8s.io

3. Instalação e Configuração do Prometheus Adapter

Primeiro, certifique-se de que o Prometheus está instalado no cluster. Caso não esteja, utilize o Helm para instalar:

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus prometheus-community/prometheus --namespace monitoring --create-namespace

Em seguida, instale o Prometheus Adapter:

helm install prometheus-adapter prometheus-community/prometheus-adapter \
  --namespace monitoring \
  --set prometheus.url=http://prometheus-server.monitoring.svc \
  --set prometheus.port=80

Para mapear queries PromQL para métricas customizadas, edite o values.yaml do adapter:

rules:
  default: false
  custom:
  - seriesQuery: '{__name__=~"http_requests_total"}'
    resources:
      overrides:
        namespace:
          resource: namespace
        pod:
          resource: pod
    name:
      matches: "^(.*)_total$"
      as: "${1}_per_second"
    metricsQuery: 'rate(<<.Series>>{<<.LabelMatchers>>}[2m])'

Esse exemplo converte a métrica http_requests_total em uma taxa por segundo, acessível como http_requests_per_second.

4. Criando Métricas Customizadas na Aplicação

Sua aplicação precisa expor métricas no formato OpenMetrics. Exemplo em Node.js usando a biblioteca prom-client:

const client = require('prom-client');
const gauge = new client.Gauge({
  name: 'app_queue_size',
  help: 'Current queue size of pending messages',
  labelNames: ['queue_name']
});

// Atualizar o valor periodicamente
setInterval(() => {
  gauge.set({ queue_name: 'orders' }, getCurrentQueueSize());
}, 5000);

// Endpoint /metrics
app.get('/metrics', async (req, res) => {
  res.set('Content-Type', client.register.contentType);
  res.end(await client.register.metrics());
});

Boas práticas para evitar explosão de séries temporais:

  • Limitar cardinalidade de labels (evitar user_id, request_id)
  • Usar labels com valores previsíveis e controlados
  • Preferir métricas de taxa (rate()) sobre contadores absolutos

Teste localmente:

curl http://localhost:3000/metrics | grep app_queue_size

5. Configuração do HPA com Métricas Customizadas

O HPA pode usar três tipos de métricas: Pods (média por pod), Object (valor absoluto de um objeto) e External (métricas externas ao cluster).

Exemplo prático: autoscaling baseado em tamanho de fila (tipo Object, referenciando um serviço):

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: worker-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: worker
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Object
    object:
      metric:
        name: app_queue_size
      describedObject:
        apiVersion: v1
        kind: Service
        name: worker-service
      target:
        type: AverageValue
        averageValue: 100
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 10
        periodSeconds: 60

Neste exemplo, o HPA mantém a média de mensagens na fila em torno de 100. Se o valor ultrapassar, mais pods são criados; se ficar abaixo, pods são removidos gradualmente.

6. Estratégias Avançadas e Boas Práticas

Múltiplas métricas no mesmo HPA

É possível combinar métricas de CPU com métricas customizadas:

metrics:
- type: Resource
  resource:
    name: cpu
    target:
      type: Utilization
      averageUtilization: 70
- type: Pods
  pods:
    metric:
      name: app_queue_size
    target:
      type: AverageValue
      averageValue: 200

O HPA escolhe a métrica que requer mais réplicas em cada ciclo de avaliação.

Configuração de cooldown e stabilization

Para evitar thrashing (oscilação rápida entre escalar para cima e para baixo), configure:

behavior:
  scaleDown:
    stabilizationWindowSeconds: 300
    policies:
    - type: Percent
      value: 10
      periodSeconds: 60
  scaleUp:
    stabilizationWindowSeconds: 0
    policies:
    - type: Percent
      value: 100
      periodSeconds: 15

Isso permite escalonamento rápido para cima, mas reduz gradualmente para baixo.

Monitoramento do comportamento

Use kubectl describe hpa para ver eventos recentes e condições:

kubectl describe hpa worker-hpa

Exiba métricas em tempo real com:

kubectl get hpa worker-hpa --watch

7. Troubleshooting e Validação

Verifique se a API de métricas customizadas está disponível:

kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1 | jq .

Teste uma consulta específica:

kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/services/worker-service/app_queue_size" | jq .

Erros comuns e soluções:

  • Métrica não encontrada: Verifique se a query PromQL está correta e se as labels correspondem
  • Labels ausentes: O Prometheus Adapter precisa de labels namespace e pod para métricas do tipo Pods
  • Timeout na consulta: Aumente o tempo limite no adapter ou otimize a query PromQL

Para testes de carga simulados, use ferramentas como hey ou wrk:

hey -n 10000 -c 50 http://worker-service:3000/process

8. Integração com CICD e Observabilidade

No pipeline de CI/CD, adicione validações antes do deploy:

# Verificar se a métrica customizada existe
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/staging/services/worker-service/app_queue_size"

# Validar o YAML do HPA
kubectl apply --dry-run=client -f hpa.yaml

Configure alertas no Prometheus para falhas de autoscaling:

groups:
- name: autoscaling-alerts
  rules:
  - alert: HPAStuck
    expr: kube_horizontalpodautoscaler_spec_target_replicas != kube_horizontalpodautoscaler_status_current_replicas
    for: 15m
    labels:
      severity: warning
    annotations:
      summary: "HPA {{ $labels.horizontalpodautoscaler }} não está convergindo"

Documente um runbook para incidentes:

Runbook: Falha no Autoscaling Baseado em Métricas Customizadas

1. Verificar disponibilidade do Prometheus Adapter:
   kubectl get pods -n monitoring | grep prometheus-adapter

2. Verificar logs do adapter:
   kubectl logs -n monitoring deployment/prometheus-adapter

3. Testar consulta direta à API custom metrics:
   kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1

4. Verificar se a aplicação está expondo métricas:
   kubectl port-forward pod/worker-pod 3000:3000
   curl http://localhost:3000/metrics

5. Reiniciar o Prometheus Adapter se necessário:
   kubectl rollout restart deployment/prometheus-adapter -n monitoring

Referências