Um SLI é uma medição do que os usuários experimentaram, um SLO é a meta que você define para essa medição, um SLA é um contrato que diz o que acontece quando a meta não é atingida, e o orçamento de erros (error budget) é a diferença entre 100 por cento e o SLO, contada em requisições falhas que você tem permissão de ter. Um SLO de 99,9 por cento em 30 dias, em um serviço que atende 10 milhões de requisições, é um orçamento de 10.000 falhas. Esses quatro números são como um SRE responde à pergunta que o resto da organização não consegue: quão confiável este serviço deveria ser, e é seguro entregar hoje?

Esta é a Parte 2 de Budget for Failure. A Parte 1 descreveu o papel do SRE e terminou na sua dependência: nada daquilo funciona sem um número. Esta parte ainda é nível 101. Ela define os termos, lê os SLAs da AWS pelo que eles de fato prometem, calcula um primeiro SLI a partir de métricas de load balancer que você já tem e transforma o resultado em um orçamento com uma política anexada.

SLI: a razão entre eventos bons e eventos válidos

Um service level indicator é um número entre 0 e 100 por cento, calculado sempre da mesma forma: eventos bons divididos por eventos válidos. Para um serviço orientado a requisições, os dois SLIs mais úteis são:

  • Disponibilidade: requisições respondidas sem erro de servidor, divididas por todas as requisições.
  • Latência: requisições respondidas mais rápido que um limiar (digamos 300 ms), divididas por todas as requisições.

Duas decisões de design se escondem nessa definição. Primeiro, o que conta como válido. Um 404 para uma URL que não existe é o serviço funcionando corretamente; um 429 do seu próprio rate limiter é discutível; um 500 não é. Decida uma vez e escreva, porque o denominador é onde os SLIs são silenciosamente manipulados. Segundo, onde você mede. Quanto mais perto do usuário, mais honesto o número. Uma métrica colhida no load balancer enxerga falhas que a aplicação nunca registra, como um target que recusou a conexão. Uma métrica colhida dentro da aplicação as perde.

Médias não se qualificam como SLI. Uma latência média de 120 ms é compatível com uma requisição em cada vinte levando quatro segundos. A forma de razão ("97,2 por cento das requisições abaixo de 300 ms") diz quantos usuários passaram por uma experiência ruim, que é o que você está tentando limitar.

SLO: a meta e a janela em que ela é medida

Um service level objective é um SLI mais uma meta mais uma janela de tempo: "99,9 por cento das requisições têm sucesso, medido em uma janela móvel de 30 dias". As três partes são obrigatórias. Uma meta sem janela é infalsificável, já que qualquer hora ruim pode ser diluída na média escolhendo um período mais longo depois do fato.

A meta é onde entram os noves, e os noves são mais fáceis de raciocinar como tempo de indisponibilidade permitido. Para uma janela de 30 dias (43.200 minutos):

SLOTempo ruim permitido em 30 diasRequisições ruins permitidas por 10 milhões
99%7 horas e 12 minutos100.000
99,5%3 horas e 36 minutos50.000
99,9%43 minutos e 12 segundos10.000
99,95%21 minutos e 36 segundos5.000
99,99%4 minutos e 19 segundos1.000
99,999%26 segundos100

Leia a tabela de baixo para cima. Em 99,99 por cento você tem cerca de quatro minutos por mês. Um humano acionado às 03:00 precisa de mais do que isso para encontrar um laptop, o que significa que um SLO de quatro noves é um compromisso com detecção automatizada e recuperação automatizada, com pessoas envolvidas apenas depois. Cada nove extra é mais ou menos um corte de dez vezes na falha permitida e, na prática, um salto grande em custo. O SLO certo é o número mais baixo acima do qual seus usuários não notariam diferença, não o número mais alto que fica bonito num slide.

Cem por cento nunca é a meta certa. Ela proíbe todo deploy, toda migração e toda dependência que você não controla, e seus usuários chegam até você por Wi-Fi doméstico e redes móveis bem menos confiáveis do que qualquer coisa que você opera.

SLA: o contrato, e o que a AWS realmente te deve

Um service level agreement é um SLO com consequências anexadas, assinado com um cliente. A consequência normalmente é dinheiro. Essa é toda a diferença, e é por isso que o SLA é sempre mais frouxo que o SLO interno: você quer perceber e reagir bem antes de uma violação virar reembolso.

O SLA da AWS é o que a maioria de nós lê primeiro, e vale ler com atenção porque a redação ensina o conceito. Quatro deles que ficam embaixo de um serviço web típico:

