SLO e error budget: confiabilidade de APIs com Prometheus

SLI (Service Level Indicator), SLO (Service Level Objective) e error budget são o vocabulário do Site Reliability Engineering para medir confiabilidade de forma objetiva. Em vez de "o sistema está lento", você diz "nosso P99 de latência está em 850ms e nosso SLO é 500ms, 68% do error budget foi consumido este mês".

Conceitos: SLI, SLO e error budget

Definir os três conceitos e como se relacionam:

bash
# SLI (Service Level Indicator):
# → A métrica que mede o comportamento do serviço
# Exemplos:
#   Disponibilidade: % de requisições bem-sucedidas (status 2xx/3xx)
#   Latência: % de requisições que respondem em < 500ms
#   Throughput: requisições processadas por segundo

# SLO (Service Level Objective):
# → A meta que você quer atingir para o SLI
# Exemplos:
#   Disponibilidade: 99.9% das requisições devem ter status 2xx/3xx
#   Latência: 95% das requisições devem responder em < 500ms

# Error budget:
# → O quanto de "não conformidade" é permitido
# 99.9% de disponibilidade = 0.1% de erro permitido
# Em 30 dias: 0.001 × 30 × 24 × 60 = 43 minutos de downtime permitido
# Em 7 dias:  0.001 × 7 × 24 × 60 =  10 minutos de downtime permitido

# Quando o error budget acaba:
# → Parar novos deploys até o fim do período
# → Focar em confiabilidade, não em features
# → Isso alinha incentivos entre Dev e Ops

# Conversão disponibilidade → tempo de downtime por mês:
# 99.0%  → 7.2h
# 99.5%  → 3.6h
# 99.9%  → 43min  (three nines — padrão para a maioria das SaaS B2B)
# 99.99% → 4.3min (four nines — para serviços críticos)

Calcular SLI de disponibilidade com Prometheus

Queries PromQL para medir disponibilidade em tempo real:

bash
# SLI de disponibilidade — % de requisições bem-sucedidas:
# "Boas" = 2xx e 3xx | "Ruins" = 5xx
# (4xx geralmente são erros do cliente, não do serviço)

# Taxa de disponibilidade nas últimas 24h:
sum(rate(http_request_duration_seconds_count{status_code!~"5.."}[24h]))
/
sum(rate(http_request_duration_seconds_count[24h]))

# Error rate (1 - disponibilidade):
sum(rate(http_request_duration_seconds_count{status_code=~"5.."}[24h]))
/
sum(rate(http_request_duration_seconds_count[24h]))

# SLI de latência — % de requisições dentro do SLO de tempo:
# SLO: 95% das requisições em < 500ms
sum(rate(http_request_duration_seconds_bucket{le="0.5"}[1h]))
/
sum(rate(http_request_duration_seconds_count[1h]))

# Janela deslizante de 30 dias (mais precisa para error budget mensal):
sum(rate(http_request_duration_seconds_count{status_code!~"5.."}[30d]))
/
sum(rate(http_request_duration_seconds_count[30d]))

# Recording rule para pré-computar (performance):
# rules/slo.yml:
# - record: job:sli_disponibilidade:ratio_rate1h
#   expr: |
#     sum(rate(http_request_duration_seconds_count{status_code!~"5.."}[1h]))
#     / sum(rate(http_request_duration_seconds_count[1h]))

Calcular e monitorar o error budget

Acompanhar quanto do orçamento de erros foi consumido:

yaml
# Recording rules para SLO — definir uma vez e usar em múltiplos lugares:
# prometheus/rules/slo.yml:
groups:
  - name: slo-nodejs-api
    interval: 1m
    rules:
      # SLI: taxa de sucesso (janelas de tempo diferentes para alertas):
      - record: job:http_sli_sucesso:rate5m
        expr: |
          sum(rate(http_request_duration_seconds_count{job="nodejs-api",status_code!~"5.."}[5m]))
          / sum(rate(http_request_duration_seconds_count{job="nodejs-api"}[5m]))

      - record: job:http_sli_sucesso:rate1h
        expr: |
          sum(rate(http_request_duration_seconds_count{job="nodejs-api",status_code!~"5.."}[1h]))
          / sum(rate(http_request_duration_seconds_count{job="nodejs-api"}[1h]))

      - record: job:http_sli_sucesso:rate30d
        expr: |
          sum(rate(http_request_duration_seconds_count{job="nodejs-api",status_code!~"5.."}[30d]))
          / sum(rate(http_request_duration_seconds_count{job="nodejs-api"}[30d]))

# Calcular error budget consumido:
# SLO_TARGET = 0.999 (99.9%)
# error_budget_total = 1 - SLO_TARGET = 0.001
# error_budget_consumido = (1 - SLI) / (1 - SLO_TARGET) * 100

# Query no Grafana — % de error budget consumido no mês:
# (1 - job:http_sli_sucesso:rate30d) / (1 - 0.999) * 100

# Exemplo de resultado:
# SLI últimos 30d = 0.9995 (99.95%)
# Error budget consumido = (1 - 0.9995) / (1 - 0.999) * 100 = 50%
# → 50% do budget foi consumido, 50% resta

Alertas baseados em burn rate do error budget

Detectar quando o error budget está sendo consumido rápido demais:

yaml
# Alertas de burn rate — alertar quando o budget está sendo queimado em ritmo que,
# se mantido, vai esgotar o budget antes do fim do mês.

# burn_rate = taxa atual de erro / taxa de erro permitida pelo SLO
# Se burn_rate = 1: consumindo budget exatamente no ritmo
# Se burn_rate = 10: consumindo 10x mais rápido → budget esgota em 3 dias (30/10)

