Как я Zabbix с LLM дружил в свободное время. Архитектурный обзор взаимодействия с нейросетью. Часть 3 HLD и немного LLD. aiops.. aiops. c4.. aiops. c4. DevOps.. aiops. c4. DevOps. hld.. aiops. c4. DevOps. hld. IT-инфраструктура.. aiops. c4. DevOps. hld. IT-инфраструктура. lld.. aiops. c4. DevOps. hld. IT-инфраструктура. lld. llm.. aiops. c4. DevOps. hld. IT-инфраструктура. lld. llm. zabbix.. aiops. c4. DevOps. hld. IT-инфраструктура. lld. llm. zabbix. автоматизация.. aiops. c4. DevOps. hld. IT-инфраструктура. lld. llm. zabbix. автоматизация. алерты.. aiops. c4. DevOps. hld. IT-инфраструктура. lld. llm. zabbix. автоматизация. алерты. Анализ и проектирование систем.. aiops. c4. DevOps. hld. IT-инфраструктура. lld. llm. zabbix. автоматизация. алерты. Анализ и проектирование систем. искусственный интеллект.. aiops. c4. DevOps. hld. IT-инфраструктура. lld. llm. zabbix. автоматизация. алерты. Анализ и проектирование систем. искусственный интеллект. мониторинг.. aiops. c4. DevOps. hld. IT-инфраструктура. lld. llm. zabbix. автоматизация. алерты. Анализ и проектирование систем. искусственный интеллект. мониторинг. Системное администрирование.
Лапки котику помогли!

Лапки котику помогли!

Это третья статья из цикла о том, как при правильно поставленной задаче и грамотном подходе к архитектуре можно собрать реализацию self-hosted системы по анализу алертов при помощи локально развернутой LLM с участием нейросети, как исполнителя.

В первой части рассматривалась постановка задачи, формирование ТЗ и пояснения, пояснение, почему даже для коммерческой LLM лучше ставить правильные, четкие задачи.

Во второй части рассмотрели требования к локальной LLM, муки выбора и сравнение небольших моделей между собой.

В этой же части погружаемся в архитектурное проектирование будущего сервиса и определим, в какой момент можно подключить нейросеть к участию в проекте.

Часть 1: Вводная и формирование ТЗ

Часть 2: Выбор локальной LLM

Часть 3: Формирование HLD и немного LLD (Вы здесь)

Часть 4: Что из этого вышло

Для формирования понятного нам стека технологий и дальнейшей постановки задачи не обойтись без таких понятий, как HLD и LLD. В процессе подготовки так же определим, когда имеет смысл подключать нейросеть к архитектурному планированию для ускорения работы и получения наилучшего результата.

В своей первой статье на Хабре я описывал применимость нотации C4 для составления архитектуры инфраструктурного ИТ-проекта, теперь также постараюсь применить к архитектуре программного решения.

Мне очень понравилась эта методология за ее простоту и логическое следование от общего к частному, благодаря чему (при должном оформлении документации) все участники процесса разработки смогут получить необходимую информацию об архитектуре и исполнении того или иного проекта.

Но, я отвлекся…

HLD

Для ИТ тоже подходит

Для ИТ тоже подходит

На данном этапе подключать нейросеть, еще рано, т.к. для описания верхнеуровневой архитектуры сервиса или инфраструктуры не до конца очерчены границы, в которые необходимо уложиться, несмотря на сформированное ранее ТЗ.

Итак, разберем проект по нотации C4 и приступим к первому же уровню

C4. L1 – Context

В ТЗ мы определили основные требования, здесь же необходимо детализировать применимость к той инфраструктуре, которую это решение будет обслуживать.

С чего начинается проект

С чего начинается проект

Итак, у нас есть несколько источников данных, метрики с которых забираются при помощи Zabbix.

