Реализовать автоматический postmortem

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Реализовать автоматический postmortem

1. Цель задачи

Разработать систему, которая после фиксации инцидента автоматически собирает логи, метрики и трейсы из целевых сервисов и формирует структурированный postmortem-отчёт в течение 5 минут. Система должна интегрироваться с существующим стеком observability (Elasticsearch, Prometheus, Jaeger) и запускаться по триггеру (вебхук, команда в чате или UI). Ключевой результат Отчёт в формате Markdown/HTML доступен для команды не позднее 5 минут после закрытия инцидента.

2. Исходные данные

Что нужноОткуда взять
Время начала и окончания инцидентаСистема алертинга (Alertmanager), PagerDuty или ручной ввод
Логи всех затронутых сервисов за период инцидентаElasticsearch (или Loki) – индексы/метки сервисов
Метрики (CPU, память, RPS, ошибки) за период +/- 5 минутPrometheus – query_range API
Трейсы (trace_id, duration, статус) для request-ов упавших/медленныхJaeger (или Tempo) – поиск по сервису и временному окну
Список затронутых сервисовИз алерта или меток логирования
Конфигурация postmortem-шаблонаGit-репозиторий (шаблон в формате Jinja2)

Если нет реального инструмента — симулируем:

  1. Развернуть Docker Compose с elasticsearch:7.17, logstash, kibana, filebeat (или Loki + Promtail).
  2. Развернуть prometheus и node_exporter для метрик; добавить несколько тестовых меток (service="api-gateway", service="user-service").
  3. Развернуть jaeger all-in-one и эмитировать трейсы простым Python-скриптом (opentelemetry-sdk).
  4. Написать скрипт-генератор инцидента, который на заданный интервал создаёт аномалии в логах/метриках/трейсах.
  5. Использовать requests и curl для имитации запросов от Alertmanager.

3. Технологический стек

КомпонентИнструментыНазначение
Оркестрация инфраструктурыDocker ComposeЛокальный подъём стека observability
Хранение логовElasticsearch (Loki)Агрегация и поиск логов за период инцидента
Хранение метрикPrometheusСбор и запрос метрик (RPS, ошибки, ресурсы)
Хранение трейсовJaegerПоиск трейсов по сервису и времени
Язык скрипта-агрегатораPython 3.10+Запросы к API, парсинг, генерация отчёта
ШаблонизацияJinja2 / MarkdownПостмортем-шаблон с подстановкой данных
Хранилище отчётовGit-репозиторий / MinIO / веб-серверВерсионирование и доступ к отчётам
CI / АвтоматизацияGitLab CI (GitHub Actions) или cronТриггер генерации после инцидента
Мониторинг процессаPrometheus Blackbox ExporterHealthcheck системы постмортема

4. Этапы выполнения

Этап 1: Развёртывание инфраструктуры сбора данных (2 часа)