Serviço e condiçãoCompromisso de uptime mensalCrédito quando não cumprido
EC2, instâncias em duas ou mais AZs (Region-Level)99,99%10%, 30% abaixo de 99,0%, 100% abaixo de 95,0%
EC2, uma única instância (Instance-Level)99,5%10%, 30% abaixo de 99,0%, 100% abaixo de 95,0%
Elastic Load Balancing, load balancer em duas ou mais AZs99,99%10%, 30% abaixo de 99,0%, 100% abaixo de 95,0%
RDS, instância ou cluster de banco Multi-AZ99,95%10%, 25% abaixo de 99,0%, 100% abaixo de 95,0%
S3 Standard99,9%10%, 25% abaixo de 99,0%, 100% abaixo de 95,0%

Três coisas nesses documentos importam mais do que as porcentagens.

O remédio é um crédito de serviço. Se o S3 Standard cair para 99,5 por cento em um mês, você fica elegível a 10 por cento das cobranças de S3 daquele mês na Região afetada, aplicados contra uma fatura futura, depois de abrir uma reclamação com evidências. Sua receita perdida, as penalidades dos seus próprios SLAs e o seu fim de semana não fazem parte do cálculo. Um SLA é um termo de precificação. Não é uma apólice de seguro e não é uma previsão de como o serviço vai se comportar.

O compromisso é condicional à sua arquitetura. Os 99,99 por cento do EC2 só valem quando todas as instâncias em execução estão distribuídas em duas ou mais Availability Zones da Região. Rode em uma única zona e você está no compromisso de 99,5 por cento no nível de instância, que permite cerca de 3,6 horas de indisponibilidade por mês. O SLA codifica o design que a AWS espera de você.

"Indisponível" tem uma definição legal estreita. Cada SLA define sua própria medição, por minuto ou por intervalo de cinco minutos, e suas próprias exclusões. Lento não é indisponível. Taxas de erro elevadas em uma API da qual você depende podem não contar de jeito nenhum.

Seu primeiro SLI, a partir de métricas que você já tem

Se o seu serviço está atrás de um Application Load Balancer, o CloudWatch já guarda um SLI de disponibilidade. Três métricas no namespace AWS/ApplicationELB bastam: RequestCount, HTTPCode_Target_5XX_Count (erros que o seu código retornou) e HTTPCode_ELB_5XX_Count (erros que o próprio load balancer gerou, por exemplo quando nenhum target saudável respondeu). Salve isto como sli.json:

[
  { "Id": "req", "ReturnData": false,
    "MetricStat": { "Stat": "Sum", "Period": 86400,
      "Metric": { "Namespace": "AWS/ApplicationELB", "MetricName": "RequestCount",
        "Dimensions": [{ "Name": "LoadBalancer", "Value": "app/checkout/0123456789abcdef" }] } } },
  { "Id": "t5xx", "ReturnData": false,
    "MetricStat": { "Stat": "Sum", "Period": 86400,
      "Metric": { "Namespace": "AWS/ApplicationELB", "MetricName": "HTTPCode_Target_5XX_Count",
        "Dimensions": [{ "Name": "LoadBalancer", "Value": "app/checkout/0123456789abcdef" }] } } },
  { "Id": "e5xx", "ReturnData": false,
    "MetricStat": { "Stat": "Sum", "Period": 86400,
      "Metric": { "Namespace": "AWS/ApplicationELB", "MetricName": "HTTPCode_ELB_5XX_Count",
        "Dimensions": [{ "Name": "LoadBalancer", "Value": "app/checkout/0123456789abcdef" }] } } },
  { "Id": "sli", "Label": "availability_percent",
    "Expression": "100 * (req - FILL(t5xx, 0) - FILL(e5xx, 0)) / req" }
]
aws cloudwatch get-metric-data \
  --start-time "$(date -u -v-30d +%Y-%m-%dT00:00:00Z)" \
  --end-time "$(date -u +%Y-%m-%dT00:00:00Z)" \
  --metric-data-queries file://sli.json \
  --query 'MetricDataResults[0].[Timestamps,Values]'

Isso retorna um valor de disponibilidade por dia para os últimos 30 dias (a sintaxe date -v é do macOS; no Linux use date -u -d '30 days ago'). O FILL(…, 0) importa: o CloudWatch não publica datapoint para uma métrica de erro em um período com zero erros, e sem o fill um dia perfeito volta como dado ausente em vez de 100 por cento.

Para latência, o mesmo namespace tem TargetResponseTime, e a estatística de percentile rank do CloudWatch a transforma diretamente na forma de razão: PR(:0.3) retorna a porcentagem de requisições concluídas em 300 ms ou menos.

O error budget: o que o SLO te deixa gastar