# SLO = 99.9% → error_rate_permitida = 0.001

# alerts/slo.yml:
groups:
  - name: slo-burn-rate
    rules:
      # CRÍTICO: burn rate > 14.4x nas últimas 1h
      # (esgota o budget mensal em ~2 dias)
      - alert: SloBurnRateCritico
        expr: |
          (1 - job:http_sli_sucesso:rate5m) / 0.001 > 14.4
          AND
          (1 - job:http_sli_sucesso:rate1h) / 0.001 > 14.4
        for: 2m
        labels:
          severity: critical
          slo: disponibilidade
        annotations:
          summary: "SLO: burn rate crítico: budget esgotará em ~2 dias"
          description: |
            Burn rate atual: {{ $value | printf "%.1f" }}x.
            Error rate: {{ with query "1 - job:http_sli_sucesso:rate5m" }}{{ . | first | value | humanizePercentage }}{{ end }}.

      # WARNING: burn rate > 6x nas últimas 6h
      # (esgota o budget mensal em ~5 dias)
      - alert: SloBurnRateAlto
        expr: |
          (1 - job:http_sli_sucesso:rate5m) / 0.001 > 6
        for: 15m
        labels:
          severity: warning
          slo: disponibilidade
        annotations:
          summary: "SLO: burn rate elevado: atenção ao error budget"

Dashboard de SLO no Grafana

Painel visual de SLO com semáforo e histórico de compliance:

bash
# Painéis Grafana para SLO:

# 1. Stat panel — SLI atual (com threshold colorido):
# Query: job:http_sli_sucesso:rate30d * 100
# Thresholds:
#   verde: >= 99.9 (dentro do SLO)
#   amarelo: 99.5 a 99.9 (dentro do budget mas atenção)
#   vermelho: < 99.5 (violação do SLO)
# Unit: percent

# 2. Stat panel — Error budget restante %:
# Query: (1 - (1 - job:http_sli_sucesso:rate30d) / 0.001) * 100
# Thresholds:
#   verde: > 50%
#   amarelo: 10% a 50%
#   vermelho: < 10%

# 3. Time series — Burn rate ao longo do tempo:
# Query: (1 - job:http_sli_sucesso:rate5m) / 0.001
# Linha de referência em y=1 (taxa normal) e y=6 (alerta)

# 4. Time series — SLI ao longo do mês (comparar com SLO):
# Query: job:http_sli_sucesso:rate1h * 100
# Linha de referência em y=99.9 (SLO target)

# Importar template pronto: Grafana ID 14348 — SLO Dashboard

# Documentar o SLO em arquivo de configuração:
# slos/nodejs-api.yml:
# service: nodejs-api
# slos:
#   - name: disponibilidade
#     indicator: taxa de requisições 2xx/3xx
#     target: 99.9
#     window: 30d
#   - name: latencia
#     indicator: P95 < 500ms
#     target: 95.0
#     window: 30d

Não quer configurar manualmente?

Implante o VPS para Node.js em menos de 5 minutos com a Runstack. Infraestrutura da OPEN Datacenter, com servidores em São Paulo.

Perguntas frequentes

99.9% (três noves) é o padrão para a maioria das SaaS B2B, 43 minutos de downtime por mês, aceitável para a maioria dos clientes empresariais. 99.5% é razoável para produtos em crescimento com menos pressão de SLA contratual. 99.99% requer infraestrutura com redundância ativa e muito investimento, vale apenas quando downtime tem custo financeiro direto altíssimo. Dica: comece medindo a disponibilidade real antes de definir metas, você pode descobrir que já está em 99.95% ou que está em 99.2%.

Alertar quando a disponibilidade cai abaixo de 99.9% na última hora detecta o problema, mas pode ser tarde demais. Burn rate detecta quando o problema está sendo criado, se você está consumindo o budget 14x mais rápido que o normal, você vai esgotar o orçamento mensal em 2 dias, mesmo que ainda esteja "acima do SLO" no momento. Isso permite agir preventivamente, não reativamente. O Alertmanager alerta fast (curta janela) + slow (janela longa) para evitar falsos positivos.

Depende da natureza do 4xx. 401 (não autenticado): geralmente não é falha do serviço. 400 (input inválido do cliente): não é falha do serviço. 429 (rate limit): pode ser incluído dependendo do contexto, se o rate limit é por design, não inclua; se está limitando usuários legítimos inesperadamente, inclua. 404 para endpoints que deveriam existir: pode indicar problema. Convenção common: excluir 4xx do SLI de disponibilidade, mas incluí-los no SLI de latência (latência de erros também importa).

Traduza para linguagem de negócio: "99.9% de disponibilidade" → "a plataforma pode ficar indisponível por no máximo 43 minutos por mês". Para error budget: "temos 50% do budget restante" → "ainda temos capacidade para 22 minutos de problemas este mês". Crie um dashboard público (link no Slack) com semáforo verde/amarelo/vermelho. Report mensal para gestão: "compliance do SLO: 100%, error budget consumido: 15%". Isso torna confiabilidade uma conversa objetiva, não subjetiva.

Sim, especialmente para serviços que outros serviços dependem. Um serviço de autenticação interno com SLO de 99.95% impacta todos os serviços que dependem dele. Para definir: identifique as dependências críticas (serviços que, se falharem, impactam o cliente final), defina SLOs proporcionais à criticidade, e configure alertas de burn rate. Para serviços não-críticos (tarefas assíncronas, jobs de relatório): SLOs menos rigorosos são adequados.