Привет, хабровчане!

Не так давно я публиковал статью про alertmanager-jira — плагин для интеграции алертов Alertmanager (из Prometheus или VictoriaMetrics) с Jira. В конце той статьи анонсировал, что готовлю аналогичное решение для EvaTeam — отечественного трекера задач, на который мы у себя перешли. Обещал — делаю. Встречайте: alertmanager-evateam.

Уже порядка полугода гоняем в продакшене — полёт нормальный.


Зачем это нужно

Если коротко: когда Alertmanager стреляет алерт, хочется автоматически получать задачу в трекере, а не перекладывать эту работу на дежурного инженера вручную. При разрешении проблемы — задача закрывается и комментируется - описывается причина, решение. Без подобной связки проблемы не отслеживаются комплексно, нет возможности оценивать проблемы на горизонте больше недели.

Jira — понятная история, под неё есть несколько готовых решений (в том числе alertmanager-jira). С EvaTeam ситуация до недавнего времени была хуже: интеграций нет, документации немного. Но мы очень хотели, и спешу поделиться с надеждой что это будет интересно кому-то ещё.


Как это работает

Схема предельно простая:

  1. Prometheus (или VictoriaMetrics) собирает метрики

  2. Alertmanager проверяет настроенные пороги и правила, обнаруживает проблему и формирует алерт. Затем отправляет его в настроенные каналы и плагины.

  3. Alertmanager по webhook_config пересылает JSON с данными алерта на HTTP-endpoint alertmanager-evateam.

  4. Он, в свою очередь, создаёт задачу в EvaTeam (или комментирует существующую, если задача уже есть и это настроено).

  5. Когда алерт разрешается (send_resolved: true), плагин это обрабатывает — добавляет комментарий или меняет статус (опять же, если настроено, по умолчанию не закрывается).

Проект реализован на Java + Quarkus с нативной сборкой через GraalVM для скорости выполнения и экономии ресурсов.


Установка и настройка

Docker / Podman

Готовые к использованию образы доступны на Docker Hub: hubbitus/alertmanager-evateam. Собираются автоматически на CI.

Быстрый запуск:

podman run -it --rm --name alertmanager-evateam \
    -p 8080:8080 \
    hubbitus/alertmanager-evateam:latest

В репозитории есть готовый пример полного стека prometheus + alertmanager + grafana для локального запуска через podman-compose (или docker-compose) — смотрите директорию _DEV.scripts/prometheus-alertmanager-grafana.


Конфигурация

Подключение в Alertmanager

receivers:
  - name: 'data-evateam'
    webhook_configs:
      - url: 'https://alertmanager-evateam.your.domain'
        send_resolved: true

Управляющие поля eva__

Основная идея: параметры создаваемой задачи задаются прямо в labels и annotations алерта через префикс eva__. Это позволяет гибко настраивать поведение на уровне каждого правила, не трогая конфигурацию плагина.

Пример правила:

- name: DATA
  rules:
    - alert: DataTest0
      expr: 'promhttp_metric_handler_requests_total > 1'
      labels:
        severity: warning
        eva__field__severity: High
      annotations:
        eva__project: data-alerts
        eva__field__issue_type_name: Task
        eva__field__cf_env: PROD
        summary: DataTest0 summary
        description: |
          Some description VALUE: {{$value}}

Ключевые поля:

  • eva__project — проект в EvaTeam, куда создавать задачи (например, data-alerts).

  • eva__field__issue_type_name — тип задачи (например, Task).

  • eva__field__<имя_поля> — любые поля задачи: eva__field__assignee, eva__field__priority и т.д.

  • eva__identification_field_name — поле для идентификации уже существующей задачи (по умолчанию cf_alert_id). Рекомендуется создать отдельное строковое поле в EvaTeam и использовать его. Не вдаваясь в подробности, через tags (аналог labels в Jira) работает значительно медленнее.

  • eva__identification_field_value — шаблон для вычисления идентификатора (по умолчанию ${context.alert.hashCode()}). По этому идентификатору будет искаться имеется ли уже задача для данного алерта (например для дополнения сообщением что проблема ещё актуальна, или автоматического закрытия)

  • eva__bql_to_find_issue_for_updateBQL-запрос (аналог JQL в Jira) для поиска существующей задачи при повторном срабатывании алерта.

  • eva__comment_in_present_issues — шаблон комментария к уже существующей задаче.