При возникновении алерта Zabbix передает его в систему обработки запросов (алертов), в которой происходит обработка в соответствии с ТЗ и доставляется уведомление пользователю по одному из каналов.

Таким образом верхнеуровнево определен способ взаимодействия элементов системы и человека.

C4. L2 – Containers

Добавляем конкретики

Добавляем конкретики

Вторая часть HLD – определение контейнеров, которые позволят нашему решению взаимодействовать в соответствии с определенными нами параметрами и верхнеуровнево определяем каналы взаимодействия между контейнерами.

Первой идеей было собрать все в один сервис, который способен выполнить все операции в монолите (принял webhook, запросил Zabbix, отправил в LLM, построил график, отправил в Matrix и почту, сохранил состояние и вернул ответ). Однако также, как и в инфраструктурных проектах, смешение быстрых и медленных операций (передача состояния и непредсказуемая по времени обработка LLM) сделает систему менее стабильной и работоспособной.

Итого мы имеем:

1. Zabbix, как внешний элемент. Источник событий, который отправляет webhook по факту срабатывания триггера. Так же к нему по API происходит обращение alert-processor worker для запроса дополнительной информации

2. Сервиса приема сообщений (alert-receiver). Его задача – принять входящее сообщение от Zabbix и проверить необходимый минимум:

  • Проверить токен;

  • Провести валидацию payload;

  • Нормализовать структуру;

  • Присвоить id для последующей корреляции;

  • Переслать событие во внутренний контур

Если сравнивать с живым организмом, то это глаза и уши системы с первичной обработкой поступающей информации. Как и у органов восприятия – необходимо провести первичный анализ предмета и сформировать сигнал для последующей обработки мозгом.

3. Сервис alert-processor ingest, который является точкой входа во внутренний пайплайн обработки. Его задача – получить нормализованное внутреннее событий и поставить в очередь, после чего вернуть ответ приемному контуру об успешности. Фактически этот компонент – архитектурное разделение приема и последующей обработки события для увеличения скорости приема и повышения стабильности.

Это уже нервная система, по которой передается сигнал в мозг.

4. Сервис alert-processor worker. Для системы это сердце и мозг, потому что на этом уровне уже:

  • Осуществляется работа с состоянием;

  • Осуществляется логика о подавлении событий;

  • Происходит обнаружение флапа;

  • Насыщение события информацией из Zabbix по API;

  • Происходит триаж через LLM;

  • Осуществляется детерминированная и LLM корреляция;

  • Предлагаются варианты диагностики и предположения через LLM;

  • Доставка уведомлений;

  • Формирование и запись audit log.

Именно этот сервис является той частью мозга, что отвечает за мышление, оценку того, что увидели глаза и формирования необходимости о дополнении информацией (может быть пристальнее рассмотреть предмет?).

5. Локальная LLM вынесена как внешний сервис, чтобы при возможных сбоях, галлюцинаций и прочих затруднениях, она не влияла на пайплайн оповещения напрямую, а терялась всего лишь часть функционала. Также это развязывает руки по обновлению модели, замене модели без серьезного влияния на основной контур.

Если так же сравнивать с организмом – это часть мозга, которая отвечает за высокоуровневое мышление и аналитические способности. Если мы на предыдущем уровне не смогли шаблонно идентифицировать объект, который увидели, начинаем предполагать, что это.

6. Redis также вынесен в отдельный контейнер для упрощения взаимодействия и обновления. Для нас это будет память, которая заданное количество времени хранит полученные события, информацию о них и принятые решения.

Его задачи:

  • Обеспечения работы очереди для событий;

  • Список что обработано/обрабатывается;

  • Deadletter;

  • Временные окна, для которых возможно подавление событий;

  • Дедупликация событий;

  • Учет восстановления событий (recovery из Zabbix);

  • Статусы флапа событий;

  • Кэш для триажа;

  • Временные окна, для которых возможна корреляция событий;

  • Хранение audit log;

  • Хранение инфо о хартбите worker.

