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

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

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

Разработать скрипт (или микросервис), который по временному окну инцидента автоматически собирает логи, метрики и traces из production-инфраструктуры и генерирует черновик postmortem-отчёта в заданном формате. Автоматизация должна сократить время сбора данных для анализа с часов до ~5 минут.

Ключевой результат Скрипт, который за 5 минут формирует markdown-файл postmortem с заполненными полями: таймлайн, ключевые метрики «до/во время/после», фрагменты логов, аномалии в traces и гипотезу о root cause.


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

Что нужноОткуда взять
Время начала и конца инцидента (ISO 8601)Из системы оповещений (PagerDuty, Opsgenie) — симулировать
Логи (сервиса RAG / API)Loki / Elasticsearch — симулировать файлами
Метрики (latency, error rate, request count)Prometheus / VictoriaMetrics — симулировать CSV
Traces (span-id, duration, status)Jaeger / Tempo — симулировать JSON
Описание сервисов и их зависимостейREADME проекта — можно выдумать
Baseline метрик за 24 часа до инцидентаТе же источники, сдвиг окна

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

  1. Создай Python-скрипт simulate_incident_data.py, который генерирует каталог incident_data/ с файлами:
    • logs_sample.log — 200–500 строк формата [Вики/Recency|timestamp [level] [Вики/Service|service [Вики/Request ID|trace_id message
    • metrics_sample.csv — 4 столбца: timestamp, service, metric_name, value (например, request_duration_seconds, error_rate)
    • traces_sample.json — список span-объектов с полями trace_id, span_id, parent_span_id, start_time, duration, status_code, service_name
  2. Задай в коде INCIDENT_START = "2025-03-15T10:30:00Z", INCIDENT_END = "2025-03-15T10:45:00Z" — окно инцидента 15 минут.
  3. В logs_sample.log выдели 10–15% записей с level=ERROR внутри окна, сделай всплеск метрики error_rate с 1% до 15%.

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

КомпонентИнструментыНазначение
ЯзыкPython 3.10+Логика агрегации и генерации отчёта
Парсинг логовre, pandasЧтение симулированных логов
Получение метрикrequests к Prometheus API (или чтение CSV)Query range
TracesJSON / PandasФильтрация по времени, аномалии
ШаблонизаторJinja2Рендеринг markdown-отчёта
Файловая системаpathlibСохранение результатов
ТестированиеpytestПроверка корректности скрипта

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

Этап 1: Подготовка окружения и данных (30 минут)

Действия

  1. Установи Python-зависимости: pip install pandas jinja2 requests pytest
  2. Скачай или создай шаблон postmortem (postmortem_template.j2):
    # POSTMORTEM: {{ incident_id }}
    - Дата инцидента {{ start_time }} — {{ end_time }}
    - Длительность {{ duration_min }} мин
    - [[Вики/severity|Severity]] {{ severity }}
    ## Таймлайн
    {% for event in timeline %}
    - {{ event.time }}: {{ event.description }}
    {% endfor %}
    ## Метрики (до/во время/после)
    {% for metric in metrics %}
    - {{ metric.name }}: {{ metric.before }} → {{ metric.during }} → {{ metric.after }}
    {% endfor %}
    ## Логи (топ ERROR за окно)
    {% for log in top_logs %}
    - `{{ log }}`
    {% endfor %}
    ## Traces: аномально медленные
    {% for trace in slow_traces %}
    - trace_id={{ trace.trace_id }}, duration={{ trace.duration }}ms, service={{ trace.service }}
    {% endfor %}
    ## Гипотеза root cause
    {{ hypothesis }}
    
  3. Создай файл simulate_incident_data.py (описан в разделе 2) и запусти его, чтобы получить набор тестовых данных.

Ожидаемый результат этапа Каталог incident_data/ с тремя файлами, готовый шаблон postmortem_template.j2.


Этап 2: Разработка модуля сбора логов (1 час)

Действия

  1. Напиши функцию load_incident_params(incident_id, start_time, end_time, data_dir), которая читает из командной строки или файла параметры окна.
  2. Реализуй parse_logs(filepath, start, end):
    • Используй pandas.read_csv с разделителем | (симулируем структурированные логи) или re для парсинга.
    • Фильтруй строки по времени.
    • Подсчитай количество ERROR-сообщений, сгруппируй по сервису.
    • Извлеки топ-10 ERROR-строк с наибольшей частотой.
  3. Реализуй get_metrics(filepath, metric_name, start, end, baseline_hours=24):
    • Прочитай CSV метрик.
    • Вычисли среднее значение метрики за три окна: [start - 1h, start), [start, end], [end, end + 1h).
    • Верни словарь {metric_name: {before: val, during: val, after: val}}.
  4. Реализуй parse_traces(filepath, start, end):
    • Загрузи JSON traces.
    • Отфильтруй spans, начавшиеся внутри окна.
    • Найди spans с duration > 3 * median(duration) (аномальные).
    • Верни список словарей аномальных trace-групп.

Ожидаемый результат этапа Три функции в collectors.py, которые возвращают структуры данных.


Этап 3: Разработка модуля генерации гипотезы (45 минут)

Действия

  1. Создай hypothesis_engine.py:
    • Простая эвристика: если error_rate выросла одновременно с ростом request_latency и есть ERROR-логи в том же сервисе — гипотеза: «Проблема в сервисе {service}, возможно, из-за увеличения времени ответа downstream».
    • Если latency выросла, но error_rate не изменилась — «Возможно, медленный upstream-запрос или изменение конфигурации».
    • Если error_rate выросла, а latency нет — «Ошибки не связаны с производительностью, возможно, логическая ошибка или некорректные данные».
  2. Функция generate_hypothesis(metrics, logs, traces) возвращает строку с гипотезой.

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


Этап 4: Интеграция и генерация отчёта (1 час)

Действия

  1. Создай главный скрипт auto_postmortem.py:
    import sys, json
    from collectors import parse_logs, get_metrics, parse_traces
    from hypothesis_engine import generate_hypothesis
    from jinja2 import Environment, FileSystemLoader
    
    def main(incident_id, start, end, data_dir):
        # 1. Загрузить данные
        logs = parse_logs(f"{data_dir}/logs_sample.log", start, end)
        metrics = {}
        for m in ["error_rate", "request_duration_seconds"]:
            metrics[m] = get_metrics(f"{data_dir}/metrics_sample.csv", m, start, end)
        traces = parse_traces(f"{data_dir}/traces_sample.json", start, end)
        hypothesis = generate_hypothesis(metrics, logs, traces)
    
        # 2. Подготовить контекст для шаблона
        duration_min = (end - start).seconds // 60
        context = {
            "incident_id": incident_id,
            "start_time": start.isoformat(),
            "end_time": end.isoformat(),
            "duration_min": duration_min,
            "severity": "P1" if metrics["error_rate"]["during"] > 10 else "P2",
            "timeline": [
                {"time": start.isoformat(), "description": "Начало роста error_rate"},
                {"time": end.isoformat(), "description": "Интервал инцидента завершён"}
            ],
            "metrics": [{"name": k, **v} for k, v in metrics.items()],
            "top_logs": logs["top_errors"][:5],
            "slow_traces": traces[:5],
            "hypothesis": hypothesis
        }
        # 3. Рендеринг
        env = Environment(loader=FileSystemLoader("."))
        template = env.get_template("postmortem_template.j2")
        report = template.render(**context)
        # 4. Сохранить
        with open(f"output/pm_{incident_id}.md", "w") as f:
            f.write(report)
        print("Report saved.")
    
  2. Добавь парсинг аргументов командной строки: --id, --start, --end, --data-dir.
  3. Протестируй на симулированных данных:
    python auto_postmortem.py --id INC-20250315 \
         --start 2025-03-15T10:30:00Z \
         --end 2025-03-15T10:45:00Z \
         --data-dir incident_data
    
  4. Убедись, что output/pm_INC-20250315.md создан и содержит осмысленные данные.

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


Этап 5: Оценка качества и доработка (30 минут)

Действия

  1. Напиши test_collectors.py с pytest:
    • Тест на корректность фильтрации логов по времени.
    • Тест на расчёт средних метрик.
    • Тест на обнаружение аномальных trace.
  2. Замерь время выполнения всего скрипта на симулированных данных (должно быть <5 мин, обычно <1 с).
  3. Добавь в отчёт секцию «Аномалии в коде» (если есть доступ к git log) — опционально, симулируй коммит перед инцидентом.

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


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

  • Скрипт auto_postmortem.py принимает параметры через CLI (incident_id, start, end, data_dir).
  • Генерируется markdown-файл с именем pm_{incident_id}.md в папке output/.
  • Отчёт содержит все обязательные разделы: таймлайн, метрики (до/во время/после), топ-5 ERROR-логов, аномальные traces, гипотеза.
  • Для метрик error_rate и request_duration_seconds корректно вычислены средние за три окна.
  • Гипотеза осмысленно связана с входными данными (не пустая строка и не случайный текст).
  • Все симулированные данные создаются однократным запуском simulate_incident_data.py.
  • Время выполнения на симулированных данных не превышает 5 минут (обычно <1 сек).
  • pytest проходит минимум 3 теста.

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

  • Основной артефакт каталог auto_postmortem/ с файлами:

    • auto_postmortem.py — главный скрипт
    • collectors.py — функции сбора
    • hypothesis_engine.py — генерация гипотезы
    • postmortem_template.j2 — шаблон Jinja2
    • simulate_incident_data.py — генератор тестовых данных
    • incident_data/ — сгенерированные данные
    • output/pm_*.md — итоговый отчёт
    • test_collectors.py — тесты
  • Содержание отчёта автоматически заполненный postmortem, готовый к редактированию человеком.

  • Опционально интеграция с реальными источниками (Prometheus API, Loki API) — можно расширить.


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

СложностьРешение
Форматы логов отличаются от симулированныхИспользовать json.loads для парсинга JSON-логов; добавить конфигурацию с регулярными выражениями
Временные метки в разных часовых поясахКонвертировать все в UTC с помощью pytz или zoneinfo
Много аномальных traces (шум)Использовать процентиль 99.9 вместо порога ×3
Не хватает данных для гипотезыВернуть шаблонную строку «Требуется ручной анализ — данных недостаточно»
Скрипт работает дольше 5 минут на реальных данныхОптимизировать запросы (batch-чтение, параллелизм) или ограничить объём пре-агрегированными данными

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

ЭтапВремя
Этап 1: Подготовка окружения и данных30 мин
Этап 2: Модуль сбора логов1 ч
Этап 3: Модуль генерации гипотезы45 мин
Этап 4: Интеграция и генерация отчёта1 ч
Этап 5: Оценка качества и доработка30 мин
Итого~3 ч 45 мин

Примечание: Если инженер впервые работает с Jinja2 или Prometheus-запросами, добавьте 1 час на ознакомление.


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

ВопросТема
102Метрики SLA/SLO/SLI
115Управление инцидентами
137Архитектура observability (logs, metrics, traces)
189Анализ root cause (RCA) в распределённых системах
210Jinja2 шаблоны для отчётов
244Prometheus query language (PromQL) basics
267OpenTelemetry и сбор traces
315Git blame и интеграция с инцидентами
418Автоматическая классификация severity
501Тестирование скриптов на симулированных данных

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

  • Я проверил, что скрипт работает на симулированных данных без ошибок.
  • Я убедился, что сгенерированный отчёт имеет все требуемые разделы.
  • Я написал минимум 3 теста (pytest) для ключевых функций.
  • Я замерил время выполнения и оно <5 минут.
  • Я проверил, что гипотеза не пустая и логична (например, «Проблема в сервисе auth, так как error_rate выросла с 1% до 15%»).