SLOs e Error Budgets: a contabilidade da confiabilidade
Confiabilidade não é "servidor no ar". É ter uma definição clara e mensurável do que significa o sistema funcionar bem para o usuário — e honrá-la. SLOs (Service Level Objectives) são essa definição. Error budgets são o que transforma confiabilidade numa ferramenta de decisão de negócio.
A ideia central do SRE (Site Reliability Engineering) do Google: se você nunca está abaixo do SLO, você está sendo conservador demais — poderia lançar features mais rápido. Se está constantemente violando, você tem um problema de confiabilidade. O error budget é o saldo entre esses extremos. Neste módulo você vai do conceito à implementação real de alertas inteligentes.
SLI → SLO → SLA: a hierarquia
Como definir um bom SLI
SLI mede a experiência do usuário — não métricas de sistema. "CPU < 80%" não é um SLI. "% de requests completados em < 300ms" é.
| Tipo de SLI | Fórmula | Quando usar |
|---|---|---|
| Availability | successful_requests / total_requests | APIs, serviços HTTP — mede se o sistema está respondendo corretamente |
| Latency | requests_under_threshold / total_requests | UX-críticos — "% de buscas respondidas em < 200ms" |
| Throughput | requests_processed / target_rate | Pipelines de dados — "% de batches completados no tempo esperado" |
| Error rate | 1 - (error_requests / total_requests) | Alternativa a availability — foca em erros de aplicação, não infraestrutura |
| Quality | good_quality_responses / total_responses | Sistemas com degradação graciosa — recomendações com fallback ainda são "sucesso" |
# Definindo SLIs em Prometheus
# Métrica base: contador de requests por status
# Instrumentado pelo servidor HTTP automaticamente (ou manualmente):
from prometheus_client import Counter
REQUEST_COUNTER = Counter(
'http_requests_total',
'Total HTTP requests',
['method', 'handler', 'status_code']
)
# SLI de Availability: requests com 5xx são falhas
# PromQL:
availability_sli = """
sum(rate(http_requests_total{status_code!~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
"""
# SLI de Latência: requests abaixo de 300ms (histograma)
from prometheus_client import Histogram
REQUEST_LATENCY = Histogram(
'http_request_duration_seconds',
'HTTP request duration',
['handler'],
buckets=[0.1, 0.3, 0.5, 1.0, 2.0, 5.0]
)
# PromQL para latency SLI:
latency_sli = """
sum(rate(http_request_duration_seconds_bucket{handler="/checkout",le="0.3"}[5m]))
/
sum(rate(http_request_duration_seconds_count{handler="/checkout"}[5m]))
"""Error budget: o saldo de falha permitido
Se o SLO é 99.5% em 30 dias, o error budget é os 0.5% restantes — convertido em tempo absoluto:
Error budget como ferramenta de decisão: O budget não é punitivo — é um contrato entre produto e engenharia. Produto quer lançar features rápido (consome budget). Engenharia quer sistema confiável (protege budget). Quando o budget acabar, a conversa é objetiva: "matemática diz que precisamos parar de lançar e estabilizar".
Multi-window multi-burn rate alerts
O Google SRE Workbook definiu a estratégia de alertas mais eficaz para SLOs. A ideia: alertar quando a taxa de consumo do error budget é anormalmente alta — não só quando há erros.
Burn rate = taxa atual de consumo ÷ taxa normal esperada. Burn rate de 1 = consome o budget exatamente na janela. Burn rate de 2 = esgota o budget em metade do tempo.
| Alerta | Burn Rate | Janela Curta | Janela Longa | Severidade | Esgota budget em |
|---|---|---|---|---|---|
| P1 | 14.4× | 1h | 5min | PagerDuty | 2 dias |
| P2 | 6× | 6h | 30min | Slack urgente | 5 dias |
| P3 | 3× | 3 dias | 6h | Ticket/email | 10 dias |
| P4 | 1× | 30 dias | — | Weekly review | 30 dias (normal) |
# Prometheus alerting rules — multi-window multi-burn rate
# SLO: 99.5% availability no checkout em janela de 30 dias
groups:
- name: slo.checkout.availability
rules:
# SLI: proporção de requests bem-sucedidos
- record: job:slo_checkout_success_rate:ratio_rate5m
expr: |
sum(rate(http_requests_total{job="checkout",status!~"5.."}[5m]))
/
sum(rate(http_requests_total{job="checkout"}[5m]))
- record: job:slo_checkout_success_rate:ratio_rate1h
expr: |
sum(rate(http_requests_total{job="checkout",status!~"5.."}[1h]))
/
sum(rate(http_requests_total{job="checkout"}[1h]))
- record: job:slo_checkout_success_rate:ratio_rate6h
expr: |
sum(rate(http_requests_total{job="checkout",status!~"5.."}[6h]))
/
sum(rate(http_requests_total{job="checkout"}[6h]))
- record: job:slo_checkout_success_rate:ratio_rate3d
expr: |
sum(rate(http_requests_total{job="checkout",status!~"5.."}[3d]))
/
sum(rate(http_requests_total{job="checkout"}[3d]))
# P1: Burn rate 14.4× — incidente crítico
# Janela curta (5m) E janela longa (1h) devem satisfazer
# Reduz false positives de spikes breves
- alert: CheckoutSLOBurnRateCritical
expr: |
(
(1 - job:slo_checkout_success_rate:ratio_rate5m) > (14.4 * 0.005)
AND
(1 - job:slo_checkout_success_rate:ratio_rate1h) > (14.4 * 0.005)
)
for: 2m
labels:
severity: critical
team: checkout
annotations:
summary: "Checkout SLO: burn rate crítico (14.4×)"
description: |
Error rate atual: {{ $value | humanizePercentage }}
Budget consumido em: ~2 dias se continuar.
Runbook: https://wiki/checkout-slo-runbook
# P2: Burn rate 6× — degradação significativa
- alert: CheckoutSLOBurnRateHigh
expr: |
(
(1 - job:slo_checkout_success_rate:ratio_rate30m) > (6 * 0.005)
AND
(1 - job:slo_checkout_success_rate:ratio_rate6h) > (6 * 0.005)
)
for: 15m
labels:
severity: warning
team: checkout
annotations:
summary: "Checkout SLO: burn rate elevado (6×)"
# P3: Burn rate 3× — degradação lenta (só janela longa)
- alert: CheckoutSLOBurnRateSlow
expr: |
(1 - job:slo_checkout_success_rate:ratio_rate3d) > (3 * 0.005)
for: 1h
labels:
severity: warning
team: checkout
annotations:
summary: "Checkout SLO: degradação silenciosa nos últimos 3 dias"SLO vs uptime puro: por que 99.9% não é suficiente como alvo
"Uptime de 99.9%" (three-nines) parece ótimo mas disfarça problemas reais:
| Uptime | Downtime por mês | Downtime por ano | Adequado para |
|---|---|---|---|
| 99% | 7h 18min | 3d 15h | Sites internos, ferramentas não-críticas |
| 99.5% | 3h 39min | 1d 19h | APIs com baixa criticidade de negócio |
| 99.9% | 43 min | 8h 45min | APIs de produção consumer-grade |
| 99.95% | 21 min | 4h 22min | Plataformas B2B com SLA financeiro |
| 99.99% | 4 min | 52 min | Serviços de pagamento, infraestrutura crítica |
| 99.999% | 26 seg | 5 min | Telecoms, sistemas de segurança — requer arquitetura especial |
Cuidado com SLOs "aspiracionais": SLO de 99.99% para um serviço que tem deploy manual semanal e sem canary releases é impossível de manter. Um deploy ruim já come 4 minutos do budget anual. SLO deve refletir a arquitetura real — não o desejo. Comece conservador (99.5%) e aperte conforme a maturidade aumenta.
Toil budget: o outro lado do SRE
Toil (do inglês "lida") é o trabalho manual, repetitivo, tático que não tem valor de engenharia duradouro. A regra do SRE: máximo 50% do tempo de trabalho.
| É Toil | NÃO é Toil |
|---|---|
| Restart manual de serviço após crash | Criar runbook automatizado para o crash |
| Aprovar manualmente deploys um a um | Implementar pipeline de deploy automático com canary |
| Responder alertas de disco cheio ad hoc | Implementar auto-scaling ou limpeza automática de logs |
| Atualizar configurações em 20 servidores manualmente | Criar playbook Ansible/Terraform para a mudança |
| Resetar senha de usuário toda semana via ticket | Implementar self-service de reset de senha |
📋 Time de SRE gasta 70% do tempo respondendo alertas e fazendo tarefas manuais recorrentes
Com 70% em toil, o time não tem capacidade para automação. O ciclo vicioso: mais toil → menos automação → mais toil. Quebrar o ciclo requer alocar Sprint completo para eliminar as 3-5 maiores fontes de toil com automação (runbooks automatizados, auto-healing, alertas melhores).
Alt: —
Alt: —
Alt: —
Error budget policy: o contrato entre produto e SRE
Uma política de error budget documenta o que acontece quando o budget é consumido. Sem uma política escrita, a decisão de pausar features vs continuar é sempre política — não dados.
# Error Budget Policy — Checkout Service
Documento: v1.2 | Owner: SRE Platform | Aprovado: 2024-01-10
## SLO
- Availability: ≥ 99.5% em janela rolling de 30 dias
- Latência P95: ≤ 500ms em janela rolling de 30 dias
## Estados do Error Budget
### 🟢 Verde (> 50% budget restante)
- Time pode lançar features normalmente
- Deploy a qualquer momento dentro da janela de mudança (Ter-Qui 10h-16h)
- Post-deployment monitoring: 30 min
### 🟡 Amarelo (10-50% budget restante)
- Deploy limitado: máximo 1 feature/semana
- Código de alto risco (mudança de DB, integração externa) requer revisão adicional
- SRE co-aprova PRs de infra
- Post-deployment monitoring: 2h com rollback automático disponível
### 🔴 Vermelho (< 10% budget restante)
- Feature freeze: ZERO novos deploys de features
- Apenas hotfixes de confiabilidade e segurança crítica
- Daily standup inclui SRE lead obrigatoriamente
- Time de produto notificado e impacto comunicado em roadmap
- Revisão de causa raiz obrigatória antes de retornar ao amarelo
### 🚨 Budget esgotado (0%)
- Freeze total: apenas rollbacks e hotfixes de segurança P0
- Post-mortem obrigatório em 48h
- Plano de ação com milestones em 7 dias
- Escalonamento para VP Engineering se não resolver em 14 dias
## Exceções
- Patches de segurança crítica (CVE CVSS ≥ 9.0): sempre permitidos
- Releases regulatórios (compliance, LGPD): aprovação explícita de VP Eng + CTOGrafana: dashboard de SLO
# Grafana dashboard: SLO tracker (JSON model resumido)
# Panels principais para um dashboard de SLO:
panels:
# 1. SLO Gauge — "Estamos dentro do SLO?"
- title: "SLO Compliance (30d)"
type: gauge
query: |
sum(increase(http_requests_total{job="checkout",status!~"5.."}[30d]))
/
sum(increase(http_requests_total{job="checkout"}[30d]))
thresholds: [0.995: green, 0.99: yellow, 0: red]
# 2. Error Budget Remaining
- title: "Error Budget Restante"
type: stat
query: |
(
sum(increase(http_requests_total{job="checkout",status!~"5.."}[30d]))
/
sum(increase(http_requests_total{job="checkout"}[30d]))
- 0.995
) / 0.005 * 100
unit: percent
# 3. Burn Rate (1h)
- title: "Burn Rate (1h)"
type: timeseries
query: |
(1 - job:slo_checkout_success_rate:ratio_rate1h) / 0.005
alert_threshold: 14.4
# 4. Error rate breakdown por status code
- title: "Errors por Status Code"
type: timeseries
query: |
sum by(status_code) (
rate(http_requests_total{job="checkout",status=~"5.."}[5m])
)Q&A — perguntas típicas de entrevista SRE
Take-aways:
Próximos passos sugeridos
Discussão
Carregando…