← voltar ao índice

· 29 min de leitura · por Tiago

SLI, SLO e Error Budget na prática

Introdução

Nos artigos anteriores desta série, construímos a base técnica da observabilidade.

Primeiro, instrumentamos aplicações .NET com OpenTelemetry e passamos a produzir logs, métricas e traces correlacionados.

Depois, evoluímos o OpenTelemetry Collector para uma arquitetura de produção no Kubernetes, discutindo Agent, Gateway, filas, retries, tail sampling, resiliência e escalabilidade.

Nesse ponto, temos muitos dados.

Sabemos quantas requisições chegam.

Sabemos quanto tempo elas levam.

Sabemos quantas falham.

Temos traces distribuídos.

Temos dashboards.

Temos métricas de CPU, memória, pods, filas e bancos.

Mas ainda falta responder uma pergunta muito mais importante:

o sistema está bom o suficiente para os usuários?

Essa pergunta parece simples, mas não pode ser respondida apenas olhando CPU em 40%, memória em 60% ou todos os pods em estado Running.

Uma aplicação pode estar com infraestrutura aparentemente saudável e ainda assim oferecer uma experiência ruim.

Um endpoint pode responder HTTP 200 em 20 segundos.

Tecnicamente, ele respondeu com sucesso.

Para o usuário, provavelmente falhou.

Uma API pode ter disponibilidade de 99,9%, mas os 0,1% de erros podem estar concentrados justamente no fluxo de pagamento.

Uma métrica agregada pode parecer excelente enquanto uma funcionalidade crítica está completamente indisponível.

É aqui que entram três conceitos fundamentais de Site Reliability Engineering:

SLI, Service Level Indicator.

SLO, Service Level Objective.

Error Budget, ou orçamento de erro.

Eles transformam observabilidade em um mecanismo de decisão.

Em vez de perguntar "a CPU está alta?", passamos a perguntar:

"Estamos entregando o nível de serviço que prometemos?"

Em vez de discutir subjetivamente se uma aplicação está estável o suficiente para receber uma nova versão, podemos perguntar:

"Quanto do nosso Error Budget já foi consumido?"

Neste artigo, vamos construir esse modelo do zero usando uma API .NET como exemplo.

Vamos definir SLIs de disponibilidade e latência, criar SLOs, calcular Error Budgets, escrever consultas PromQL, entender burn rate e configurar alertas que representam impacto real para o usuário.

O objetivo não é apenas aprender fórmulas.

É mudar a forma como pensamos sobre confiabilidade.

O problema de monitorar infraestrutura em vez de experiência

Considere uma API de pedidos.

Temos os seguintes dashboards:

CPU:             38%
Memória:         62%
Pods disponíveis: 6 de 6
PostgreSQL CPU:  45%
Redis CPU:       20%
HTTP 5xx:        0,3%

A primeira impressão pode ser positiva.

Nada parece crítico.

Agora imagine que o endpoint:

POST /checkout

leva 12 segundos para responder em 15% das requisições.

A infraestrutura continua "verde".

Mas usuários estão abandonando compras.

Esse exemplo mostra uma distinção fundamental.

Métricas de recursos respondem:

Como o sistema está funcionando internamente?

SLIs tentam responder:

Qual experiência o sistema está entregando?

CPU, memória e disco continuam importantes.

Eles ajudam a diagnosticar a causa de problemas.

Mas geralmente não são uma boa definição primária de confiabilidade.

Um usuário não se importa se seu pod está usando 40% ou 80% de CPU.

Ele se importa se conseguiu concluir a compra.

Essa mudança de perspectiva é a base de SLI e SLO.

O que é um SLI?

SLI significa Service Level Indicator.

É uma medida quantitativa de algum aspecto relevante do serviço entregue.

Em uma API HTTP, exemplos comuns são:

Disponibilidade

Latência

Taxa de sucesso

Correção da resposta

Freshness de dados

A definição precisa do indicador importa muito.

Dizer:

Nosso SLI é disponibilidade.

não é suficiente.

Precisamos responder:

O que significa uma requisição disponível?

Quais endpoints entram no cálculo?

HTTP 404 conta como indisponibilidade?

HTTP 429 conta como erro?

Timeout conta?

A medição é feita no cliente, no ingress ou na aplicação?

Health checks entram no cálculo?

Uma definição melhor seria:

SLI de disponibilidade:

Percentual de requisições HTTP válidas realizadas por usuários
que recebem uma resposta considerada bem-sucedida,
excluindo health checks e erros causados pelo próprio cliente.

Agora temos algo que pode ser medido.

Essa precisão evita um dos erros mais comuns em programas de SLO: criar números bonitos sem definir exatamente o que eles significam.

Bons eventos e eventos válidos

Uma forma prática de modelar muitos SLIs é usar uma razão:

SLI = eventos bons / eventos válidos

Imagine que em 30 dias nossa API recebeu:

10.000.000 requisições válidas

Dessas:

9.990.000 foram consideradas boas

O SLI de disponibilidade seria:

9.990.000 / 10.000.000 = 99,9%

A parte difícil não é a divisão.

A parte difícil é definir "bom" e "válido".

Considere HTTP 404.

Se um usuário solicita:

GET /products/999999

e o produto realmente não existe, retornar 404 Not Found é o comportamento correto.

Contabilizar isso como indisponibilidade seria errado.

Agora considere HTTP 500.

