/ru/blog · материалы
Алерт из VictoriaLogs становится issue в репозитории продукта, его разбирает roboomp на omp по подписке через auth-broker. Пять граблей по дороге, от алерта без имени проекта до 403 Request not allowed, и проба: меньше минуты от issue до ответа бота.
Утром мы с idevops настроили доставку алертов в сессию проекта. Через час задумались: а почему в Claude, а не в нашу систему omp?
Внятного ответа не нашлось. Задачу «слать ошибки из почты в сессию проекта» я прочитал как «в Claude-сессию с именем проекта» и так и написал в задании для idevops. Вариант с omp даже не рассматривали.
Claude-сессия проекта живёт в tmux на сервере разработки. Её перезапускают, иногда она просто не запущена, и алерт, пришедший в выключенную сессию, никто не прочитает.
И разбор остаётся в переписке. Через неделю не найти, кто смотрел инцидент и что решил.
Нужен был адресат, который работает всегда и оставляет след. Такой у нас уже был, просто стоял в стороне от алертов.
В репозитории oh-my-pi лежит python/robomp: GitHub-бот, которым авторы omp разбирают свои issue. В их CHANGELOG полно PR от @roboomp. На новое issue он ставит метки и действует по классу: баг воспроизводит, чинит в отдельном worktree и открывает PR с разделами Repro, Cause, Fix, Verification; на вопрос отвечает комментарием. На каждое issue заводится своя сессия omp --mode rpc, и ответы в issue её продолжают.
PAT бота живёт в отдельном контейнере gh-proxy, оркестратор ходит к нему с HMAC-подписью. Агент, который исполняет команды модели, токена не видит.
У нас roboomp поднят в чарте iomp на стенде: под из двух контейнеров, наружу через шлюз открыт один путь, POST /webhook/github. Схема целиком:
алерт VictoriaLogs → alert-relay → issue в iconicompany/<project> → вебхук → roboomp → omp → комментарий или PR
По дороге было пять граблей.
Правило HighErrorRate считало ошибки так:
| stats by (namespace, container) count() as error_count
В пространстве iconicompany-production живут все модули кабинета, и контейнер у каждого называется app. По письму namespace=iconicompany-production container=app не понять, чей это всплеск и куда его нести.
Зато говорит имя пода. Vector и так пишет поле pod в каждую запись лога: iomp-58dcd966d5-8c6tr. Отрезаем хеш ReplicaSet и пятисимвольный суффикс, остаётся iomp. Идея выглядит так:
| extract_regexp "^(?P<project>.+?)(-[0-9a-f]{6,10})?-[0-9a-z]{5}quot; from pod
| stats by (namespace, project, container) count() as error_count
idevops добавил ветку для StatefulSet (<имя>-<N>) и прогнал выражение по шести часам логов. iconicompany-production/italent/app, 16 ошибок, имя проекта верное.
roboomp из коробки ходит в модели через LLM-шлюз с API-ключами. Разбор каждого алерта на Opus по API стоит денег, а подписки у нас уже оплачены.
В omp для этого есть omp auth-broker. Это хранилище учётных данных: другим omp оно раздаёт токены доступа, а refresh-токены держит у себя и никому не отдаёт. На хосте запускаем:
omp auth-broker serve --bind=<адрес docker0>:9000
В под передаются две переменные, OMP_AUTH_BROKER_URL и OMP_AUTH_BROKER_TOKEN. Проверка: контейнер без единой учётной записи спрашивает, сколько будет 6×7, и отвечает «42». Значит, подписка доехала через брокер.
Из docker на хосте брокер работал. Из пода в кластере Anthropic ответил:
{"type": "forbidden", "message": "Request not allowed"}
Через прокси платформы с выходом в США ответ был 403, напрямую с выходом из РФ тоже 403. Без токена тот же прокси возвращал 401: сеть доходит, отказывают именно подписке по адресу выхода. Docker на хосте ходил через sing-box, и этот выход Anthropic принимает.
Первая попытка idevops пустила через sing-box весь трафик подов. Сломались входящие соединения, упал прод iagent, правку откатили. Вторая пошла через connmark: в sing-box уходят только соединения, которые открыл сам под. После неё тот же запрос из пода вернул «56».
roboomp пропускает события от собственного логина, иначе он отвечал бы на свои же комментарии. Если alert-relay откроет issue токеном бота, алерт так и останется неразобранным.
Поэтому issue заводит отдельный GitHub App iconicompany-issues, установленный на все репозитории организации. На его комментарии roboomp тоже не реагирует. Это правильно: «resolved» от релея разбирать не нужно.
HighErrorRate срабатывает каждую минуту, пока ошибок больше порога. Если заводить issue на каждое срабатывание, через час репозиторий превратится в свалку.
alert-relay кладёт в тело маркер <!-- alert-fp: … --> с fingerprint алерта. Если алерт сработал снова, пока issue открыто, релей пишет комментарий, а когда алерт прошёл, добавляет «resolved». Сам релей issue не закрывает: это делает PR с исправлением или человек.
Сырых строк лога в issue нет: только тип ошибки, счёт, образцы request_id и команда logs.sh --vl 'request_id:"…"'. Репозитории приватные, но персональным данным из логов в issue всё равно делать нечего.
idevops завёл пробный алерт, iomp#2. Хронология по логам roboomp:
05:21:19 issues.opened → вебхук 202 → dispatch
05:21:34 rpc_model_pick model=anthropic/claude-opus-5-5
05:22:12 rpc_done task=triage_issue
Разбор занял 38 секунд. Ответ бота: «Проба доставки получена… Алерт тестовый (severity: info), в коде iomp чинить нечего — помечено invalid». Всё верно, пробу закрыли.
Следующий настоящий всплеск в подах iomp-* придёт issue в репозиторий, и первым его прочитает бот.
Подписки кончаются. За неделю Codex ответил usage_limit_reached, а Gemini исчерпал квоту со сбросом через 94 часа. Когда упрётся и Claude, бот останется без модели: roboomp повторит попытку трижды и сдастся, а алерт пролежит открытым issue до человека.
Бот не видит логов. У пода roboomp нет доступа к VictoriaLogs, он разбирает текст issue и код. Для всплеска ошибок в своём модуле этого часто хватает, а причину падения Postgres так не найти.
Брокер подписок слушает на docker0, и его видит любой контейнер и под на хосте. Защищает его только bearer-токен.
Автоматизация на потребительских подписках находится в серой зоне условий провайдеров. Если аккаунт заблокируют, встанет и бот, и ручная работа на той же подписке.
Наконец, roboomp отвечает на каждое новое issue в разрешённом репозитории, а не только на алерты. В iomp это нормально. В репозитории с живой очередью задач он будет мешать.
Алерт в выключенной сессии ничем не отличается от алерта, которого не было. Issue хотя бы висит открытым, пока его кто-нибудь не закроет.