Спойлер

Необходимо добавить оговорку – для домашнего использования и MVP это еще нормальная архитектура, но для мало-мальски прода естественно необходимо предусмотреть еще внешнюю СУБД, в которой необходимо перенести долгосрочное хранение audit log (к примеру).

7. И тут же 8 – каналы доставки. Это полностью внешние сервисы. Как мне кажется, необходимости описания, почему они внешние, совсем нет.

Итак, чтобы расставить все галочки над Й, есть смысл ответить на несколько “почему”.

1. Почему прием и обработка разделены?

Потому что в одной цепочке уже есть куча связей и задач (API, очереди, графики, вызовы LLM, корреляция, уведомления и т.д.). В этом случае синхронная схема становится уязвимой к ошибкам и задержкам (пока LLM думает, сыпятся другие события, которые могут потеряться из-за задержки перед обработкой или стать не актуальными), а значит менее предсказуемой.

2. Почему нельзя отдавать критичные ошибки LLM?

Ответ проще, чем кажется. Так как модель для нас – внешняя зависимость, она может быть недоступна по разным причинам, слишком сильно задуматься или попросту фатально ошибиться. Т.о., как мы формировали в ТЗ, события High+ должны идти отдельным треком, чтобы не строить через недоверенный узел источник истины.

C4. L3 – Components

На этом уровне мы уже заглядываем внутрь каждого основного контейнера решения для составления карты взаимосвязи их логических компонентов.

C4. L3 alert-receiver

Привратник

Привратник

Самый маленький контейнер. Состоит из 4-х основных блоков:

1. Webhook API, что является точкой входа для событий из Zabbix. Принимает событие и инициализирует дальнейшую обработку.

2. Token Validator проверяется корректность передаваемого токена. Минимальная защита контура.

3. Payload Validator / Normalizer проверяет структуру входящего payload, обязательные поля и типы значений, а затем нормализует событие к внутреннему формату, понятному остальной системе.

4. Forwarder просто передает событие далее во внутренний контур alert-processor ingest.

Главный смысл этого контейнера – входной шлюз.

C4. L3 alert-processor ingest

Врата

Врата

Так же не самый большой контейнер, чья задача ввести события во внутренний асинхронный пайплайн.

На компонентном уровне выделены 3 блока:

1. Event API принимает внутреннее событие от alert-receiver. Вход во внутреннюю шину обработки

2. Queue Repository отвечает за постановку события в очередь Redis

3. Audit reg фиксирует факт принятия и постановки события в очередь во внутреннем контуре

Отдельно выделены Redis, как элемент внешнего контура и следующий контейнер.

C4. L3 alert-processor worker

Основная рабочая лошадка

Основная рабочая лошадка

Самый тяжелый контейнер всей системы, отвечающий за всю логику работы.

Учитывая это внутри контейнера пришлось четко разделить зоны ответственности.

1. Worker Runtime: забирает сообщения из очереди, инициирует обработку, выносит вердикт о подтверждении обработки (или повторной постановке в очередь) и отправку в deadletter.

2. Queue Repository. С его помощью Worker Runtime получает событие из Redis, подтверждает успешную обработку (возвращает в очередь при временном сбое, отправляет в deadletter, если число попыток исчерпано).

3. Processor Service центральный компонент оркестратор обработки одного события, который вызывает все остальные блоки и собирает итоговую логику обработки.

4. Redis State Repository работает с кратковременным состоянием обработки. То есть система через него система получает окна подавления событий, флапы, открытые инциденты и т.д.

5. Policy Engine реализует детерминированную логику событий. В нем находятся правила по маршруту критических событий, когда применять подавление, что считается флапом.

Обязательно нужно отметить – этот элемент необходим в архитектуре, чтобы не зависеть полностью от решения локальной LLM.

6. Zabbix Enricher, который обращается к Zabbix API за дополнительным контекстом по событию (подробности, метаданные, ссылки на событие и графики и т.д.).