Nesse caso, a aplicação provavelmente falhou ao processar uma requisição válida.

Esse evento normalmente deveria consumir Error Budget.

HTTP 429 também exige contexto.

Se o cliente ultrapassou deliberadamente um limite documentado, talvez o evento não deva contar como falha do serviço.

Se todos os usuários recebem 429 porque o rate limiter foi configurado incorretamente após um deploy, temos um problema real de disponibilidade.

SLIs bons exigem semântica de negócio.

Não basta agrupar status codes mecanicamente.

Primeiro exemplo: instrumentando uma API .NET

Vamos criar métricas explícitas para uma API de pedidos.

Embora instrumentações automáticas já produzam métricas HTTP, construir um exemplo manual ajuda a entender como o modelo funciona.

using System.Diagnostics.Metrics;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenTelemetry()
    .WithMetrics(metrics =>
    {
        metrics
            .AddMeter("Logby.Orders")
            .AddAspNetCoreInstrumentation()
            .AddOtlpExporter();
    });

var app = builder.Build();

var meter = new Meter("Logby.Orders");

var requests = meter.CreateCounter<long>(
    name: "orders.requests",
    unit: "{request}",
    description: "Number of valid order requests.");

var successfulRequests = meter.CreateCounter<long>(
    name: "orders.requests.success",
    unit: "{request}",
    description: "Number of successful order requests.");

var requestDuration = meter.CreateHistogram<double>(
    name: "orders.request.duration",
    unit: "s",
    description: "Duration of order requests.");

app.MapPost("/orders", async () =>
{
    var startedAt = Stopwatch.GetTimestamp();

    requests.Add(
        1,
        new KeyValuePair<string, object?>(
            "operation",
            "create_order"));

    try
    {
        await Task.Delay(
            Random.Shared.Next(
                20,
                250));

        successfulRequests.Add(
            1,
            new KeyValuePair<string, object?>(
                "operation",
                "create_order"));

        return Results.Ok(
            new
            {
                OrderId = Guid.NewGuid()
            });
    }
    finally
    {
        var elapsed =
            Stopwatch.GetElapsedTime(startedAt);

        requestDuration.Record(
            elapsed.TotalSeconds,
            new KeyValuePair<string, object?>(
                "operation",
                "create_order"));
    }
});

app.Run();

Existem três métricas principais nesse exemplo.

orders.requests representa o denominador do nosso SLI.

Ele registra quantas operações válidas foram recebidas.

orders.requests.success representa o numerador da disponibilidade.

orders.request.duration registra a distribuição de latência.

É importante usar Histogram para duração em vez de armazenar apenas uma média.

Uma média pode esconder a experiência da cauda.

Imagine:

99 requisições: 100 ms
1 requisição:   20 segundos

A média é aproximadamente 299 ms.

Esse número não mostra claramente que um usuário esperou 20 segundos.

Histograms permitem trabalhar com distribuições, buckets e percentis aproximados.

Também existe uma decisão importante no atributo:

"operation", "create_order"

O valor pertence a um conjunto pequeno e previsível.

Isso é bom.

Seria perigoso fazer:

"user_id", userId

ou:

"order_id", orderId

Esses atributos possuem cardinalidade potencialmente enorme.

Para SLIs, dimensões devem ser cuidadosamente controladas.

A métrica precisa ser útil para segmentação sem criar milhões de séries temporais.

Nem sempre precisamos criar métricas customizadas

Em aplicações ASP.NET Core instrumentadas com OpenTelemetry, métricas HTTP padronizadas já podem fornecer boa parte dos dados necessários.

Uma das métricas recomendadas pelas convenções semânticas do OpenTelemetry é:

http.server.request.duration

Ela representa a duração das requisições HTTP no servidor.

Dependendo do stack de métricas usado, também teremos contadores e atributos que permitem distinguir método, rota e status.

Isso significa que não devemos criar métricas customizadas apenas porque podemos.

Uma regra prática é:

Use métricas padronizadas quando elas representam corretamente a experiência que você quer medir.

Crie métricas de negócio quando a semântica necessária não existe nas métricas técnicas.

Por exemplo:

checkout.completed

payment.authorized

report.generated

document.processed

Esses eventos podem representar melhor o sucesso percebido pelo usuário do que simplesmente "HTTP 200".

Um endpoint pode responder 200 e ainda retornar:

{
  "status": "processing_failed"
}

Do ponto de vista HTTP, houve sucesso.

Do ponto de vista do negócio, houve falha.

SLIs maduros frequentemente combinam telemetria técnica e semântica de negócio.

O que é um SLO?

SLO significa Service Level Objective.

É o objetivo definido para um SLI.

Se nosso SLI mede disponibilidade, um SLO poderia ser:

99,9% das requisições válidas devem ser bem-sucedidas
em uma janela móvel de 30 dias.

Observe que essa definição possui três elementos.

O indicador:

percentual de requisições bem-sucedidas

O objetivo:

99,9%

A janela:

30 dias

Sem a janela, o número perde significado.

99,9% em cinco minutos é muito diferente de 99,9% em 30 dias.

Também precisamos evitar uma armadilha comum.

Um SLO não deveria ser escolhido simplesmente olhando a disponibilidade atual e copiando esse número.

Suponha que o sistema entregue historicamente:

99,997%

Isso não significa que o SLO precisa ser:

99,997%

