DevOps Admin

Incident Management: Runbooks, Post-mortems e Cultura de Confiabilidade na Prática Já leu

Incident Management: Fundamentos e Importância Incident Management é a disciplina de detectar, responder e resolver problemas em sistemas em produção com agilidade e qualidade. Não se trata apenas de apagar incêndios — é uma abordagem estruturada que minimiza o impacto nos usuários, aprende com os problemas e constrói sistemas mais confiáveis. Em empresas modernas, especialmente aquelas com arquitetura em microsserviços ou deploy contínuo, incidentes acontecem regularmente. A diferença entre empresas que prosperam e aquelas que falham está em como elas gerenciam essas crises. O objetivo central do Incident Management é reduzir o tempo médio de detecção (MTTD) e, principalmente, o tempo médio de resolução (MTTR). Quando um serviço crítico cai às 3 da manhã, você não quer improvisar — quer um plano já testado. Quer saber exatamente para onde correr, quem chamar e quais ferramentas usar. Runbooks, post-mortems e uma cultura forte de confiabilidade são os três pilares que transformam respostas caóticas em processos metódicos e eficientes. Runbooks: Seu Manual

Incident Management: Fundamentos e Importância

Incident Management é a disciplina de detectar, responder e resolver problemas em sistemas em produção com agilidade e qualidade. Não se trata apenas de apagar incêndios — é uma abordagem estruturada que minimiza o impacto nos usuários, aprende com os problemas e constrói sistemas mais confiáveis. Em empresas modernas, especialmente aquelas com arquitetura em microsserviços ou deploy contínuo, incidentes acontecem regularmente. A diferença entre empresas que prosperam e aquelas que falham está em como elas gerenciam essas crises.

O objetivo central do Incident Management é reduzir o tempo médio de detecção (MTTD) e, principalmente, o tempo médio de resolução (MTTR). Quando um serviço crítico cai às 3 da manhã, você não quer improvisar — quer um plano já testado. Quer saber exatamente para onde correr, quem chamar e quais ferramentas usar. Runbooks, post-mortems e uma cultura forte de confiabilidade são os três pilares que transformam respostas caóticas em processos metódicos e eficientes.

Runbooks: Seu Manual de Combate aos Incidentes

O que é um Runbook e por que importa

Um runbook é um documento técnico que contém instruções passo a passo para diagnosticar e resolver problemas conhecidos. É como um livro de receitas para engenheiros — quando algo dá errado, você abre o runbook, segue os passos e resolve o problema. A diferença crítica entre um runbook eficiente e um inútil é sua precisão, clareza e, acima de tudo, o fato de ser testado regularmente.

Runbooks bem construídos reduzem drasticamente o MTTR. Em um cenário real, um incidente que levaria 45 minutos para ser resolvido através de tentativa e erro pode ser resolvido em 5 minutos com um bom runbook. Além disso, diminui a pressão psicológica sobre quem está respondendo ao incidente — quando você sabe exatamente o que fazer, fica mais calmo e comete menos erros.

Estrutura prática de um Runbook

A estrutura de um runbook deve ser simples e direta. Inclua: título descritivo, breve resumo do problema, pré-requisitos (ferramentas, acesso necessário), passos detalhados em ordem sequencial, seção de troubleshooting alternativo, e um ponto de escalação claro.

Aqui está um exemplo real de runbook em Markdown:

# Runbook: API Gateway retornando erro 502

## Resumo
A API Gateway está respondendo com erro 502 (Bad Gateway), impossibilitando 
requisições dos clientes.

## Impacto
- Clientes não conseguem usar a plataforma
- Severidade: CRÍTICO
- Afeta: Serviço principal de requisições

## Pré-requisitos
- Acesso SSH aos servidores de produção
- Acesso ao dashboard de monitoramento (Datadog/New Relic)
- kubectl configurado para acessar o cluster Kubernetes

## Passos para resolução