7. LLM Triage Adapter подключается для событий низкой критичности и помогает вынести вердикт о необходимости уведомления оператора.

Снова необходимо подчеркнуть, что LLM – это просто инструмент для решения конкретной задачи в четко ограниченном сценарии!

8. Correlation Engine основанный на правилах компонент детерминированной корреляции, который помогает определить по определенным шаблоном роль события (причина или следствие). В процессе реализации было решено использовать внешние YAML-правила с регулярными выражениями по тексту.

9. Correlation Fallback подключается в случае, если детерминированная корреляция не дала результата. Предоставляет предположение LLM о роли события, как в предыдущем шаге.

10. LLM Remediation Adapter отвечает за формирование рекомендаций по диагностике. Все рекомендации обязательно должны ограничиваться ТОЛЬКО диагностическим контуром, т.е. предложить посмотреть логи, статистику, состояние демона/процесса БЕЗ изменения состояния системы.

11. Notification Dispatcher, как понятно из названия – диспетчер маршрутизации уведомлений, который получает итоговое решение обработки и определяет, в какие каналы его отправить.

12. Matrix Notifier – интеграция с Matrix. Именно с ним было больше всего проблем, о которых кратко расскажу в следующей статье.

13. Mail Notifier – компонент SMTP-доставки.

Примечание: держать каналы лучше отдельно, чтобы не было зависимости элементов друг от друга, была проще масштабируемость.

14. Audit Logger фиксирует весь жизненный цикл обработки события от входных данных до отправки в один из каналов оповещений.

15. Worker Status Publisher публикует статус самого компонента worker.

LLD

Так как статья получилась очень большой, представлю LLD просто описание о том, что это такое и почему я его отдал полностью нейросети.

Что такое LLD?

LLD (или Low-Level Design) – это уровень детализации, на котором верхнеуровневая архитектура перестает быть просто набором контейнеров и связей между ними и превращается в понятную внутреннюю структуру будущего решения. По сути, это мост между архитектурной схемой и реализацией: именно здесь фиксируются роли внутренних модулей, последовательности обработки событий, зоны ответственности, а также то, какие технические блоки должны появиться в проекте.

LLD в рамках моего проекта

Как выглядит лень

Как выглядит лень

Именно этот уровень уже можно отдавать нейросети на проработку, но только под обязательным контролем человека. Причина проста: к моменту LLD уже зафиксированы ТЗ, границы системы и HLD, а значит, модели не нужно придумывать архитектуру заново, она поможет разложить ее на более мелкие строительные блоки.

В текущем проекте это выразилось вполне предметно: LLD зафиксировал отдельные сервисы alert-receiver, alert-processor-ingest, alert-processor-worker, audit-api и redis, использование Redis Streams для очереди, детерминированную доставку для High и Disaster, конфигурируемый режим для Average, строгий JSON-контракт для ответов LLM, guardrails для remediation и поэтапный audit log.

Именно в этом и заключается практическая ценность LLD: после него уже можно последовательно собирать прототип по заранее определенным модулям и сценариям, а не строить предположения на тему, как оно должно работать.

Спойлер

При желании – можно ознакомиться с полным вариантом LLD тут. Однако я не могу гарантировать доступность 99,9% 24/7, т.к. это моя домашняя коробочка, на которой крутятся все мои self-hosted сервисы, да еще и в поселке Владимирской области :-)

Итог третьего этапа

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

Что будет дальше

Заключительная статья будет посвящена уже тому, какие были сложности в процессе реализации (а они, конечно же, были), что получилось (со скриншотами) и ссылка на мою gitea (если вдруг взлетит, выложусь в нормальном человеческом git и исправлю ссылку), где можно будет посмотреть элементы решения, поругать криворукий вайбкодинг и даже дать несколько советов!

Автор: it_zoobik

Источник