Talvez os usuários fiquem completamente satisfeitos com 99,9%.

Adotar 99,997% pode obrigar a empresa a investir muito dinheiro para preservar uma confiabilidade que não produz valor proporcional.

SLO é uma decisão de produto e engenharia.

Não é apenas uma fotografia do desempenho atual.

Por que 100% costuma ser um SLO ruim

É tentador escrever:

SLO: 100% de disponibilidade

Parece profissional.

Na prática, costuma ser contraproducente.

Sistemas distribuídos falham.

Deploys falham.

Redes falham.

Dependências falham.

Certificados expiram.

Bancos ficam lentos.

Cloud providers possuem incidentes.

Erros humanos acontecem.

Buscar 100% pode levar a uma organização extremamente conservadora.

Qualquer mudança se torna ameaça.

Deploys diminuem.

Arquiteturas ficam mais caras.

Times evitam experimentar.

Pior ainda, se o objetivo é impossível de cumprir, ele deixa de orientar decisões.

Todo minuto de indisponibilidade já representa uma violação.

Não existe orçamento restante para discutir risco.

Um SLO útil reconhece que alguma falha é aceitável.

A pergunta real é:

quanto de falha podemos aceitar sem prejudicar significativamente os usuários e o negócio?

É exatamente essa margem que cria o Error Budget.

O que é Error Budget?

Se o SLO de disponibilidade é:

99,9%

a parcela de falha permitida é:

100% - 99,9% = 0,1%

Esse 0,1% é o Error Budget.

Em uma janela de 30 dias:

30 dias
= 720 horas
= 43.200 minutos

0,1% de 43.200 minutos representa aproximadamente:

43,2 minutos

Portanto, um SLO de 99,9% corresponde, de forma simplificada, a cerca de 43 minutos de indisponibilidade equivalente em 30 dias.

Mas existe uma forma melhor de pensar sobre isso.

Em sistemas baseados em requisições, o budget não precisa ser medido apenas em minutos.

Podemos medir em eventos ruins.

Imagine:

100.000.000 requisições válidas em 30 dias

Com SLO de 99,9%, podemos ter:

0,1% de eventos ruins

Isso corresponde a:

100.000 eventos ruins

Esse modelo costuma ser mais preciso para serviços com tráfego irregular.

Uma indisponibilidade de cinco minutos às 3 da manhã e outra de cinco minutos durante Black Friday podem afetar quantidades completamente diferentes de usuários.

Medir por requisições captura melhor essa diferença.

SLO de disponibilidade com PromQL

Suponha que o Prometheus receba dois counters:

orders_requests_total

orders_requests_success_total

Podemos calcular a disponibilidade dos últimos 30 dias:

sum(
  increase(
    orders_requests_success_total[30d]
  )
)
/
sum(
  increase(
    orders_requests_total[30d]
  )
)

Multiplicando por 100, obtemos percentual:

100
*
sum(
  increase(
    orders_requests_success_total[30d]
  )
)
/
sum(
  increase(
    orders_requests_total[30d]
  )
)

A consulta parece simples, mas existe uma decisão importante.

Estamos usando increase() sobre counters.

Counters crescem monotonamente e podem reiniciar quando um processo reinicia.

Prometheus entende esse comportamento e consegue ajustar o cálculo considerando resets.

Não devemos calcular disponibilidade usando diretamente a diferença manual entre o valor atual e um valor antigo sem entender resets.

Também devemos decidir se queremos somar todas as instâncias antes de dividir.

Neste exemplo:

sum(success)
/
sum(total)

faz sentido porque queremos a experiência global do serviço.

Fazer a média da disponibilidade de cada pod pode produzir resultados enganosos.

Imagine dois pods:

Pod A:
9.999 sucessos em 10.000 requisições
99,99%

Pod B:
1 sucesso em 10 requisições
10%

A média simples seria:

54,995%

Mas o resultado real agregado seria:

10.000 sucessos
/
10.010 requisições
≈ 99,9%

Médias de razões frequentemente criam erros.

Para SLIs baseados em eventos, prefira agregar numerador e denominador primeiro.

SLO de latência

Disponibilidade não é suficiente.

Uma requisição que demora 40 segundos pode ser tecnicamente bem-sucedida e ainda representar uma experiência inaceitável.

Podemos criar um SLO como:

99% das requisições de criação de pedido
devem completar em menos de 500 ms
em uma janela de 30 dias.

Observe que estamos falando de uma proporção de eventos dentro de um limite.

Isso é diferente de dizer:

p99 menor que 500 ms.

As duas formas estão relacionadas, mas para SLOs baseados em good events, normalmente é mais conveniente pensar:

requisições abaixo de 500 ms
/
total de requisições

Se usarmos um histogram Prometheus clássico com bucket 0.5, podemos calcular:

sum(
  increase(
    http_server_request_duration_seconds_bucket{
      http_route="/orders",
      le="0.5"
    }[30d]
  )
)
/
sum(
  increase(
    http_server_request_duration_seconds_count{
      http_route="/orders"
    }[30d]
  )
)

O bucket le="0.5" representa observações com duração menor ou igual a 0,5 segundo.

O denominador representa todas as observações.

Se o resultado for:

0,992

então:

99,2%

das requisições ficaram dentro do limite.

O SLO de 99% foi cumprido.

Por que média de latência é perigosa

Considere dez requisições:

100 ms
110 ms
100 ms
120 ms
100 ms
110 ms
100 ms
120 ms
100 ms
5.000 ms

A média é:

596 ms

Essa média parece ruim, mas não explica a distribuição.

Agora imagine um milhão de requisições onde poucas são extremamente lentas.

Uma média pode variar muito dependendo dessas exceções.

Percentis e SLIs baseados em thresholds respondem perguntas mais úteis.

Por exemplo:

99% abaixo de 500 ms

significa algo operacionalmente claro.

Um usuário em cada cem pode experimentar uma requisição acima desse limite.

Também podemos ter múltiplos objetivos:

95% abaixo de 300 ms

99% abaixo de 800 ms

99,9% abaixo de 2 segundos

Isso descreve melhor a distribuição de experiência.

Mas cuidado.

Cada SLO adicional cria mais uma política para operar.

Não crie quinze thresholds apenas porque o dashboard consegue mostrar.

Escolha poucos objetivos que realmente representam expectativas do usuário.

Error Budget baseado em eventos

Vamos criar um exemplo completo.

Nosso SLO:

99,9% de disponibilidade em 30 dias

Volume:

50.000.000 requisições

Budget permitido:

0,1%

Logo:

50.000 eventos ruins permitidos

Agora suponha que já tivemos:

35.000 eventos ruins

Consumimos:

35.000 / 50.000 = 70%

Restam:

30% do Error Budget

Essa informação é muito mais útil para decisões do que apenas dizer:

Disponibilidade atual: 99,93%

Podemos criar uma regra operacional.

Por exemplo:

Budget saudável:
deploys normais

Budget em risco:
reduzir mudanças de alto risco

Budget esgotado:
priorizar confiabilidade
e limitar mudanças não essenciais

O detalhe importante é que Error Budget não deve virar punição.

A ideia não é:

O time gastou o budget, ninguém mais pode trabalhar.

A ideia é alinhar incentivos.

Quando a confiabilidade está boa, existe espaço para assumir risco e inovar.

Quando a confiabilidade está ruim, investir temporariamente em estabilidade passa a ter prioridade objetiva.

A diferença entre SLO e SLA

SLO e SLA são frequentemente confundidos.

Um SLO é um objetivo operacional.

Um SLA, Service Level Agreement, é um acordo que normalmente possui consequências explícitas quando determinados níveis de serviço não são cumpridos.

Por exemplo:

SLO interno:

99,95% de disponibilidade

e:

SLA contratual:

99,9% de disponibilidade,
com crédito financeiro se violado

Ter um SLO interno mais rigoroso que o SLA cria margem de segurança.

A equipe consegue detectar degradação e agir antes de chegar a uma violação contratual.

Outro ponto importante é que nem todo sistema precisa de SLA.

Uma ferramenta interna pode ter SLOs muito úteis sem qualquer contrato financeiro.

SLO é uma ferramenta de engenharia e produto.

SLA envolve compromisso externo e consequências comerciais ou jurídicas.

O problema dos alertas tradicionais

Muitos ambientes ainda alertam assim:

CPU > 80%

Memória > 85%

5xx > 10 por minuto

p95 > 1 segundo

Esses alertas não são necessariamente errados.

O problema é usá-los como principal mecanismo para decidir quando acordar alguém às 3 da manhã.

Considere:

CPU = 95%

Se o serviço continua cumprindo todos os SLOs, talvez não exista impacto imediato.

Pode ser um sinal de capacidade futura.

Merece investigação.

Mas talvez não mereça pager.

Agora considere:

CPU = 30%

enquanto 20% dos pagamentos falham por erro em uma dependência externa.

Infraestrutura verde.

Usuários sofrendo.

O alerta mais importante deve representar impacto no serviço.

Essa é uma das razões para usar alertas baseados em consumo de Error Budget.

O que é burn rate?

Burn rate representa a velocidade com que estamos consumindo o Error Budget.

Uma burn rate de:

1

significa que estamos consumindo o budget exatamente na velocidade que faria ele acabar no final da janela.

Uma burn rate de:

2

significa consumo duas vezes mais rápido.

Uma burn rate de:

10

significa dez vezes mais rápido.

Considere um SLO de:

99,9%

A taxa de erro permitida é:

0,1%

ou:

0,001

Se a taxa atual de eventos ruins é:

1%

então:

burn rate
=
0,01 / 0,001
=
10

Estamos queimando o budget dez vezes mais rápido do que deveríamos.

Esse conceito permite criar alertas proporcionais ao impacto.

Calculando burn rate em PromQL

Suponha:

orders_requests_total

orders_requests_success_total

A taxa de erro em uma janela de uma hora pode ser calculada como:

1
-
(
  sum(
    rate(
      orders_requests_success_total[1h]
    )
  )
  /
  sum(
    rate(
      orders_requests_total[1h]
    )
  )
)

Para um SLO de 99,9%, a taxa de erro permitida é:

1 - 0,999 = 0,001

A burn rate fica:

(
  1
  -
  (
    sum(
      rate(
        orders_requests_success_total[1h]
      )
    )
    /
    sum(
      rate(
        orders_requests_total[1h]
      )
    )
  )
)
/
0.001

Se essa consulta retornar:

14

estamos consumindo o Error Budget quatorze vezes mais rápido do que o ritmo sustentável.

Agora temos uma métrica operacional poderosa.

Mas ainda falta decidir quando alertar.