Действия

  1. Подготовить docker-compose.yml для стека: elasticsearch, logstash, kibana, filebeat, prometheus, node_exporter, jaeger-all-in-one.
  2. Настроить Filebeat для отправки тестовых логов (например, из /var/log/test-app/*.log) в Logstash/Elasticsearch.
  3. Настроить Prometheus для сбора метрик с node_exporter и добавить несколько scrape_configs для тестовых сервисов (например, service: 'api-gw', service: 'user-svc').
  4. Запустить Jaeger и проверить приём трейсов через Python-скрипт:
from opentelemetry import trace
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

resource = Resource(attributes={SERVICE_NAME: "api-gateway"})
provider = TracerProvider(resource=resource)
jaeger_exporter = JaegerExporter(
    agent_host_name="localhost", agent_port=6831
)
provider.add_span_processor(BatchSpanProcessor(jaeger_exporter))
trace.set_tracer_provider(provider)
tracer = trace.get_tracer(__name__)

with tracer.start_as_current_span("test-request") as span:
    span.set_attribute("http.status_code", 200)
  1. Сгенерировать тестовые данные (набор логов с уровнями ERROR, метрики с повышением CPU, трейсы с задержками).

Ожидаемый результат этапа Все три хранилища (логи, метрики, трейсы) работают, тестовые данные визуализируются в Kibana/Grafana/Jaeger UI.

Этап 2: Написание скрипта-агрегатора (3 часа)

Действия

  1. Создать Python-модуль postmortem_collector.py с функциями:
    import requests
    def get_logs(service, start, end):
        body = {
            "query": {
                "bool": {
                    "must": [{"match": {"service": service}}],
                    "filter": [{"range": {"@timestamp": {"gte": start, "lt": end}}}]
                }
            },
            "size": 1000
        }
        resp = requests.post("http://localhost:9200/logs-*/_search", json=body)
        return resp.json()["hits"]["hits"]
    
    import requests
    def get_metrics(service, metric_names, start, end):
        results = {}
        for metric in metric_names:
            query = f'{metric}{{service="{service}"}}'
            resp = requests.get("http://localhost:9090/api/v1/query_range",
                                params={"query": query,
                                        "start": start.isoformat(),
                                        "end": end.isoformat(),
                                        "step": "15s"})
            results[metric] = resp.json()["data"]["result"]
        return results
    
  2. Реализовать параллельное выполнение запросов через asyncio или ThreadPoolExecutor для ускорения (цель <5 мин).
  3. Обработать ошибки (timeout, недоступность) – логировать и вставлять заглушки.

Ожидаемый результат этапа Скрипт собирает все три типа данных за заданный интервал и возвращает структурированные словари (лог-записи, метрики с временными рядами, трейсы).

Этап 3: Генерация отчёта по шаблону (1 час)

Действия

  1. Подготовить шаблон отчёта postmortem_template.md.j2 в формате Jinja2. Разделы: Сводка, Временная шкала, Затронутые сервисы, Метрики (графики), Ключевые логи (ERROR), Трейсы (slow/error), Причина (root cause), Рекомендации.
  2. Написать функцию render_report(data, template_path):
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader('templates'))
template = env.get_template('postmortem_template.md.j2')
report_md = template.render(
    start_time=data['start'].isoformat(),
    end_time=data['end'].isoformat(),
    services=data['services'],
    log_count=len(data['logs']),
    metrics=data['metrics'],
    traces=data['traces']
)
  1. Сохранить отчёт в папку reports/incident_{id}_{timestamp}.md. Дополнительно конвертировать в HTML (с графиками) с помощью markdown2 + base64 графиков.

Ожидаемый результат этапа При запуске скрипта с тестовыми данными формируется читаемый отчёт.

Этап 4: Автоматизация и триггер (1.5 часа)

Действия

  1. Реализовать HTTP-эндпоинт (Flask / FastAPI) /postmortem/generate, принимающий POST с JSON:
{
  "incident_id": "INC-123",
  "start_ts": "2025-02-01T10:30:00Z",
  "end_ts": "2025-02-01T11:00:00Z",
  "services": ["api-gateway", "user-service"]
}

Эндпоинт запускает сборщик и возвращает 202 Accepted, а после генерации сохраняет отчёт и отправляет ссылку в Slack (опционально). 2. Настроить периодический run через cron (если нет системы инцидентов) – скрипт читает последний закрытый алерт из Alertmanager. 3. Добавить healthcheck /health и метрики (Prometheus client) для наблюдения за временем генерации.

Ожидаемый результат этапа Отчёт генерируется по вызову API; время выполнения <5 мин.

Этап 5: Тестирование и документирование (1.5 часа)

Действия

  1. Написать unit-тесты для каждой функции сбора данных (моки API через responses или requests-mock).
  2. Провести интеграционное тестирование: запустить полный цикл с симулированным инцидентом.
  3. Замерить время генерации (strace, time). Оптимизировать при необходимости (например, агрессивное кэширование метрик).
  4. Документировать README: как запустить стенд, как добавить новый сервис, как вызвать генерацию.