### 1. Confirme o problema
```bash
curl -v https://api.exemplo.com/health
# Se receber 502, está confirmado. Se receber 200, volte 10 minutos no tempo
# (pode ter sido um problema transitório já resolvido)

2. Verifique os logs da API Gateway

kubectl logs -f deployment/api-gateway -n production | tail -100
# Procure por padrões de erro, connection timeouts ou Out of Memory

3. Verifique a saúde dos serviços upstream

kubectl get pods -n production | grep -E "auth-service|user-service|payment-service"
# Se algum pod está em CrashLoopBackOff, é o culpado

4. Verifique recursos do cluster

kubectl top nodes
kubectl top pods -n production
# Se CPU ou memória estão em 90%+, inicie um scale-up

5. Reinicie a API Gateway como último recurso

kubectl rollout restart deployment/api-gateway -n production
# Aguarde 2-3 minutos para o novo pod subir

Se nada funcionar

  • Escalável para time de SRE
  • Contate o oncall lead do backend
  • Considere fazer rollback da última deploy (veja runbook de rollback)

Validação final

curl -v https://api.exemplo.com/health
# Deve retornar 200 OK

Este runbook tem clareza operacional — cada passo é executável e testável. Note que não presume conhecimento prévio de Kubernetes; é acessível para qualquer engenheiro da equipe.

### Documentando runbooks em equipes reais

Em uma equipe profissional, runbooks não vivem em documentos esquecidos — vivem em repositórios Git, versionados como código. Muitas empresas usam plataformas como Confluence ou Notion, mas a tendência moderna é armazená-los junto ao código, em pasta `/runbooks` ou `/docs/incidents`.

```bash
# Estrutura recomendada em repositório Git
./docs/incidents/
├── api-gateway-502.md
├── database-connection-pool-exhausted.md
├── cache-redis-down.md
├── memory-leak-detection.md
└── payment-processing-lag.md

Integre com seu processo de CI/CD: quando um runbook é atualizado, notifique a equipe. Quando um incidente ocorre, ligue o runbook diretamente no seu sistema de alertas (ferramentas como PagerDuty ou Opsgenie fazem isso nativamente).

Post-mortems: Aprendendo com os Fracassos

Por que post-mortems não são "culpar e punir"

Um post-mortem (ou incident review) é uma análise realizada após a resolução de um incidente significativo. Seu propósito único é aprender — identificar causas raiz, não culpados. Esta é a distinção mais importante: post-mortems em empresas com cultura de confiabilidade genuína nunca punem pessoas. Punem processos ruins.

Quando uma pessoa tem medo de ser culpada em um post-mortem, ela omite informações críticas, mente sobre decisões tomadas, e a aprendizagem morre. Quando uma pessoa confia que o objetivo é aprender juntos, ela relata honestamente: "Eu assumi que o arquivo .env estava configurado, não verifiquei" ou "Entrei em pânico e reiniciei o servidor sem pensar duas vezes". Esses insights são ouro para melhorar processos.

Estrutura de um Post-mortem eficaz

Um post-mortem deve responder cinco perguntas: O quê? (o que aconteceu), Quando? (timeline), Por quê? (causa raiz), Qual foi o impacto? (quantifique), Como evitamos no futuro? (ações). A sessão deve incluir o engenheiro que detectou o problema, quem respondeu, quem escalou, e interessados na causa raiz.

Aqui está um exemplo estruturado em template de post-mortem:

# Post-Mortem: Outage de 47 minutos no serviço de pagamento

## Data e Hora
- **Início detectado:** 2024-01-15 14:32 UTC
- **Resolução:** 2024-01-15 15:19 UTC
- **Duração:** 47 minutos

## O quê aconteceu?
O serviço de processamento de pagamentos retornou erro 500 para 100% das 
transações durante 47 minutos. Aproximadamente 3.200 transações falharam, 
com prejuízo estimado de R$ 85.000 em taxas não processadas.

## Timeline (em ordem cronológica)

| Tempo | Evento |
|-------|--------|
| 14:32 | Primeiro alerta dispara no Datadog (error rate > 10%) |
| 14:33 | On-call engineer recebe notificação via SMS |
| 14:35 | Engineer acessa dashboard, identifica timeout nas queries ao banco |
| 14:40 | Database team chamada, começa investigação |
| 14:42 | Descoberta: connection pool estava esgotado (max 100 conexões, todas em uso) |
| 14:45 | Identificada query de relatório de auditoria em background job usando 60 conexões |
| 14:47 | Background job manual encerrado |
| 14:52 | Aplicação reiniciada para limpar pool de conexão |
| 15:19 | Todas as métricas normalizadas, transações fluindo novamente |

## Causa Raiz

Um deploy em produção às 14:15 incluía um novo relatório de auditoria que 
roda a cada 5 minutos. Esse job usava uma conexão por linha retornada (não 
aplicava batch processing). Com crescimento de dados de auditoria, retornava 
60+ linhas, consumindo 60+ conexões simultaneamente.

**Por que não foi detectado em staging?** O volume de dados em staging é 
apenas 2% do volume de produção — o problema não foi reproduzido.

## Impacto

- **Taxa de transação afetada:** 100% (todas as transações falharam)
- **Número de transações:** ~3.200
- **Prejuízo direto:** ~R$ 85.000 (taxa de processamento não paga)
- **Satisfação do cliente:** 200+ clientes reportaram falhas
- **Reputação:** Sem impacto significativo (bem menos de 1 hora)