Por que um único threshold de burn rate não basta

Imagine que configuramos:

alertar quando burn rate > 10

Durante dois minutos ocorre um pico.

O alerta dispara.

Cinco minutos depois tudo volta ao normal.

Talvez tenhamos criado ruído.

Agora imagine uma degradação pequena:

burn rate = 2

Ela não dispara o alerta.

Mas permanece durante três dias.

Esse problema lento pode consumir grande parte do budget.

Temos dois tipos de incidente.

Incidente rápido e grave.

Incidente lento e persistente.

Uma única janela não detecta ambos adequadamente.

Por isso alertas modernos de SLO usam múltiplas janelas.

Multi-window multi-burn-rate

A ideia é combinar:

uma janela longa

com:

uma janela curta

A janela longa confirma que o problema possui impacto significativo.

A janela curta confirma que ele ainda está acontecendo.

Para um alerta rápido, podemos observar algo como:

burn rate alta em 1 hora

e:

burn rate alta em 5 minutos

Para um alerta mais lento:

burn rate moderada em 6 horas

e:

burn rate moderada em 30 minutos

Um exemplo de regra Prometheus pode ser:

groups:
  - name: orders-slo-alerts

    rules:
      - alert: OrdersAvailabilityBudgetBurnFast

        expr: |
          (
            (
              1
              -
              (
                sum(
                  rate(
                    orders_requests_success_total[1h]
                  )
                )
                /
                sum(
                  rate(
                    orders_requests_total[1h]
                  )
                )
              )
            )
            / 0.001
          ) > 14.4
          and
          (
            (
              1
              -
              (
                sum(
                  rate(
                    orders_requests_success_total[5m]
                  )
                )
                /
                sum(
                  rate(
                    orders_requests_total[5m]
                  )
                )
              )
            )
            / 0.001
          ) > 14.4

        for: 2m

        labels:
          severity: page

        annotations:
          summary: >
            Orders API is consuming the availability
            error budget too quickly.

          description: >
            The 99.9% availability SLO is burning
            faster than the fast-burn threshold.

O número 14.4 não é mágico.

Ele vem de uma política de quanto budget aceitamos consumir dentro de determinada janela.

Uma estratégia conhecida para um SLO de 30 dias é detectar consumo de aproximadamente 2% do budget em uma hora, o que corresponde a uma burn rate de 14,4.

O objetivo aqui não é decorar o número.

É entender a relação:

janela de SLO

fração do budget que justifica ação

janela de observação

A partir disso calculamos thresholds coerentes.

Como pensar no cálculo do burn rate

Imagine:

janela do SLO = 30 dias

Isso equivale a:

720 horas

Se estamos dispostos a ser paginados quando 2% do budget pode ser consumido em uma hora, temos:

0,02 * 720 / 1
=
14,4

Para uma janela de seis horas consumindo 5%:

0,05 * 720 / 6
=
6

Isso gera dois níveis úteis:

Fast burn:
14,4x

Slow burn:
6x

Uma política comum poderia ser:

14,4x em 1h e 5m
-> pager

6x em 6h e 30m
-> pager ou alta prioridade

1x a 3x por períodos longos
-> ticket ou investigação

Os valores devem refletir a realidade operacional da empresa.

Uma equipe que opera pagamentos pode ter tolerância diferente de uma ferramenta interna de relatórios.

Um SLO não precisa gerar pager automaticamente

Esse ponto é importante.

Nem toda violação de SLO exige acordar alguém.

Considere um sistema interno usado apenas em horário comercial.

Um slow burn iniciado às 2 da manhã talvez possa gerar um ticket para o início do expediente.

Por outro lado, uma API de autenticação usada 24 horas pode exigir resposta imediata.

SLO define confiabilidade desejada.

Política de alerta define reação operacional.

São conceitos relacionados, mas diferentes.

Uma boa política separa:

Page

Ticket

Dashboard

Relatório

Page significa:

Alguém precisa agir agora.

Ticket significa:

Existe um problema relevante,
mas pode ser tratado em horário normal.

Dashboard significa:

Precisamos observar tendência.

Usar pager para tudo cria fadiga.

Depois de alertas falsos suficientes, o alerta realmente importante será ignorado.

Disponibilidade não deve ser global demais

Imagine uma plataforma com:

GET /health

GET /products

POST /checkout

POST /payments

GET /reports

Podemos calcular um único SLI global.

Mas isso pode esconder falhas críticas.

Suponha:

GET /products:
100 milhões de requisições

POST /payments:
100 mil requisições

Se todos os pagamentos falharem:

100 mil erros

a disponibilidade global ainda pode parecer muito alta por causa do enorme volume de leitura de produtos.

Para o negócio, o incidente é catastrófico.

Por isso devemos agrupar SLIs por jornadas ou classes de serviço relevantes.

Por exemplo:

SLO de navegação

SLO de checkout

SLO de pagamento

SLO de relatórios

Mas existe o risco oposto.

Criar um SLO por endpoint produz dezenas ou centenas de objetivos.

Isso se torna impossível de operar.

A solução normalmente é agrupar por experiência de usuário ou criticidade.

Pense em jornadas.

Não em controllers.

SLI baseado na jornada completa

Considere uma compra com quatro etapas:

Criar carrinho

Calcular frete

Autorizar pagamento

Confirmar pedido

Cada endpoint pode funcionar 99,9% das vezes.