Ожидаемый результат этапа Все тесты проходят, документация готова, время генерации укладывается в 5 минут.

5. Критерии приемки (Definition of Done)

  • Отчёт формируется и доступен не позднее 5 минут после триггера (измеряется Prometheus histogram).
  • Отчёт содержит: уникальный ID инцидента, временные метки, список сервисов, графики метрик (CPU, RPS, ошибки), топ-10 ошибок логов, трейсы с длительностью > p95, вывод о root cause, рекомендации.
  • Система обрабатывает случай, когда часть источников недоступна (без падения, с пометкой).
  • Реализовано не менее одного автоматического триггера (API или webhook).
  • Код размещён в Git-репозитории с CI-проверкой (линтер, тесты).
  • README содержит инструкцию по локальному запуску и добавлению нового сервиса.
  • Тесты покрывают >70% кода агрегатора и шаблонизатора.

6. Ожидаемый результат

  • Основной артефакт Скрипт postmortem_generator.py и FastAPI-приложение app.py, которые генерируют отчёт в формате Markdown/HTML.
  • Содержание отчёта Сводка инцидента, временная шкала, метрики (встроенные ASCII-графики или base64 png), выдержки логов с указанием ошибок, список трейсов-кандидатов, гипотезы причины и рекомендации.
  • Дополнительные результаты Docker Compose-стенд для разработки, CI-пайплайн в .gitlab-ci.yml, Slack-интеграция (опционально).

7. Возможные сложности и их решение

СложностьРешение
Нет реальных инцидентов для тестированияИспользовать скрипт-симулятор инцидента, создающий аномалии в реальном времени
Разные форматы данных от Elasticsearch и LokiАбстрагировать слой доступа через единый интерфейс (ABC), реализовать два класса-провайдера
Высокая загрузка API при сборе данныхИспользовать параллельные запросы (concurrent.futures), ставить таймауты 10 сек на каждый вызов
Графики метрик в Markdown сложно отобразитьГенерировать base64 PNG с помощью matplotlib или вставлять ссылки на Grafana iframe
Задержки трейсов при поиске по большому окнуРазбивать окно на части (2-минутные слоты) и объединять результаты

8. Бюджет времени (оценка)

ЭтапВремя (часы)
Этап 1: Развёртывание инфраструктуры2
Этап 2: Сборщик данных3
Этап 3: Генерация отчёта1
Этап 4: Автоматизация1.5
Этап 5: Тестирование и документация1.5
Итого9 часов
Примечание: для первого раза заложите дополнительно 2 часа на отладку интеграции.

9. Связанные вопросы из базы знаний

ВопросТема
12Как настроить централизованное логирование (ELK/Loki)
34Автоматическое уведомление об инцидентах через Webhook
56Анализ метрик Prometheus с помощью PromQL
78Интеграция Jaeger в микросервисную архитектуру
101Шаблоны postmortem-отчётов и их автоматизация
145Оптимизация времени ответа API при параллельных запросах
203CI/CD пайплайн для Python-приложений
267Мониторинг времени генерации отчётов с помощью Prometheus
312Тестирование интеграций с моками внешних сервисов
401Управление конфигурацией через GitOps

10. Чек-лист самопроверки

  • Я запустил скрипт с тестовыми данными и убедился, что отчёт сформирован за <5 минут.
  • Я проверил, что отчёт содержит логи, метрики и трейсы за правильный интервал.
  • Я протестировал случай, когда один из источников (например, Jaeger) недоступен – отчёт не упал, а указал отсутствие данных.
  • Я добавил в README инструкцию по регистрации нового сервиса (добавление scrape config и индекса).
  • Я запустил pytest и убедился, что покрытие >70%, а интеграционные тесты проходят.