Uma vez que o negócio concordou que 99,9 por cento é bom o suficiente, os 0,1 por cento restantes deixam de ser um defeito a eliminar e viram uma reserva a usar. Se o serviço está em 99,97 por cento com dez dias restantes na janela, há espaço para correr risco: entregar a migração, atualizar o cluster, rodar o teste de failover. Se está em 99,88 por cento, não há, e ninguém precisa vencer uma discussão para estabelecer isso. Um serviço que termina toda janela com o orçamento intocado também não é um triunfo: ou o SLO é mais frouxo do que os usuários precisam, ou o time está pagando por confiabilidade que ninguém pediu.

O orçamento pode ser contado em eventos ou em tempo, e os dois se comportam de forma diferente.

SLO                      99.9% over 30 days
requests in window       10,000,000
error budget             (1 - 0.999) x 10,000,000 = 10,000 failed requests

bad requests so far      6,400
budget consumed          6,400 / 10,000 = 64%
budget remaining         3,600 requests = 36%

Orçamentos baseados em eventos pesam a falha pelo tráfego. Dez minutos de queda total no pico de terça-feira custam muito mais do que dez minutos às 04:00 de domingo, o que corresponde ao que os usuários experimentaram. É a forma a preferir para qualquer coisa orientada a requisições.

Orçamentos baseados em tempo contam minutos ruins: 99,9 por cento em 30 dias são 43 minutos e 12 segundos. São mais fáceis de explicar a um não-engenheiro e servem para serviços sem fluxo constante de requisições, como um pipeline em lote ou uma ferramenta interna de baixo tráfego. O custo é que um minuto conta como totalmente ruim ou totalmente bom, então você precisa de uma regra para o que torna um minuto ruim (por exemplo, "taxa de erro acima de 1 por cento"), e uma queda parcial fica invisível ou é contada por inteiro.

A janela importa tanto quanto a meta. Uma janela móvel de 30 dias sempre reflete o passado recente, e um dia ruim sai do cálculo exatamente 30 dias depois; é a melhor opção para acompanhar a experiência do usuário. Uma janela de calendário zera no primeiro dia do mês, o que se alinha com relatórios e com a maioria dos SLAs, e tem uma propriedade ruim: um incidente no dia 2 gasta o orçamento por quatro semanas, enquanto o mesmo incidente no dia 30 é perdoado dois dias depois.

O que gasta o orçamento

Tudo que faz a requisição de um usuário falhar saca da mesma conta, seja quem for o causador. Esse é o ponto. Em ordem aproximadamente decrescente de quanto um time típico gasta:

  • Mudanças. Deploys, pushes de configuração, feature flags, migrações de schema. Na maioria das coleções de postmortems, mudança é a maior causa isolada de incidentes. Se você acompanha o change failure rate do DORA, já está medindo o maior gastador.
  • Dependências. Uma lentidão regional de API, um provedor de pagamento terceiro, um problema de DNS. É o seu orçamento mesmo quando a culpa não é sua, porque o usuário não se importa com de quem é a culpa.
  • Capacidade e carga. Throttling, caches frios, uma política de escalonamento que reage dois minutos tarde demais.
  • Trabalho planejado. Janelas de manutenção, testes de failover, game days. Um exercício deliberado de evacuação de zona que custa 300 requisições falhas é orçamento bem gasto, e ter um orçamento é o que torna o exercício aprovável.
  • Hardware e plataforma. Retirada de instâncias, eventos de zona. Menor do que as pessoas esperam, se a arquitetura for multi-AZ.

Mantenha um livro-razão. Depois de cada incidente ou mudança arriscada, registre quanto orçamento ela consumiu e a causa. Três meses desse registro dizem para onde o tempo de engenharia de confiabilidade deveria ir melhor do que qualquer discussão de roadmap.

DataEventoRequisições falhasFatia do orçamento
3 agoDeploy com configuração errada de pool de conexões, rollback em 9 minutos3.10031%
11 agoLatência do provedor de pagamento, timeouts no checkout2.20022%
14 agoTeste planejado de failover do RDS3503,5%
Janela inteiraErros de fundo, sem causa única7507,5%

Neste exemplo, um deploy custou mais do que uma queda de terceiro e um teste planejado juntos. O caso de investimento se escreve sozinho: análise de canary e rollback automático antes de qualquer outra coisa.

A política é o produto