Mas o usuário precisa que os quatro funcionem na mesma jornada.

Uma aproximação simplificada da confiabilidade conjunta seria:

0,999 ^ 4
≈ 99,6%

Ou seja, serviços individualmente "três noves" podem produzir uma jornada com disponibilidade menor.

Isso mostra por que medir apenas componentes internos é insuficiente.

Quando possível, crie indicadores próximos da jornada final.

Por exemplo:

checkout_started_total

checkout_completed_total

Uma razão entre conclusões válidas e tentativas válidas pode capturar uma dimensão de sucesso de negócio que nenhum endpoint isolado mostra.

É preciso cuidado porque abandono voluntário do usuário não deve ser confundido com falha técnica.

Esse tipo de SLI exige entendimento do produto e dos eventos de domínio.

Correctness também pode ser SLI

Nem toda falha é indisponibilidade ou latência.

Imagine uma API de precificação.

Ela responde:

HTTP 200

em:

80 ms

mas calcula o preço errado.

Disponibilidade perfeita.

Latência excelente.

Sistema incorreto.

Para alguns sistemas, correctness é o indicador mais importante.

Podemos ter:

SLI:
percentual de cálculos validados corretamente

ou:

SLI:
percentual de documentos processados sem divergência

Esses indicadores são mais difíceis de medir.

Mas a dificuldade de medir não reduz sua importância.

Um dos princípios mais úteis ao definir SLOs é começar pelo que o usuário realmente valoriza.

Depois descobrimos como medir.

Fazer o inverso, começar apenas pelas métricas mais fáceis disponíveis, tende a produzir SLOs tecnicamente elegantes e operacionalmente irrelevantes.

SLO para jobs e pipelines assíncronos

Nem todo sistema responde requisições HTTP.

Considere um pipeline que processa documentos.

O usuário envia um arquivo.

O processamento pode levar minutos.

Disponibilidade HTTP diz pouco sobre a experiência.

SLIs melhores poderiam ser:

99% dos documentos válidos
processados com sucesso

e:

95% dos documentos
processados em menos de 5 minutos

Agora temos sucesso e freshness.

Uma métrica .NET poderia registrar o tempo completo do workflow:

using System.Diagnostics.Metrics;

public sealed class DocumentProcessingMetrics
{
    private readonly Counter<long> _started;
    private readonly Counter<long> _completed;
    private readonly Histogram<double> _duration;

    public DocumentProcessingMetrics(
        IMeterFactory meterFactory)
    {
        var meter =
            meterFactory.Create(
                "Logby.Documents");

        _started =
            meter.CreateCounter<long>(
                "documents.processing.started",
                unit: "{document}");

        _completed =
            meter.CreateCounter<long>(
                "documents.processing.completed",
                unit: "{document}");

        _duration =
            meter.CreateHistogram<double>(
                "documents.processing.duration",
                unit: "s");
    }

    public void Started(
        string documentType)
    {
        _started.Add(
            1,
            new KeyValuePair<string, object?>(
                "document.type",
                documentType));
    }

    public void Completed(
        string documentType,
        TimeSpan duration)
    {
        _completed.Add(
            1,
            new KeyValuePair<string, object?>(
                "document.type",
                documentType));

        _duration.Record(
            duration.TotalSeconds,
            new KeyValuePair<string, object?>(
                "document.type",
                documentType));
    }
}

Aqui estamos medindo o workflow, não apenas uma chamada HTTP.

Se o arquivo entra em uma fila, passa por três workers e termina dez minutos depois, o SLI deve enxergar o tempo end-to-end.

Esse é um erro comum em arquiteturas assíncronas.

Cada componente possui latência excelente individualmente, mas o usuário espera horas porque existe backlog entre as etapas.

SLIs devem acompanhar o resultado.

Como escolher o número do SLO

Escolher:

99%

99,9%

99,99%

não deveria ser um exercício de vaidade.

Cada nove adicional pode aumentar drasticamente o custo de engenharia.

Considere disponibilidade mensal aproximada.

99%
permite cerca de 7h12m

99,9%
permite cerca de 43m12s

99,99%
permite cerca de 4m19s

99,999%
permite cerca de 26s

Esses números são uma simplificação baseada em 30 dias e tempo contínuo, mas ajudam a visualizar a diferença.

Sair de 99,9% para 99,99% não é uma melhoria pequena.

O tempo equivalente de falha cai de dezenas de minutos para poucos minutos.

Isso pode exigir:

redundância entre zonas

failover automatizado

deploy progressivo

capacidade ociosa

testes de disaster recovery

eliminação de single points of failure

on-call 24x7

engenharia adicional

Talvez tudo isso faça sentido para pagamentos.

Talvez não faça sentido para um painel administrativo usado duas vezes por semana.

O SLO deve refletir valor e risco.

Não defina SLO baseado apenas no melhor mês

Suponha que uma API teve:

Janeiro: 99,99%

Fevereiro: 99,98%

Março: 99,999%

É tentador definir:

SLO = 99,99%

porque "já entregamos isso".

Mas talvez esse desempenho dependa de:

engenheiros fazendo trabalho manual

deploys evitados

capacidade excessiva

incidentes resolvidos por sorte

on-call sobrecarregado

Um SLO precisa ser sustentável.

A pergunta não é apenas:

Conseguimos atingir?

Também é:

Vale a pena garantir esse nível
de forma consistente?