Чтобы не дублировать общие значения в каждом правиле, удобно использовать alert_relabel_configs:

alerting:
  alert_relabel_configs:
    - target_label: eva__project
      replacement: data-alerts
    - target_label: eva__field__issue_type_name
      replacement: Task

Имена полей со спецсимволами

Из-за ограничений схемы Alertmanager и labels, и annotations должны быть валидными идентификаторами. Если нужно передать поле с именем вроде Component/s или Итоговый результат, используется форма с парами:

annotations:
  eva__field__name__1: 'Component/s'
  eva__field__value__1: 'DQ-issues+alerts, DevOps+infrastructure'
  eva__field__name__result: 'Итоговый результат'
  eva__field__value__result: 'Some result description'

Значение <n> в паре eva__field__name__<n> / eva__field__value__<n> может быть произвольной строкой — главное, чтобы она совпадала для имени и значения и была уникальной среди остальных пар.

Шаблонизация значений

Значения полей поддерживают шаблоны через объект context. Например, можно подставить severity из labels прямо в тег:

annotations:
  eva__field__tags: '["severity:${context.field("severity")}"]'

Полный список доступных переменных — в классе AlertContext.


Про API EvaTeam — удобство и подводные камни

Нельзя не сказать пару слов про сам API EvaTeam, потому что здесь есть важная особенность, которая напрямую влияет на то, как нужно подходить к настройке.

Хорошая новость: API весьма гибкий и удобный. Большинство полей принимаются в виде свободных kwargs-параметров (EvaTeam написана на Python) — это позволяет передавать произвольные атрибуты без жёсткой привязки к конкретной схеме. Документации, правда, немного, но с живым API разбираться вполне можно.

Плохая новость: обратная сторона этой гибкости — фактически отсутствует нормальная валидация на этапе биндинга параметров. На стороне клиента нет DTO-объектов, серверная валидация тоже весьма слабая. На практике это означает: если вы допустили опечатку в имени поля или передали несуществующее значение — EvaTeam, как правило, просто проигнорирует это молча, без каких-либо ошибок.

К сожалению, на моей стороне эту проблему надёжно не решить — она на стороне API. Вывод один: всё, что настраивается, необходимо тщательно тестировать перед выводом в продакшен. Убедитесь, что задачи создаются с нужными полями, а не только что плагин «не упал».


Если хотите разрабатывать

Сборка из исходников

./gradlew build
java -jar build/quarkus-app/quarkus-run.jar

Для нативной сборки (для запуска без JVM):

./gradlew build -Dquarkus.native.enabled=true \
    -Dquarkus.package.jar.enabled=false \
    -Dquarkus.native.container-build=true

Запуск тестов

Стоит отметить что писать ПО без тестов уже давно не просто мода, а необходимость. В проекте есть юнит тесты и интеграционные.

Внимание! Мок-объектов для EvaTeam нет. Для запуска интеграционных тестов нужен живой экземпляр EvaTeam с соответствующей конфигурацией окружения. Тут опять же, замечу, это не то чтобы здорво, но система проприетарная, и я пока не вижу хорошего способа поднять на момент тестов реальный инстанс, скажем в качестве testcontainers. Просто учитывайте это, если решите дорабатывать и предложить пулл-реквесты.

./gradlew test --info

Для нативного режима:

./gradlew testNative --info

Итого

Если вы используете EvaTeam в качестве трекера и хотите замкнуть цикл мониторинга от Prometheus (или VictoriaMetrics) до задачи в трекере — alertmanager-evateam решает эту задачу. Проект в активной эксплуатации, работает стабильно. И если для реализации Alertmanager-jira я сравнивал его с существующим решением jiralert, то полагаю для EvaTeam моё решение пока уникально.

Буду очень рад, если хоть кому-то поможет!

Любой фидбек горячо приветствуется — пишите в комментариях или открывайте issue в репозитории. Если есть пожелания по новым фичам или обнаружили баг — тем более.