Um orçamento sem consequência é um dashboard. O que o faz funcionar é uma política escrita e curta, acordada com antecedência pela liderança de engenharia e de produto, que diz o que muda quando o orçamento fica baixo. O Google SRE Workbook publica uma política de exemplo cuja estrutura vale copiar. Uma primeira versão funcional cabe em meia página:

  • Orçamento acima de 50 por cento: processo normal de release. Mudanças arriscadas e experimentos são incentivados.
  • Orçamento abaixo de 50 por cento: mudanças precisam de uma etapa de canary e de um rollback testado. Nenhum experimento em produção.
  • Orçamento esgotado: releases de feature param até o serviço voltar para dentro do SLO. Só saem correções de confiabilidade, patches de segurança e mudanças necessárias para restaurar o orçamento. A prioridade do time são os itens principais dos postmortems recentes.
  • Um único incidente consome mais de 20 por cento do orçamento: postmortem obrigatório, com pelo menos um item de ação de alta prioridade.
  • Desacordo: um executivo nomeado decide. O caminho de escalonamento é escrito antes de ser necessário.

Duas propriedades fazem isso se sustentar. É simétrico: o mesmo documento que congela releases quando o orçamento acabou manda operações parar de bloqueá-los quando ele está saudável. E é previamente acordado: o dono do produto assinou numa semana calma, então invocá-lo numa semana ruim é execução, não confronto. Se a liderança não assina uma política que pode parar trabalho de feature, você ainda não tem um SLO. Você tem uma métrica.

Deixe o CloudWatch manter o livro-razão

O SLI acima foi calculado à mão com get-metric-data. Para o orçamento corrente, o CloudWatch tem um recurso gerenciado de SLO dentro do Application Signals. Você define o indicador, a meta de atingimento e o intervalo, e ele acompanha continuamente o atingimento e o error budget restante. Três escolhas no modelo dele mapeiam diretamente para este post:

  • Baseado em período ou baseado em requisição. Um SLO baseado em período divide o intervalo em períodos (um minuto, por exemplo), julga cada um como bom ou ruim e reporta períodos bons sobre períodos totais. Um SLO baseado em requisição conta requisições boas sobre requisições totais. São os orçamentos baseados em tempo e em eventos descritos acima.
  • Intervalo móvel ou de calendário. Ambos são suportados, com o trade-off já descrito.
  • Sobre o que o SLI é construído. Serviços instrumentados com Application Signals ganham métricas padrão de latência e disponibilidade, com respostas 5xx contadas como falhas e respostas 4xx tratadas como sucesso. Se você não está instrumentado, um SLO pode ser definido sobre qualquer métrica do CloudWatch ou expressão de metric math que resulte em uma única série temporal, o que inclui a expressão de load balancer acima.

O console mostra o resultado como dois números: atingimento contra a meta e orçamento restante. Para um SLO baseado em requisição, o orçamento restante pode subir além de descer durante o intervalo, porque ele depende da razão entre requisições boas e totais até o momento, e uma semana tranquila de tráfego limpo dilui um incidente anterior.

Modos de falha e o que observar

Tirar média de porcentagens. Um dia com 100 requisições a 90 por cento e um dia com um milhão de requisições a 99,99 por cento não dão média de 95. O SLI de 30 dias é o total de requisições boas sobre o total de requisições válidas. Some os numeradores e os denominadores, depois divida.

Um denominador com buracos. RequestCount só conta requisições para as quais o load balancer conseguiu escolher um target, então a expressão acima superestima ligeiramente o dano em uma queda total. Essa é a direção segura de errar. O SLI também não enxerga requisições que nunca chegaram ao load balancer: uma falha de DNS, um bloqueio de WAF, um cliente numa rede quebrada.

O SLO que ninguém acordou. Uma meta escolhida só pelo time de plataforma não tem autoridade quando colide com uma data de lançamento. O número precisa ser de quem é dono do roadmap.

SLOs demais. Comece com um ou dois por serviço voltado ao usuário: disponibilidade e latência no caminho de requisição que gera a receita. Vinte SLOs produzem vinte orçamentos, um dos quais está sempre esgotado, e a política vira ruído.

Definir a meta antes de ver os dados. Rode a consulta contra os últimos 30 dias primeiro. Um SLO escolhido antes de você ter visto o SLI é um desejo. Um definido ligeiramente abaixo do histórico medido, e depois apertado de propósito, é uma meta que você consegue defender.

Descobrir no fim do mês. Um orçamento lido num relatório mensal só consegue explicar o passado. Agir enquanto ainda há orçamento exige um alerta sobre a velocidade do gasto, e fazer o serviço gastar menos exige mudanças no código dele. Ambos são a Parte 3.

Leia isto a seguir

Para consultoria em AWS, arquitetura de nuvem, prática de SRE, pipelines de CI/CD, Linux e mais, comece em ercanermis.com.

Referências