## O que aprendemos

1. **Testes de carga em staging devem usar dados reais (ou proporção real).**
   - Background jobs podem comportar-se de forma totalmente diferente com dados reais.

2. **Connection pooling precisa de alertas mais agressivos.**
   - Devíamos alertar quando pool > 80%, não quando 100%.

3. **Batch processing deve ser a regra, não exceção.**
   - Processar 1 linha = 1 conexão é um anti-padrão.

## Ações de melhoria (e proprietários)

| Ação | Proprietário | Prazo | Prioridade |
|------|-------------|-------|-----------|
| Implementar teste de carga com snapshot de dados reais | Engenheiro A | 2 sprints | ALTA |
| Adicionar alerta de conexão pool > 80% | Engenheiro B | 1 semana | CRÍTICA |
| Refatorar job de auditoria para usar batch processing | Engenheiro A | 3 sprints | ALTA |
| Documentar práticas de connection pooling | Tech Lead | 1 semana | MÉDIA |
| Implementar feature flag para novos jobs em background | DevOps | 2 semanas | ALTA |

## Conclusão

Este incidente foi **evitável**. O processo de testing não simulava volume real. 
Agora sabemos que testes de carga devem usar dados representativos, e que 
alertas devem disparar **antes** do esgotamento, não depois.

Frequência e distribuição de post-mortems

Nem todo incidente requer post-mortem — apenas os significativos. Uma regra prática: se durou mais de 5 minutos e afetou usuários, merece análise. Use critérios como severidade (P1, P2), número de usuários afetados, e impacto financeiro.

Agende o post-mortem dentro de 48 horas do incidente — enquanto a memória está fresca, mas com tempo suficiente para que o time respire. Sessões muito rápidas são defensivas; sessões muito lentas perdem detalhes.

Cultura de Confiabilidade: O Alicerce de Tudo

Confiabilidade como responsabilidade compartilhada

Muitas empresas separam desenvolvedores e operações: devs criam features, ops mantém sistemas vivos. Esta separação é a morte da confiabilidade. Quando um desenvolvedor não sente dor quando seu código causa um incidente às 3 da manhã, ele não aprendera a ser cuidadoso. Quando um operador não entende o código que mantém, não consegue propor melhorias inteligentes.

Uma cultura genuína de confiabilidade exige que todo engenheiro seja responsável pela confiabilidade do que cria. Isto significa: desenvolvedores em rotação de oncall, developers olhando seus próprios logs, product managers entendendo SLOs, e líderes recompensando resiliência tanto quanto velocidade.

SLOs, SLIs e Error Budget

SLO (Service Level Objective) é o alvo — por exemplo, "99.95% de uptime". SLI (Service Level Indicator) é a métrica que mede — por exemplo, "requisições bem-sucedidas / total de requisições". Error Budget é o tempo permitido de downtime antes de quebrar o SLO — com SLO de 99.95%, você tem ~22 minutos de downtime por mês.

Este conceito é crucial para uma cultura saudável: quando você tem budget de erro, pode iterar rápido. Quando o budget acaba, você desacelera e investe em estabilidade. Isto alinha desenvolvimento com confiabilidade.

# Exemplo: Calculador de error budget em Python

class ErrorBudgetCalculator:
    def __init__(self, slo_percentage: float, period_days: int = 30):
        self.slo = slo_percentage
        self.period_days = period_days
        self.total_seconds = period_days * 24 * 60 * 60

    def allowed_downtime_seconds(self) -> int:
        """Calcula tempo total permitido de downtime"""
        allowed_error_rate = (100 - self.slo) / 100
        return int(self.total_seconds * allowed_error_rate)

    def allowed_downtime_readable(self) -> str:
        """Converte para formato legível"""
        seconds = self.allowed_downtime_seconds()
        hours = seconds // 3600
        minutes = (seconds % 3600) // 60
        secs = seconds % 60
        return f"{hours}h {minutes}m {secs}s"

    def budget_consumed(self, downtime_seconds: int) -> float:
        """Percentual do budget gasto"""
        total_budget = self.allowed_downtime_seconds()
        return (downtime_seconds / total_budget) * 100

    def remaining_budget(self, downtime_seconds: int) -> str:
        """Budget restante"""
        total_budget = self.allowed_downtime_seconds()
        remaining = max(0, total_budget - downtime_seconds)
        minutes = remaining // 60
        secs = remaining % 60
        return f"{minutes}m {secs}s"