Error Budget como ferramenta de priorização

Imagine duas equipes discutindo.

Produto:

Precisamos lançar novas features.

Plataforma:

Precisamos parar tudo
e melhorar resiliência.

Sem uma referência objetiva, essa discussão vira opinião.

Agora adicione Error Budget.

Situação A:

SLO: 99,9%

Disponibilidade atual: 99,98%

Budget quase intacto

Talvez possamos assumir mais risco.

Situação B:

SLO: 99,9%

Disponibilidade atual: 99,72%

Budget esgotado

Agora existe evidência forte para priorizar confiabilidade.

O Error Budget não decide automaticamente a estratégia.

Mas cria uma linguagem comum.

Produto e engenharia deixam de discutir:

"estável"

"instável"

"acho arriscado"

"parece bom"

e passam a discutir dados.

Uma política simples de Error Budget

Uma organização pode começar com algo simples:

Budget restante > 50%

Mudanças normais.
Deploy contínuo.
Experimentos permitidos.
Budget entre 20% e 50%

Revisar mudanças de alto risco.
Aumentar atenção a regressões.
Priorizar problemas recorrentes.
Budget abaixo de 20%

Reduzir mudanças não essenciais.
Priorizar trabalho de confiabilidade.
Exigir rollout mais conservador.
Budget esgotado

Congelar mudanças de risco,
exceto correções, segurança
e ações para restaurar confiabilidade.

Isso é apenas um exemplo.

A política precisa ser negociada pela organização.

O mais importante é defini-la antes do incidente.

Criar regras durante uma crise tende a produzir decisões políticas e inconsistentes.

SLOs precisam ter ownership

Um SLO sem dono vira um gráfico decorativo.

Cada SLO deveria ter pelo menos:

Serviço ou jornada

Owner

SLI

Objetivo

Janela

Fonte dos dados

Política de Error Budget

Alertas associados

Podemos documentar assim:

service: orders-api
owner: commerce-team

slos:
  - name: availability

    sli:
      good_events:
        metric: orders_requests_success_total

      valid_events:
        metric: orders_requests_total

    objective: 99.9

    window: 30d

    alerting:
      fast_burn: page
      slow_burn: ticket

  - name: latency

    sli:
      description: >
        Valid order creation requests
        completed within 500 ms.

    objective: 99.0

    window: 30d

Esse arquivo não precisa necessariamente alimentar automação.

Mesmo como documentação, ele força clareza.

Quem lê consegue responder:

O que estamos medindo?

Qual é o objetivo?

Quem responde?

Como reagimos?

Em ambientes maduros, essa definição pode virar configuração versionada e gerar recording rules, dashboards e alertas automaticamente.

Recording rules evitam PromQL impossível de manter

Consultas de SLO podem ficar longas.

Repetir a fórmula completa em dashboards e alertas aumenta risco de inconsistência.

Prometheus recording rules podem pré-calcular indicadores.

Por exemplo:

groups:
  - name: orders-sli

    rules:
      - record: orders:sli_availability:rate5m

        expr: |
          sum(
            rate(
              orders_requests_success_total[5m]
            )
          )
          /
          sum(
            rate(
              orders_requests_total[5m]
            )
          )

      - record: orders:error_rate:rate5m

        expr: |
          1
          -
          orders:sli_availability:rate5m

      - record: orders:error_budget_burn:rate5m

        expr: |
          orders:error_rate:rate5m
          /
          0.001

Agora dashboards podem consultar:

orders:sli_availability:rate5m

em vez de repetir uma expressão enorme.

Alertas também ficam mais legíveis.

Isso reduz duplicação e facilita revisão.

Mas recording rules introduzem uma responsabilidade.

Se a regra estiver errada, todos os dashboards estarão consistentemente errados.

Trate regras de SLO como código.

Revise em Pull Request.

Teste consultas.

Valide com incidentes conhecidos.

SLO não substitui observabilidade detalhada

Pode parecer que, depois de criar SLOs, métricas de CPU e traces deixam de importar.

É o contrário.

SLO responde:

Existe impacto relevante?

Observabilidade detalhada responde:

Por que existe impacto?

O fluxo ideal de incidente pode ser:

Burn-rate alert dispara

        ↓

Dashboard de SLO confirma
qual jornada está degradada

        ↓

Métricas RED mostram
a operação afetada

        ↓

Trace exemplifica
onde a latência está concentrada

        ↓

Logs estruturados mostram
o erro específico

        ↓

Métricas de infraestrutura
confirmam a causa

Isso conecta toda a série de observabilidade.

OpenTelemetry coleta sinais.

Collector transporta e processa.

SLI organiza indicadores.

SLO define expectativa.

Error Budget transforma confiabilidade em decisão.

Alertas detectam consumo anormal.

Traces, logs e métricas ajudam a explicar a causa.

Cada camada possui uma função.

Armadilhas comuns

Criar SLO para tudo

Se existem 150 SLOs, provavelmente ninguém sabe quais realmente importam.

Comece com poucas jornadas críticas.

Usar CPU como SLI

CPU é uma métrica de recurso.

Ela pode explicar degradação, mas raramente representa diretamente experiência do usuário.

Considerar todo HTTP não 2xx como erro

404, 401 e 429 podem representar comportamentos esperados.

Defina eventos bons e válidos semanticamente.

Misturar health checks com tráfego de usuário

