SRE no Azure: implementando SLIs, SLOs e Error Budgets com Azure Monitor
Você já configurou alta disponibilidade, implementou observabilidade, e até validou a resiliência com Engenharia de Caos. Mas como responder com confiança à pergunta mais importante que o negócio faz: "nosso sistema é confiável o suficiente?"
Essa é a pergunta central da Site Reliability Engineering (SRE), e a resposta não é "temos 99,9% de uptime no SLA". A resposta precisa ser baseada em dados reais, medidos do ponto de vista do usuário, com critérios objetivos que orientam decisões de engenharia.
Neste artigo, vamos explorar como implementar os pilares fundamentais de SRE no Azure: SLIs (Service Level Indicators), SLOs (Service Level Objectives) e Error Budgets, usando Azure Monitor, Application Insights e KQL como base.
O que é SRE e por que importa?
Site Reliability Engineering é uma disciplina criada pelo Google que aplica práticas de engenharia de software à operação de infraestrutura. O objetivo não é buscar 100% de disponibilidade, isso é impossível e economicamente inviável, mas sim definir um nível aceitável de confiabilidade e gerenciar o equilíbrio entre velocidade de entrega e estabilidade.
Os três conceitos fundamentais são:
| Conceito | Descrição |
|---|---|
| SLI (Service Level Indicator) | Uma métrica quantitativa que reflete a saúde do serviço do ponto de vista do usuário. Exemplos: latência, taxa de erro, throughput. |
| SLO (Service Level Objective) | Um alvo interno para o SLI. Exemplo: "99,9% das requisições devem ter latência abaixo de 500ms em uma janela de 30 dias". |
| Error Budget | A margem de erro permitida pelo SLO. Se o SLO é 99,9%, o error budget é 0,1% — o quanto o sistema pode falhar antes de violar o objetivo. |
SLA vs. SLO: O SLA (Service Level Agreement) é um contrato externo com penalidades. O SLO é um objetivo interno, tipicamente mais rigoroso que o SLA. Você define SLOs para garantir que nunca chegue perto de violar o SLA.
Por que SLIs, SLOs e Error Budgets mudam o jogo?
Sem SLOs, as equipes operam com base em intuição:
- Desenvolvimento quer entregar features o mais rápido possível.
- Operações quer estabilidade máxima e resiste a mudanças.
- Negócio quer ambos, mas não tem como arbitrar.
Com SLOs e Error Budgets, essa tensão se resolve com dados:
- Error Budget sobrando? O time pode fazer deploys mais agressivos, experimentar, inovar.
- Error Budget apertado? Hora de desacelerar, focar em confiabilidade, corrigir debt técnico.
- Error Budget esgotado? Congelar mudanças até recuperar. Priorizar estabilidade.
Essa é a essência: o error budget é a moeda que compra velocidade de entrega.
Definindo SLIs eficazes
O primeiro passo é escolher indicadores que reflitam genuinamente a experiência do usuário. Um SLI ruim leva a um SLO inútil.
Princípios para bons SLIs
- Meça do ponto de vista do usuário: não meça CPU do servidor — meça latência percebida pelo cliente.
- Use proporções: "X% das requisições atendem o critério Y" é mais útil que médias.
- Seja específico: "latência do endpoint
/api/orders" é melhor que "latência geral da aplicação". - Cubra as dimensões certas: disponibilidade, latência, qualidade (correção) e throughput.
SLIs recomendados por tipo de serviço
| Tipo de serviço | SLIs recomendados |
|---|---|
| API / Web Service | Disponibilidade (% de respostas não-5xx), Latência (P50, P95, P99), Taxa de erro |
| Pipeline de dados | Freshness (tempo desde última atualização), Completude (% de registros processados), Correção |
| Storage / Banco de dados | Latência de leitura/escrita, Disponibilidade, Durabilidade |
| Serviço de streaming | Latência end-to-end, Taxa de mensagens perdidas, Throughput |
| Aplicação de IA / Inferência | Latência de inferência (P95), Disponibilidade do endpoint, Taxa de erro do modelo |
Implementando SLIs no Azure com Application Insights e KQL
Vamos implementar SLIs concretos para uma API hospedada no Azure (App Service, AKS ou Container Apps) instrumentada com Application Insights.
SLI de Disponibilidade
A disponibilidade é medida como a proporção de requisições bem-sucedidas (não-5xx) sobre o total de requisições.
// SLI de Disponibilidade — janela de 30 dias
let janela = 30d;
requests
| where timestamp > ago(janela)
| summarize
totalRequests = count(),
successfulRequests = countif(toint(resultCode) < 500),
sli_availability = round(100.0 * countif(toint(resultCode) < 500) / count(), 4)
SLI de Latência
A latência é medida como a proporção de requisições abaixo de um limiar aceitável.
// SLI de Latência — % de requisições abaixo de 500ms
let janela = 30d;
let limiar_ms = 500;
requests
| where timestamp > ago(janela)
| summarize
totalRequests = count(),
fastRequests = countif(duration < limiar_ms),
sli_latency = round(100.0 * countif(duration < limiar_ms) / count(), 4)
SLI de Latência por endpoint crítico
Nem todos os endpoints têm a mesma importância. Defina SLIs específicos para jornadas críticas:
// SLI de Latência por endpoint
let janela = 30d;
let limiar_ms = 500;
requests
| where timestamp > ago(janela)
| where name in ("/api/orders", "/api/payments", "/api/auth/login")
| summarize
totalRequests = count(),
fastRequests = countif(duration < limiar_ms),
sli_latency = round(100.0 * countif(duration < limiar_ms) / count(), 4)
by name
| order by sli_latency asc
SLI de Disponibilidade com dependências
Muitas vezes a falha não está na sua aplicação, mas em uma dependência (banco de dados, API externa, cache). Meça isso:
// SLI de Disponibilidade de dependências
let janela = 30d;
dependencies
| where timestamp > ago(janela)
| summarize
totalCalls = count(),
successfulCalls = countif(success == true),
sli_dependency = round(100.0 * countif(success == true) / count(), 4)
by target, type
| order by sli_dependency asc
Definindo SLOs
Com os SLIs implementados, o próximo passo é definir objetivos para cada um. Um SLO é a combinação de:
- O SLI (o que medir)
- O alvo (o valor aceitável)
- A janela de tempo (o período de avaliação)
Exemplos de SLOs bem definidos
| SLO | SLI | Alvo | Janela |
|---|---|---|---|
| Disponibilidade da API | % de respostas não-5xx | ≥ 99,9% | 30 dias (rolling) |
| Latência da API | % de requisições < 500ms | ≥ 95% | 30 dias (rolling) |
| Latência P99 da API | Percentil 99 da latência | ≤ 2000ms | 30 dias (rolling) |
| Disponibilidade do endpoint de pagamentos | % de respostas não-5xx em /api/payments | ≥ 99,95% | 30 dias (rolling) |
| Freshness do pipeline de dados | Tempo desde última ingestão | ≤ 15 minutos | Contínuo |
Como escolher o alvo certo?
Definir o alvo do SLO é uma decisão de negócio, não apenas técnica:
- Analise dados históricos: qual é o SLI atual? Se sua disponibilidade real é 99,95%, um SLO de 99,99% seria aspiracional demais.
- Considere o impacto no usuário: uma API interna pode ter SLO de 99,5%. Um sistema de pagamentos precisa de 99,99%.
- Alinhe com o SLA: o SLO deve ser mais rigoroso que o SLA externo. Se o SLA é 99,9%, o SLO deveria ser 99,95%.
- Comece conservador: é melhor ter um SLO que você consegue cumprir e apertar gradualmente.
Consulta KQL para avaliar SLOs
// Dashboard de SLOs — visão consolidada
let janela = 30d;
let slo_disponibilidade = 99.9;
let slo_latencia = 95.0;
let limiar_latencia_ms = 500;
requests
| where timestamp > ago(janela)
| summarize
total = count(),
// SLI de disponibilidade
sucesso = countif(toint(resultCode) < 500),
sli_disponibilidade = round(100.0 * countif(toint(resultCode) < 500) / count(), 4),
// SLI de latência
rapidas = countif(duration < limiar_latencia_ms),
sli_latencia = round(100.0 * countif(duration < limiar_latencia_ms) / count(), 4),
// Percentis
p50 = round(percentile(duration, 50), 1),
p95 = round(percentile(duration, 95), 1),
p99 = round(percentile(duration, 99), 1)
| extend
status_disponibilidade = iff(sli_disponibilidade >= slo_disponibilidade, "✅ Dentro do SLO", "🛑 Violação de SLO"),
status_latencia = iff(sli_latencia >= slo_latencia, "✅ Dentro do SLO", "🛑 Violação de SLO"),
// Error budget
budget_total_disponibilidade = round(total * (100.0 - slo_disponibilidade) / 100.0, 0),
budget_consumido_disponibilidade = total - sucesso,
budget_total_latencia = round(total * (100.0 - slo_latencia) / 100.0, 0),
budget_consumido_latencia = total - rapidas
| extend
budget_restante_disponibilidade_pct = round(100.0 * (1 - (toreal(budget_consumido_disponibilidade) / budget_total_disponibilidade)), 2),
budget_restante_latencia_pct = round(100.0 * (1 - (toreal(budget_consumido_latencia) / budget_total_latencia)), 2)
Error Budgets: a moeda da confiabilidade
O Error Budget é o conceito mais transformador de SRE. Ele converte o SLO em algo acionável.
Calculando o Error Budget
Se o SLO de disponibilidade é 99,9% em uma janela de 30 dias:
Error Budget = 100% - 99,9% = 0,1%
Em uma janela de 30 dias:
- Total de minutos: 30 × 24 × 60 = 43.200 minutos
- Error Budget em minutos: 43.200 × 0,001 = 43,2 minutos
Em requisições (supondo 1 milhão de requisições/mês):
- Error Budget em requisições: 1.000.000 × 0,001 = 1.000 requisições com erro permitidas
Isso significa que o sistema pode falhar 43 minutos por mês ou 1.000 requisições antes de violar o SLO.
Monitorando o consumo do Error Budget ao longo do tempo
// Consumo diário do Error Budget de disponibilidade
let janela = 30d;
let slo = 99.9;
requests
| where timestamp > ago(janela)
| summarize
total = count(),
falhas = countif(toint(resultCode) >= 500)
by bin(timestamp, 1d)
| order by timestamp asc
| extend
falhas_acumuladas = row_cumsum(falhas),
total_acumulado = row_cumsum(total)
| extend
budget_total = round(total_acumulado * (100.0 - slo) / 100.0, 0),
budget_consumido = falhas_acumuladas
| extend
budget_restante_pct = round(100.0 * (1.0 - toreal(budget_consumido) / toreal(budget_total)), 2)
| project
timestamp,
falhas_dia = falhas,
falhas_acumuladas,
budget_total,
budget_consumido,
budget_restante_pct
Taxa de queima (Burn Rate)
O Error Budget sozinho diz quanto já foi consumido. A burn rate (taxa de queima) diz quão rápido está sendo consumido — e isso é fundamental para alertas.
Burn Rate = taxa_de_erro_atual / taxa_de_erro_permitida_pelo_SLO
Se burn rate = 1: o budget será consumido exatamente no fim da janela (normal).
Se burn rate = 10: o budget será esgotado 10x mais rápido que o esperado (urgente!).
Se burn rate = 0.5: o budget está sendo consumido pela metade da taxa — tudo tranquilo.
// Burn rate das últimas 1h e 6h
let slo = 99.9;
let taxa_erro_permitida = (100.0 - slo) / 100.0; // 0.001
// Burn rate da última 1 hora
let br_1h = toscalar(
requests
| where timestamp > ago(1h)
| summarize taxa_erro = 1.0 * countif(toint(resultCode) >= 500) / count()
) / taxa_erro_permitida;
// Burn rate das últimas 6 horas
let br_6h = toscalar(
requests
| where timestamp > ago(6h)
| summarize taxa_erro = 1.0 * countif(toint(resultCode) >= 500) / count()
) / taxa_erro_permitida;
print burn_rate_1h = round(br_1h, 2), burn_rate_6h = round(br_6h, 2),
alerta = case(
br_1h > 14.4, "🔴 CRÍTICO — budget esgota em ~1h",
br_1h > 6, "🟠 ALTO — budget esgota em ~5h",
br_6h > 3, "🟡 MÉDIO — budget esgota em ~10h",
br_6h > 1, "🔵 BAIXO — consumo acima do ideal",
"✅ Normal")
Alertas baseados em Burn Rate
Alertas tradicionais baseados em thresholds estáticos (ex: "alerte se a taxa de erro > 1%") são problemáticos:
- Threshold alto: perde incidentes lentos que drenam o budget gradualmente.
- Threshold baixo: gera alertas excessivos para picos momentâneos que não impactam o SLO.
Alertas baseados em burn rate resolvem isso. O Google SRE Book recomenda uma abordagem com múltiplas janelas:
| Severidade | Burn Rate | Janela curta | Janela longa | Budget consumido em | Ação |
|---|---|---|---|---|---|
| Crítico (page) | 14.4x | 1 hora | — | ~1 hora | Resposta imediata |
| Alto (page) | 6x | — | 6 horas | ~5 horas | Investigar urgente |
| Médio (ticket) | 3x | — | 3 dias | ~10 dias | Planejar correção |
| Baixo (log) | 1x | — | 30 dias | ~30 dias | Monitorar tendência |
Implementando alertas no Azure Monitor
// Alert Rule — Burn Rate Crítico (>14.4x na última hora)
let slo = 99.9;
let taxa_erro_permitida = (100.0 - slo) / 100.0;
let burn_rate_threshold = 14.4;
requests
| where timestamp > ago(1h)
| summarize
total = count(),
erros = countif(toint(resultCode) >= 500)
| extend
taxa_erro = 1.0 * erros / total,
burn_rate = (1.0 * erros / total) / taxa_erro_permitida
| where burn_rate > burn_rate_threshold
| project
alerta = "CRÍTICO: Burn rate de disponibilidade acima de 14.4x",
burn_rate = round(burn_rate, 2),
taxa_erro_pct = round(taxa_erro * 100, 4),
total_requisicoes = total,
total_erros = erros
Para criar o alerta via CLI:
# Criar scheduled query alert rule para burn rate crítico
az monitor scheduled-query create \
--resource-group meu-rg \
--name "slo-burn-rate-critico" \
--scopes "/subscriptions/<sub-id>/resourceGroups/meu-rg/providers/Microsoft.Insights/components/meu-appinsights" \
--condition "count 'BurnRateAlto' > 0" \
--condition-query BurnRateAlto="requests | where timestamp > ago(1h) | summarize total=count(), erros=countif(toint(resultCode) >= 500) | extend burn_rate=(1.0*erros/total)/0.001 | where burn_rate > 14.4" \
--severity 0 \
--evaluation-frequency 5m \
--window-size 5m \
--action-groups "/subscriptions/<sub-id>/resourceGroups/meu-rg/providers/Microsoft.Insights/actionGroups/time-oncall"
Criando um Workbook de SLO no Azure
Um dashboard de SLOs precisa responder três perguntas em segundos:
- Estamos dentro do SLO? (sim/não, por serviço)
- Quanto error budget resta? (% restante na janela atual)
- Qual é a tendência? (burn rate — estamos melhorando ou piorando?)
Estrutura recomendada do Workbook
| Seção | Conteúdo |
|---|---|
| Resumo executivo | Semáforo por serviço: 🟢 dentro do SLO, 🟡 budget < 30%, 🔴 SLO violado |
| SLIs atuais | Valores atuais de cada SLI vs. alvo do SLO |
| Error Budget | Gráfico de consumo acumulado do budget ao longo da janela de 30 dias |
| Burn Rate | Gráfico de burn rate nas últimas 24h com linhas de referência (1x, 6x, 14.4x) |
| Histórico de SLO | Compliance do SLO nos últimos 3-6 meses (tendência mensal) |
| Incidentes | Correlação entre consumo de budget e incidentes/deploys |
Query para o semáforo por serviço
// Semáforo de SLOs por serviço
let janela = 30d;
let slo_disponibilidade = 99.9;
let slo_latencia = 95.0;
let limiar_latencia_ms = 500;
requests
| where timestamp > ago(janela)
| extend servico = tostring(split(cloud_RoleName, "-")[0])
| summarize
total = count(),
sli_disp = round(100.0 * countif(toint(resultCode) < 500) / count(), 3),
sli_lat = round(100.0 * countif(duration < limiar_latencia_ms) / count(), 3)
by servico
| extend
budget_disp_restante = round(100.0 * (1.0 - (total * (1 - sli_disp/100.0)) / (total * (1 - slo_disponibilidade/100.0))), 1),
budget_lat_restante = round(100.0 * (1.0 - (total * (1 - sli_lat/100.0)) / (total * (1 - slo_latencia/100.0))), 1)
| extend
status_disp = case(
budget_disp_restante <= 0, "🔴 Violado",
budget_disp_restante < 30, "🟡 Atenção",
"🟢 OK"),
status_lat = case(
budget_lat_restante <= 0, "🔴 Violado",
budget_lat_restante < 30, "🟡 Atenção",
"🟢 OK")
| project servico, sli_disp, status_disp, budget_disp_restante, sli_lat, status_lat, budget_lat_restante
| order by budget_disp_restante asc
Error Budget Policies: transformando dados em decisões
Ter SLOs e Error Budgets sem uma política de ação é como ter um velocímetro sem limites de velocidade. A Error Budget Policy define o que acontece quando o budget atinge determinados níveis.
Exemplo de Error Budget Policy
╔══════════════════════════════════════════════════════════════════╗
║ ERROR BUDGET POLICY ║
║ Serviço: API de Pedidos ║
╠══════════════════════════════════════════════════════════════════╣
║ ║
║ SLO: 99,9% de disponibilidade em janela rolling de 30 dias ║
║ ║
║ Budget > 50%: ║
║ → Operação normal ║
║ → Deploys liberados conforme pipeline padrão ║
║ → Experimentos de Chaos Engineering recomendados ║
║ ║
║ Budget entre 30% e 50%: ║
║ → Revisar incidentes recentes e ações corretivas ║
║ → Reduzir frequência de deploys para 1x/dia ║
║ → Priorizar itens de confiabilidade no backlog ║
║ ║
║ Budget entre 10% e 30%: ║
║ → Feature freeze: apenas correções críticas e melhorias ║
║ de confiabilidade ║
║ → Postmortem obrigatório para qualquer novo incidente ║
║ → Suspender experimentos de Chaos Engineering ║
║ ║
║ Budget < 10%: ║
║ → Deploy freeze total ║
║ → Equipe focada exclusivamente em restaurar confiabilidade ║
║ → Escalação para liderança de engenharia ║
║ → Revisão arquitetural obrigatória ║
║ ║
║ Budget esgotado (SLO violado): ║
║ → Incident review com liderança ║
║ → Plano de ação documentado com prazos ║
║ → Revisão do SLO: o alvo é realista? ║
║ ║
╚══════════════════════════════════════════════════════════════════╝
Conexão com Chaos Engineering: se você já leu o artigo sobre [Engenharia de Caos com Azure Chaos Studio], note como o Error Budget determina quando executar experimentos de caos. Com budget folgado, é o momento ideal para validar resiliência. Com budget apertado, o foco é estabilizar.
Integrando SLOs no ciclo de engenharia
SLOs e CI/CD
Integre a verificação de SLOs no pipeline de deployment:
# GitHub Actions — gate de SLO antes do deploy em produção
- name: Verificar Error Budget
uses: azure/cli@v2
with:
inlineScript: |
# Consultar error budget via Log Analytics
BUDGET=$(az monitor log-analytics query \
--workspace ${{ env.WORKSPACE_ID }} \
--analytics-query "
requests
| where timestamp > ago(30d)
| summarize sli = 100.0 * countif(toint(resultCode) < 500) / count()
| extend budget_restante = round(100.0 * (1.0 - ((100.0 - sli) / (100.0 - 99.9))), 2)
| project budget_restante
" \
--query "[0].budget_restante" -o tsv)
echo "Error Budget restante: ${BUDGET}%"
# Bloquear deploy se budget está abaixo de 30%
if (( $(echo "$BUDGET < 30" | bc -l) )); then
echo "::error::Error budget abaixo de 30% (${BUDGET}%). Deploy bloqueado."
echo "Priorize correções de confiabilidade antes de deployar novas features."
exit 1
fi
- name: Deploy para produção
if: success()
run: |
echo "Error budget saudável. Prosseguindo com deploy..."
SLOs e Postmortems
Todo incidente que consome error budget significativo deve gerar um postmortem. A estrutura recomendada:
- Impacto no SLO: quanto error budget foi consumido?
- Timeline: o que aconteceu, quando, e como foi detectado?
- Causa raiz: por que aconteceu? (use os "5 porquês")
- Ações corretivas: o que será feito para prevenir recorrência?
- Lições aprendidas: o que aprendemos sobre nossos SLIs/SLOs?
Armadilhas comuns ao implementar SLOs
1. Medir o que é fácil, não o que é importante
Errado: usar CPU, memória ou health check como SLI. Certo: medir latência e taxa de erro das requisições reais dos usuários.
Health checks podem retornar 200 enquanto o endpoint de login está retornando 500. CPU pode estar em 20% enquanto o banco de dados está com deadlock.
2. Definir SLOs sem dados históricos
Errado: "nosso SLO será 99,99% porque isso soa bem". Certo: analisar os dados dos últimos 3-6 meses, entender o baseline real, e definir um SLO alcançável mas desafiador.
// Analisar disponibilidade histórica por mês para definir SLO realista
requests
| where timestamp > ago(180d)
| summarize
sli_disponibilidade = round(100.0 * countif(toint(resultCode) < 500) / count(), 4),
total = count()
by mes = startofmonth(timestamp)
| order by mes asc
3. Não ter Error Budget Policy
SLOs sem política de ação são apenas números em um dashboard. O valor real vem de decisões automatizadas ou semi-automatizadas baseadas no estado do budget.
4. Janela de tempo inadequada
- Janela muito curta (1 dia): gera volatilidade excessiva. Um pico de erros de 5 minutos pode "violar" o SLO do dia.
- Janela muito longa (90 dias): reage lentamente a degradações. Um mês ruim é diluído pelos meses bons.
- Recomendação: janela rolling de 30 dias para a maioria dos cenários.
5. SLOs uniformes para tudo
Nem todos os endpoints merecem o mesmo SLO:
// Exemplo: SLOs diferenciados por criticidade
let janela = 30d;
requests
| where timestamp > ago(janela)
| extend criticidade = case(
name has "/api/payments" or name has "/api/auth", "crítico",
name has "/api/orders" or name has "/api/users", "alto",
name has "/api/reports" or name has "/api/search", "médio",
"baixo")
| extend slo_alvo = case(
criticidade == "crítico", 99.95,
criticidade == "alto", 99.9,
criticidade == "médio", 99.5,
99.0)
| summarize
total = count(),
sli = round(100.0 * countif(toint(resultCode) < 500) / count(), 3),
take_any(slo_alvo)
by criticidade
| extend
dentro_slo = iff(sli >= slo_alvo, "✅", "🛑"),
margem = round(sli - slo_alvo, 3)
| order by slo_alvo desc
Exemplo completo: implementando SRE para uma aplicação no AKS
Vamos juntar tudo em um cenário prático. Considere uma aplicação de e-commerce rodando no AKS com os seguintes componentes:
- API Gateway (NGINX Ingress)
- Serviço de Pedidos (orders-api)
- Serviço de Pagamentos (payments-api)
- Serviço de Catálogo (catalog-api)
- Banco de dados (Azure SQL + Cosmos DB)
Passo 1: Definir SLOs por serviço
| Serviço | SLI | SLO | Error Budget (30d, ~1M req) |
|---|---|---|---|
| API Gateway | Disponibilidade | ≥ 99,95% | 500 erros |
| Pedidos | Disponibilidade | ≥ 99,9% | 1.000 erros |
| Pedidos | Latência (< 800ms) | ≥ 95% | 50.000 req lentas |
| Pagamentos | Disponibilidade | ≥ 99,99% | 100 erros |
| Pagamentos | Latência (< 1000ms) | ≥ 99% | 10.000 req lentas |
| Catálogo | Disponibilidade | ≥ 99,5% | 5.000 erros |
| Catálogo | Latência (< 300ms) | ≥ 90% | 100.000 req lentas |
Passo 2: Implementar coleta de SLIs
Garanta que todos os serviços estejam instrumentados com o SDK do Application Insights (ou OpenTelemetry exportando para Application Insights):
# Para aplicações .NET no AKS, configurar via variáveis de ambiente
kubectl set env deployment/orders-api \
APPLICATIONINSIGHTS_CONNECTION_STRING="InstrumentationKey=<key>;IngestionEndpoint=https://brazilsouth-1.in.applicationinsights.azure.com/"
# Para aplicações com OpenTelemetry, usar o Azure Monitor Exporter
# Isso garante que requests e dependencies sejam capturados automaticamente
Passo 3: Criar alertas de burn rate
# Alerta crítico para payments-api (SLO 99,99%)
az monitor scheduled-query create \
--resource-group meu-rg \
--name "slo-payments-burn-rate-critico" \
--scopes "/subscriptions/<sub-id>/resourceGroups/meu-rg/providers/Microsoft.Insights/components/meu-appinsights" \
--condition "count 'BurnRate' > 0" \
--condition-query BurnRate="requests | where timestamp > ago(1h) | where cloud_RoleName == 'payments-api' | summarize total=count(), erros=countif(toint(resultCode) >= 500) | extend burn_rate=(1.0*erros/total)/0.0001 | where burn_rate > 14.4" \
--severity 0 \
--evaluation-frequency 5m \
--window-size 5m \
--action-groups "/subscriptions/<sub-id>/resourceGroups/meu-rg/providers/Microsoft.Insights/actionGroups/payments-oncall"
Passo 4: Conectar com Chaos Engineering
Com o error budget saudável, programe experimentos de caos para validar as hipóteses implícitas nos SLOs:
| SLO | Experimento de Caos | Hipótese |
|---|---|---|
| Pagamentos 99,99% | Failover do Azure SQL | O failover completa em < 30s sem perda de transações |
| Pedidos 99,9% | Kill pods do orders-api | HPA recria pods em < 60s, LB redistribui |
| Catálogo P95 < 300ms | Latência de 200ms no Cosmos DB | Cache absorve o impacto, latência < 300ms |
| Gateway 99,95% | Shutdown de nó do AKS | Pods são reagendados em < 2min |
Conclusão
SRE não é um cargo — é uma prática. E os pilares de SLIs, SLOs e Error Budgets são o que transformam operações reativas em engenharia de confiabilidade proativa.
Com o Azure Monitor e Application Insights, você tem tudo o que precisa para implementar SRE sem ferramentas adicionais:
- Application Insights coleta os dados que alimentam seus SLIs.
- KQL permite calcular SLIs, SLOs e Error Budgets com precisão.
- Azure Workbooks visualiza o estado dos SLOs para toda a organização.
- Scheduled Query Alerts implementa alertas inteligentes baseados em burn rate.
- Azure Chaos Studio valida as hipóteses por trás dos SLOs (quando o budget permite!).
O caminho é claro:
- Comece com 1-2 SLOs para o serviço mais crítico.
- Meça por 30 dias antes de definir políticas.
- Implemente alertas de burn rate para substituir alertas baseados em thresholds.
- Crie uma Error Budget Policy e alinhe com o time.
- Expanda gradualmente para outros serviços.
A confiabilidade não é um estado, é um processo contínuo de medição, aprendizado e melhoria. SLOs e Error Budgets dão a linguagem comum para que engenharia, produto e negócio tomem decisões juntos, baseados em dados, não em intuição.