# Uso prático
if __name__ == "__main__":
    # SLO de 99.95% por mês
    calc = ErrorBudgetCalculator(slo_percentage=99.95, period_days=30)

    print(f"Total permitido (99.95%): {calc.allowed_downtime_readable()}")
    # Output: Total permitido (99.95%): 0h 21m 36s

    # Temos um incidente de 15 minutos
    downtime = 15 * 60  # 900 segundos
    print(f"Budget consumido: {calc.budget_consumed(downtime):.1f}%")
    # Output: Budget consumido: 69.4%

    print(f"Budget restante: {calc.remaining_budget(downtime)}")
    # Output: Budget restante: 6m 36s

Este framework muda conversas em reuniões. Em vez de "queremos 100% de uptime" (impossível), você diz "temos 6 minutos de budget restante esse mês — vamos desacelerar novas features e investir em testes?" Esta linguagem é objetiva e alinha toda a organização.

Prática: Chaos Engineering e testes de resiliência

Confiabilidade não é apenas sorte — é construída. Chaos Engineering é a prática de injetar falhas deliberadamente em produção para descobrir problemas antes deles descobrirem você. Parece contra-intuitivo, mas é profundamente eficaz.

Ferramentas como Chaos Monkey (Netflix), Gremlin, ou até scripts caseiros matam processos, saturram CPU, ou desligam servidores. O objetivo: encontrar gaps no seu sistema quando o dano é controlado, não quando afeta usuários reais.

# Exemplo: Ferramenta simples de chaos testing para endpoints

import requests
import random
import time
from typing import List, Callable

class ChaosTestRunner:
    def __init__(self, base_url: str, timeout: int = 10):
        self.base_url = base_url
        self.timeout = timeout
        self.results = []

    def test_endpoint_resilience(
        self, 
        endpoint: str, 
        method: str = "GET",
        iterations: int = 100,
        chaos_probability: float = 0.3
    ) -> dict:
        """
        Testa resiliência de um endpoint injetando latência aleatória
        """
        successful = 0
        failed = 0
        latencies = []

        for i in range(iterations):
            # 30% de chance de injetar latência caótica
            if random.random() < chaos_probability:
                time.sleep(random.uniform(0.5, 5))  # Latência de 0.5s a 5s

            try:
                start = time.time()
                if method == "GET":
                    response = requests.get(
                        f"{self.base_url}{endpoint}",
                        timeout=self.timeout
                    )
                else:
                    response = requests.post(
                        f"{self.base_url}{endpoint}",
                        timeout=self.timeout
                    )

                elapsed = time.time() - start
                latencies.append(elapsed)

                if response.status_code == 200:
                    successful += 1
                else:
                    failed += 1
                    print(f"[CAOS] Iteração {i}: Status {response.status_code}")

            except requests.exceptions.Timeout:
                failed += 1
                print(f"[CAOS] Iteração {i}: Timeout!")
            except Exception as e:
                failed += 1
                print(f"[CAOS] Iteração {i}: Erro - {type(e).__name__}")

        result = {
            "endpoint": endpoint,
            "successful_requests": successful,
            "failed_requests": failed,
            "success_rate": (successful / iterations) * 100,
            "avg_latency": sum(latencies) / len(latencies) if latencies else 0,
            "max_latency": max(latencies) if latencies else 0,
        }

        self.results.append(result)
        return result

# Uso prático
if __name__ == "__main__":
    chaos = ChaosTestRunner(base_url="http://localhost:8000")

    result = chaos.test_endpoint_resilience(
        endpoint="/api/users",
        iterations=50,
        chaos_probability=0.4
    )

    print(f"\n=== Resultado do Teste de Caos ===")
    print(f"Taxa de sucesso: {result['success_rate']:.1f}%")
    print(f"Latência média: {result['avg_latency']:.3f}s")
    print(f"Latência máxima: {result['max_latency']:.3f}s")

    if result['success_rate'] < 95:
        print("⚠️  ALERTA: Taxa de sucesso abaixo de 95%!")

Conclusão

Dominar Incident Management significa compreender três pilares interdependentes: Runbooks garantem respostas rápidas e consistentes, reduzindo tempo de resolução e pressão no time. Post-mortems transformam crises em aprendizado, eliminando problemas sistêmicos em vez de apenas sintomas. Cultura de confiabilidade alinha a organização em torno de resiliência, fazendo com que cada engenheiro sinta propriedade sobre o que cria.

A lição central que deve guiar seu aprendizado: incidentes são oportunidades, não tragédias. Empresas como Google, Netflix e Stripe não têm menos incidentes que startups — têm sistemas e processos para aprender com eles. Comece pequeno: documente seus próximos três problemas em runbooks, faça um post-mortem honesto da próxima falha, e observe sua equipe ganhar confiança e velocidade simultaneamente.

Referências


Artigos relacionados