Milhões de health checks bem-sucedidos podem inflar artificialmente a disponibilidade.

Exclua tráfego que não representa experiência real.

Definir SLO igual ao SLA

Isso elimina margem operacional.

Considere um objetivo interno mais rigoroso quando fizer sentido.

Escolher 99,99% porque parece melhor

Cada nove custa.

Escolha com base no valor para o usuário e no custo de entrega.

Alertar apenas quando o SLO já foi violado

Quando uma janela de 30 dias cruza o objetivo, pode ser tarde demais.

Burn-rate alerts detectam trajetórias perigosas antes.

Usar apenas uma janela de alerta

Janelas curtas geram ruído.

Janelas longas detectam incidentes graves tarde demais.

Combine múltiplas janelas.

Criar Error Budget sem política

Um gráfico mostrando "budget restante" não muda comportamento sozinho.

Defina o que acontece quando o budget é saudável, baixo ou esgotado.

Um guia de decisão prático

Use SLI de disponibilidade quando o usuário precisa conseguir executar uma operação.

Use SLI de latência quando sucesso lento também representa experiência ruim.

Use freshness para pipelines, sincronizações e processamento assíncrono.

Use correctness quando retornar a resposta errada é tão ruim ou pior que não responder.

Prefira SLIs baseados em eventos quando o volume varia muito ao longo do tempo.

Use métricas técnicas padronizadas quando elas representam corretamente o serviço.

Adicione métricas de negócio quando status HTTP não representa o resultado real.

Use SLOs diferentes para jornadas com criticidades realmente diferentes.

Evite um SLO por endpoint.

Use Error Budget para orientar risco e priorização, não para punir equipes.

Use burn-rate alerts para detectar consumo perigoso antes que o SLO seja completamente perdido.

Por onde começar em uma empresa que ainda não usa SLO

Não comece criando vinte dashboards.

Escolha um serviço importante.

Identifique uma jornada crítica.

Por exemplo:

Usuário cria um pedido.

Defina dois SLIs.

Disponibilidade

Latência

Escreva as definições em linguagem humana.

Por exemplo:

Disponibilidade:

Percentual de tentativas válidas
de criação de pedido que terminam
com o pedido persistido corretamente.
Latência:

Percentual de tentativas válidas
de criação de pedido concluídas
em menos de 500 ms.

Depois descubra quais métricas representam essas definições.

Talvez métricas HTTP sejam suficientes.

Talvez você precise criar um evento de negócio.

Colete dados por algumas semanas.

Não escolha o SLO no primeiro dia apenas porque 99,9% parece familiar.

Entenda o comportamento atual.

Converse com produto.

Entenda impacto para usuários.

Defina um objetivo inicial.

Crie o Error Budget.

Só então configure alertas.

Essa sequência evita transformar SLO em mais uma ferramenta de monitoramento criada sem propósito.

Um exemplo final completo

Imagine uma plataforma de e-commerce.

Para checkout definimos:

SLI de disponibilidade:

checkouts concluídos tecnicamente
/
tentativas válidas de checkout

Objetivo:

99,9% em 30 dias

Também definimos:

SLI de latência:

checkouts concluídos em <= 2 segundos
/
checkouts válidos

Objetivo:

99% em 30 dias

Agora suponha:

Disponibilidade:
99,96%

Latência:
98,7%

O primeiro SLO está saudável.

O segundo foi violado.

Se olhássemos apenas taxa de erro HTTP, poderíamos concluir:

Sistema saudável.

Mas usuários estão esperando demais.

Investigamos traces.

Descobrimos:

payment-service

com latência crescente.

Os logs mostram retries.

As métricas mostram saturação no pool de conexões.

O SLO não encontrou a causa.

Ele nos disse que existia impacto relevante.

OpenTelemetry, traces, logs e métricas nos ajudaram a explicar por quê.

Essa é a relação correta entre SRE e observabilidade.

Conclusão

Ter métricas não significa saber se um sistema está saudável.

Ter dashboards não significa saber quando agir.

Ter alertas não significa que estamos protegendo a experiência do usuário.

SLIs, SLOs e Error Budgets criam uma camada de significado sobre a telemetria.

O SLI define o que representa qualidade.

O SLO define quanto dessa qualidade queremos entregar.

O Error Budget define quanto de falha estamos dispostos a aceitar.

Burn rate mostra a velocidade com que estamos consumindo essa margem.

Quando esses conceitos são usados corretamente, confiabilidade deixa de ser uma discussão subjetiva.

Produto e engenharia ganham uma linguagem comum.

Podemos decidir quando assumir risco.

Podemos decidir quando desacelerar mudanças.

Podemos distinguir incidentes que exigem resposta imediata de problemas que podem virar trabalho planejado.

E podemos finalmente responder à pergunta que dashboards tradicionais frequentemente não conseguem responder:

o sistema está entregando uma experiência boa o suficiente para quem realmente importa, o usuário?

No próximo passo natural dessa série, podemos usar esses SLOs para construir dashboards de Grafana orientados a incidentes.

Em vez de dashboards cheios de gráficos sem hierarquia, podemos organizar a investigação a partir de SLO, RED, Golden Signals e drill-down entre métricas, traces e logs.

A observabilidade deixa de ser um conjunto de ferramentas.

Ela passa a funcionar como um sistema operacional para entender confiabilidade.

Fontes

← voltar ao índice