ESPN DeportesCómo las Valkyries fueron de la expansión hasta las Finales WNBAESPNFIFA president Infantino received undisclosed $1.7M World Cup paymentThe Jerusalem PostHerzog honors Israel Bonds for raising billions post-Oct 7 while mourning northern school tragedyInquirer300 kilos of trash collected in Sarangani Bay mangrove cleanupBollywood HungamaSadhu Tiwari to perform Nayyi Navelli song ‘Awadhiya Ke Raja’ at Ram Ki Paidi in AyodhyaRTP DesportoLens - Sporting CPDaily MaverickHow the Middle East conflict is reshaping the multipolar global orderRadio Times'It was jaw dropping!'The Hollywood Reporter‘Queen Silvia’ Ditches the Glass Slipper for Un-Cinderella Royal Romance3DNewsМарсоход NASA Perseverance побил рекорд пробега на другой планете — и останавливаться не собираетсяZDF heuteEntdecken Sie das ZDF-NachrichtenstudioBBC NewsMurder investigation after four people die in arson attack
The Daily Newsstand · Free, Always
Tuesday, October 13, 2026

AIOps: наш опыт внедрения HolmesGPT на трёх проектах

Translate
Окно Slack: алерт KubernetesJobFailed от Alertmanager и под ним разбор Holmes с вердиктом и шагами проверки

Это пример алерта и его разбора Holmes в Slack.

Алерт приходит в чат без контекста. «Pod перезапускается», «очередь переполнена», «Job упал» — и дежурный начинает с одного и того же: открыть дашборд, найти Pod, полистать логи, вспомнить, что выкатывали накануне. Это первичное расследование почти всегда одинаково по форме, и именно его мы отдали агенту.

Мы внедрили HolmesGPT — open-source SRE-агента — в шести Kubernetes-кластерах: в собственном кластере Tuna и в пяти кластерах наших клиентов, которые у нас на DevOps-сопровождении. Клиентских проектов два: один живёт в российском облаке, другой — зарубежный, на двух облаках.

По ходу внедрения нам пришлось написать собственный инструмент — holmes-alert-bridge: готовой связки с Alertmanager в открытой версии HolmesGPT нет. Он принимает алерт, отсеивает лишнее, передаёт агенту вместе с контекстом и приносит разбор в чат; код мы открыли. Теперь через несколько минут после алерта рядом с ним лежит гипотеза: что происходит, почему и что проверить. Ниже — как это устроено, сколько стоит и чему мы научились за первый месяц.

Коротко

  • HolmesGPT — open-source SRE-агент из CNCF Sandbox: сам ходит в Kubernetes, метрики и логи и формулирует гипотезу о причине алерта.

  • Штатной push-интеграции с Alertmanager в открытой версии нет, поэтому мы написали и открыли holmes-alert-bridge.

  • Агент у нас только читает и только строит гипотезы. Чинить он ничего не может — мы не дали.

  • Разбор занимает 2–3 минуты и стоит несколько рублей или 10–30 центов, в зависимости от модели.

  • До внедрения проверьте свои логи на пароли, токены и персональные данные: всё, что прочитает агент, уйдёт провайдеру модели.

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

Правило алерта знает только то, что написано в его выражении: метрика пересекла порог. Почему — оно не знает, а аннотация с советом «поднимите лимит» написана один раз и на все случаи. Дежурный получает сигнал и тратит первые минуты на сбор контекста, прежде чем начать думать.

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

Вот как это выглядит на одном реальном эпизоде в Tuna — подробно он разобран ниже, в кейсах. График построен в нашей Grafana по обычным метрикам: выражение из правила алерта, серия ALERTS и счётчики bridge.

График из Grafana: всплеск отброшенных соединений, состояние алерта pending и firing, период работы Holmes и момент появления гипотезы в чате

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

Время

Что произошло

14:34:49

метрика пересекла порог, алерт перешёл в pending: у правила for: 5m

14:39:49

алерт сработал; через 10 секунд Alertmanager отправил сообщение в чат и копию в bridge

14:39:59

Holmes начал расследование

14:42

всплеск закончился, алерт погас

14:44:07

разбор с гипотезой в чате — через 4 минуты 17 секунд после срабатывания алерта

От начала проблемы до гипотезы прошло чуть больше девяти минут, и пять из них — ожидание самого правила. Алерт к этому моменту уже погас, но теперь понятно, что это было и нужно ли что-то делать.

Три решения, которые мы приняли сразу.

Только гипотезы, без автопочинки. Сам HolmesGPT умеет больше: если дать ему права на запись и описать действия в skills, он будет не только искать причину, но и устранять её. Мы этого сознательно не включаем: у агента права только на чтение, а его ответ помечен как гипотеза, а не диагноз.

Оценка качества — честно оценка. По нашему опыту гипотеза оказывается верной в 90–95 % случаев. Это именно оценка по ощущениям от дежурств, а не результат замера: систематически сверять разборы с реальными причинами инцидентов мы пока не начинали.

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

Контуры получились разными, и это удобно для сравнения:

Tuna

Проект в российском облаке

Зарубежный проект на двух облаках

Кластеров

1

2

3

Модель

DeepSeek V4.1 Flash

DeepSeek V4.1 Flash

Claude Sonnet 5

Как подключена

OpenRouter

ИИ-роутер Selectel

напрямую у провайдера модели

Куда приходит разбор

Telegram

Slack

Slack

Что такое HolmesGPT

HolmesGPT — агент для расследования проблем в облачной инфраструктуре. Лицензия Apache 2.0, проект создан в Robusta.dev и развивается при заметном участии Microsoft, с октября 2025 года входит в CNCF Sandbox. На GitHub у него около 3,5 тысячи звёзд, релизы выходят раз в одну-две недели — за последние четыре месяца их было тринадцать. Мы используем версии 0.41–0.42.

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

  • Toolsets — наборы инструментов: Kubernetes (ресурсы, логи Pod’ов, метрики), Prometheus, VictoriaLogs, ArgoCD, Grafana, проверки сетевой доступности и другие. Свой набор описывается YAML-файлом со скриптами, кодом на Python или подключается как MCP-сервер.

  • Skills — то, что раньше называлось runbooks: файл SKILL.md с именем и описанием. Агент видит только описания, а сам текст подгружает по необходимости. Здесь живёт знание о вашем проекте.

  • Модель — любая, до которой дотягивается litellm: Anthropic, OpenAI, Azure, Bedrock, Gemini, Ollama, OpenRouter и любой OpenAI-совместимый API.

  • Способы запуска — CLI с интерактивной консолью, HTTP-сервер с Helm-чартом, оператор для Kubernetes (пока alpha), Python SDK.

Архитектура HolmesGPT: алерты и вопросы на входе, модель и runbooks сверху, данные наблюдаемости снизу, анализ причин на выходе

Схема из README HolmesGPT (github.com/HolmesGPT/holmesgpt), лицензия Apache 2.0.

Как мы его ставим

Основной сценарий — внутри Kubernetes, отдельная инсталляция на каждый кластер. Так не нужно строить сложную авторизацию: агент работает под ServiceAccount своего кластера с правами на чтение и ничего, кроме этого кластера, не видит. Запустить Holmes на выделенном сервере тоже можно — из CLI или docker-compose с kubeconfig, — но в кластере проще.

Минимальный values.yaml:

additionalEnvVars:
  - name: MODEL                       # ключ записи из modelList
    value: fast
  - name: OVERRIDE_MAX_CONTENT_SIZE   # окно контекста, см. «Грабли»
    value: "400000"
  - name: OPENROUTER_API_KEY
    valueFrom:
      secretKeyRef:
        name: holmes-llm
        key: OPENROUTER_API_KEY

modelList:
  fast:
    model: openrouter/deepseek/deepseek-v4.1-flash
    api_key: "{{ env.OPENROUTER_API_KEY }}"
    temperature: 0.5
    extra_body:
      provider:
        zdr: true                     # см. «Данные и безопасность»

toolsets:
  kubernetes/core:
    enabled: true
  kubernetes/logs:
    enabled: true
  prometheus/metrics:
    enabled: true                     # + config с адресом Prometheus
  victorialogs:
    enabled: true                     # + config с адресом и доступом
  skills:
    enabled: true
  # всё, что умеет писать или ходить наружу, выключаем явно
  bash:
    enabled: false
  internet:
    enabled: false
  robusta:
    enabled: false

enableTelemetry: false
helm repo add robusta https://robusta-charts.storage.googleapis.com
helm upgrade --install holmes robusta/holmes -n holmes --create-namespace -f values.yaml

По ресурсам это скромно: Holmes у нас занимает около полугигабайта памяти, всплески дают инструменты вроде kubectl на больших списках.

Не только алерты

Агента можно спрашивать и руками — это удобно, когда алерта нет, а вопрос есть. Через CLI (holmes ask "...") или прямо у инсталляции в кластере:

kubectl -n holmes port-forward svc/holmes-holmes 8080:80
curl -s localhost:8080/api/chat -H 'Content-Type: application/json' \
  -d '{"ask": "Почему Pod checkout в namespace shop перезапускается?"}' | jq -r .analysis

В консоли это выглядит так:

Консольная сессия HolmesGPT: вызовы инструментов kubectl и итоговый ответ агента

Запись из документации HolmesGPT (holmesgpt.dev): агент вызывает инструменты и формулирует ответ.

Мост к Alertmanager

Здесь нас ждал сюрприз. В открытой версии HolmesGPT умеет только забирать алерты: команда holmes investigate alertmanager разово читает активные алерты и разбирает их. Приёмника вебхуков в HTTP-сервере нет, у оператора триггер по алертам пока в планах, а автоматический разбор в продукте Robusta идёт через их SaaS-платформу.

Нам же нужно было простое: Alertmanager шлёт вебхук, через пару минут в том же чате появляется разбор. Так появился holmes-alert-bridge — небольшой сервис на Go под лицензией Apache 2.0.

Схема: путь алерта от Alertmanager через holmes-alert-bridge к HolmesGPT и обратно в чат

Путь алерта: обычное оповещение уходит в чат как раньше, копия — в bridge, а оттуда, если пройдёт фильтры, к агенту.

Почти всё, что делает bridge, — это защита бюджета и нервов:

  • Сразу отвечает 200. Holmes думает минуты, Alertmanager столько не ждёт. Расследование идёт в фоне.

  • Allowlist по имени алерта. Пустой список — сервис откажется стартовать: расследовать всё подряд означает счёт от провайдера за каждый флапающий алерт.

  • Дедупликация по эпизоду. Эпизод — это имя алерта, namespace и время начала. Повторные доставки того же эпизода игнорируются. Для упавших CronJob’ов ключом служит имя CronJob’а, иначе каждый новый запуск был бы новым эпизодом.

  • Троттлинг. По умолчанию не больше двух расследований в час и десяти в сутки. Это не тюнинг производительности, а потолок расходов.

  • Один воркер. Расследования идут строго по очереди, реплика у сервиса всегда одна.

  • Таймаут с разрывом стрима. Ответ агента читается потоком, поэтому через 15 минут bridge рвёт соединение, и Holmes останавливается на границе текущего шага, а не дорешивает задачу в фоне за ваш счёт.

  • Состояние в базе. Отметки дедупликации и троттлинга переживают рестарт: SQLite по умолчанию, PostgreSQL или MySQL — если база уже есть.

  • Доставка. Slack, Telegram, почта, произвольный вебхук. Markdown от агента переводится в разметку канала, длинный разбор не обрезается, а разбивается на части. Под разбором — длительность, стоимость, модель и ссылка на полную запись в VictoriaLogs.

Насколько это отсекает: в Tuna за первый месяц Alertmanager доставил в bridge 645 алертов, а до агента дошёл 41 — остальное сняли дедупликация, allowlist и троттлинг. В других контурах картина та же: больше 90 % доставок расследования не запускают.

Готовый отчёт выглядит так — это та же картинка, что в начале статьи:

Установка

Нужен работающий HolmesGPT в кластере и список алертов, которые имеет смысл расследовать.

helm repo add holmes-alert-bridge \
  https://gitlab.com/api/v4/projects/yuccastream%2Fholmes-alert-bridge/packages/helm/stable
helm upgrade --install holmes-alert-bridge holmes-alert-bridge/holmes-alert-bridge \
  -n holmes -f values.yaml
config:
  alertAllowlist:
    - KubernetesPodCrashLooping
    - KubernetesJobFailed
    - InstanceDown
  holmesUrl: http://holmes-holmes.holmes.svc:80
  holmesModel: fast            # ключ из modelList Holmes
  promptLang: ru
  clusterName: prod
  environment: production
  costCurrency: USD
  maxPerHour: 2
  maxPerDay: 10
  telegram:
    chatId: "-1001234567890"
  victoriaLogs:
    uiUrl: https://logs.example.com
    streamFilter: '{app="holmes-alert-bridge",cluster="prod"}'

existingSecret: holmes-alert-bridge-secrets   # TELEGRAM_BOT_TOKEN и остальные

На стороне Alertmanager — получатель и маршрут с continue: true, чтобы разбор дополнял обычное оповещение, а не заменял его:

receivers:
  - name: holmes
    webhook_configs:
      - url: http://holmes-alert-bridge.holmes.svc:8080/webhook
        send_resolved: false

route:
  routes:
    - receiver: holmes
      matchers:
        - severity =~ "warning|critical"
      continue: true

Если в вашем дереве маршрутов нет явного catch-all, continue может отнять доставку у алертов, которые раньше падали в корневой получатель. Тогда проще добавить bridge вторым webhook_configs к уже существующим получателям — у нас так сделано в четырёх кластерах из шести, а отбор целиком лежит на allowlist.

Остальное — каналы, секреты, свои шаблоны промптов и сообщений — описано в INSTALL.md.

Модели и бюджет

Как выбирали

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

Тогда мы устроили отбор: восемь моделей, 52 прогона в три волны, включая задачу с заранее известным ответом. Смотрели в первую очередь на недорогие модели: мониторинг не должен стоить дороже того, за чем он следит. Пару моделей классом выше добавили для сравнения.

Модель

Прогонов

Падений

Задача с известным ответом

Цена прогона

qwen3.7-flash

6

1

0 из 1

≈ 3 ₽

gpt-5.6-luna

4

0

0,5 из 2

≈ 6 ₽

deepseek-v4-flash-0731

16

0

4 из 4

≈ 16 ₽

minimax-m3

6

1

0 из 1

≈ 24 ₽

gemini-3.7-flash

4

1

1 из 1

≈ 41 ₽

longcat-2.0

4

0

0 из 2

≈ 42 ₽

glm-5.2

6

0

2 из 2

≈ 54 ₽

deepseek-v4-pro-0813

6

0

2 из 2

≈ 77 ₽

Цена прогона — оценка сверху, без учёта кэша: с кэшем разбор обходится в разы дешевле. Половина балла — частично верный ответ. Выборка небольшая: от 4 до 16 прогонов на модель.

Что из этого вышло:

  • По одному прогону сравнивать нельзя. Разница в цене, которая на первом прогоне выглядела как порядок, на полусотне сжалась до нескольких раз: разброс внутри одной модели оказался больше разницы между моделями.

  • Дешёвая модель — не значит дешёвый разбор. Половина кандидатов провалила задачу с известным ответом, причём одинаково: уверенно сообщила, что нужного компонента в кластере нет. Это был управляемый автоскейлер, которого среди Pod’ов действительно не видно.

  • Победила deepseek-v4-flash: 16 прогонов без единого падения и верный ответ во всех четырёх попытках. Она не самая дешёвая в списке, но обе модели дешевле неё задачу с известным ответом не решили.

В продакшене первая версия продержалась недолго. В одном контуре примерно каждый пятый разбор не укладывался в 15 минут. В другом модель зацикливалась на повторных вызовах инструментов и уходила в таймаут, а её версию к тому же сняли с поддержки у провайдера. В середине сентября мы переехали на DeepSeek V4.1 Flash. По прайсу она в разы дороже, средний разбор тоже подорожал — зато разборы стали заканчиваться, и заканчиваться быстро.

Оговорка: чистого сравнения «до и после» у нас нет. В те же дни мы поправили инструкции для инструментов и обновили bridge, и вклад каждого изменения по отдельности не измеряли.

Что получилось

Tuna

Проект в российском облаке

Зарубежный проект

Модель

DeepSeek V4.1 Flash

DeepSeek V4.1 Flash

Claude Sonnet 5

Успешных разборов

22 из 22

92,9 %

15 из 15

Длительность, медиана

2–3 минуты

2–3 минуты

около минуты

Цена разбора

около 10 центов

несколько рублей

25–30 центов

Окно — 30 дней до 6 октября 2026 года; успешность считаем после стабилизации — смены модели и выхода bridge v0.11. Рублёвые суммы — оценка: цены модели в конфигурации у нас расходились с прайсом роутера.

Счёт за месяц — единицы долларов на проект: около четырёх долларов в Tuna по биллингу провайдера, четыре-пять в зарубежном проекте на три кластера и меньше тысячи рублей на два кластера в российском облаке. В Tuna мы сверили оценку стоимости, которую bridge берёт из ответа Holmes, с биллингом ключа — сошлось до цента.

Почему хватает дешёвой быстрой модели

Разбор алерта — это около миллиона токенов, из которых сама модель генерирует пару десятков тысяч. Всё остальное — накопленная история, которую агент пересылает на каждом ходу. Отсюда три свойства модели, которые важнее цены за миллион токенов по прайсу:

  • Дешёвое чтение кэша. По нашей оценке, из кэша читается около 90 % входных токенов. Это оценка обратным счётом, а не цифра из биллинга, но порядок понятен: экономика разбора держится на кэше.

  • Большое окно контекста. При окне в 200 тысяч токенов Holmes начинал сжимать историю уже около 126 тысяч, после сжатия заново запрашивал собранное — и разбор становился дольше и дороже. С окном в 400 тысяч сжатия прекратились.

  • Надёжный вызов инструментов. Модель, которая время от времени пишет вызов инструмента текстом или ломает JSON аргументов, обходится дороже любой «умной».

Где стоит модель подороже: в зарубежном проекте с первого дня работает Claude Sonnet 5, подключённая напрямую у провайдера. Сравнения моделей там мы не устраивали. Алертов в этом проекте мало — большинство гаснет раньше, чем Alertmanager успевает их отправить, — поэтому в абсолютных цифрах дорогая модель обходится в те же несколько долларов в месяц.

Два практических совета:

  • Ставьте месячный лимит на ключ провайдера. Троттлинг bridge защищает только путь от Alertmanager; ручные вопросы и другие клиенты агента идут мимо него.

  • Сверяйте цены. Если модель описана в modelList со своими input_cost_per_token и output_cost_per_token, стоимость в отчёте считается по ним. У нас эти числа расходились с прайсом роутера, и суммы в чате оказывались неверными.

Данные и безопасность

Сначала посмотрите, что лежит в ваших логах

При внешнем API всё, что прочитает агент, уходит провайдеру модели: строки логов, манифесты, события, метки метрик. До внедрения проверьте свои приложения и логи на пароли, токены и персональные данные. Агент не отличает секрет от обычной строки: содержимое лога попадает в запрос к модели как есть.

Что мы советуем сделать до первого алерта.

1. Аудит логов. У агента есть доступ к логам Pod’ов всего кластера. Если приложение пишет в лог токен авторизации или адрес почты клиента, это надо чинить в приложении — и сделать это до подключения агента, а не после.

2. Zero Data Retention — в двух местах. ZDR означает, что провайдер не хранит ваши запросы. В OpenRouter его стоит включать и настройкой аккаунта, и параметром запроса provider.zdr: true (в примере выше он уже есть). Настройку аккаунта не видно из конфигурации кластера, а параметр запроса зависит от того, донесла ли его цепочка библиотек, — вместе они страхуют друг друга. Проверяется это живым запросом: при включённом ZDR обращение к провайдеру вне ZDR-списка возвращает ошибку ZDR violation.

3. Роутер, у которого ZDR есть на деле. Годится любой роутер с API, но лучше тот, чьи провайдеры действительно дают нулевое хранение. OpenRouter публикует живой список таких endpoint’ов, и на него стоит посмотреть до выбора модели: одна и та же модель у разных хостеров может быть как в списке, так и вне его.

4. Права только на чтение. В чарте HolmesGPT по умолчанию включены наборы bash и internet — выключайте их явно. Secret’ы чарт агенту читать не даёт, но запрет на изменения должен обеспечивать RBAC, а не фраза в промпте.

5. Правило в skill’е — не техническое ограничение. Мы пишем агенту «работай с агрегатами, не цитируй персональные идентификаторы», и он в целом слушается. Но это текст, а не барьер, и полагаться на него как на защиту нельзя.

6. Закройте API самого Holmes. По умолчанию /api/chat доступен без аутентификации любому Pod’у кластера, а вместе с ним — права агента на чтение и ваш бюджет у провайдера. Варианты — NetworkPolicy (проверьте, что ваш CNI их исполняет) или ключ HOLMES_API_KEY.

Теперь честно про нас. В Tuna стоит OpenRouter с ZDR, включённым в обоих местах, — проверено живыми запросами. В проекте в российском облаке запросы идут через ИИ-роутер Selectel: отдельной настройки ZDR там нет, и мы опираемся на заявленную провайдером политику обработки запросов. В зарубежном проекте модель подключена напрямую у её провайдера: политика хранения там — свойство договора и настроек организации, из конфигурации кластера её не видно, и это отдельный пункт в нашем чек-листе.

VictoriaLogs

Логи — главный источник данных для агента: на них приходится больше всего вызовов инструментов. Мы рекомендуем VictoriaLogs: она быстрая, а её HTTP API достаточно прост, чтобы им одинаково легко пользовались и скрипты, и агенты. В HolmesGPT для неё есть встроенный toolset.

Мы специально переехали на VictoriaLogs — два проекта с Loki, один с Graylog — именно ради того, чтобы логи стали доступны программно: и нашим рабочим агентам, и Holmes. В итоге хранилище играет для агента сразу три роли:

  • источник логов приложений;

  • собственная память: skill ищет прошлые разборы в логах bridge;

  • адрес ссылки «полный разбор» под сообщением в чате.

Вот запись, на которую ведёт ссылка «полный разбор» под сообщением из кейса про очередь TCP. Служебные поля коллектора убраны, текст разбора обрезан:

{
  "_time": "2026-10-03T10:44:06.793Z",
  "_msg": "investigation done",
  "cluster": "yucca",
  "alertname": "TCPListenQueueOverflow",
  "episode": "TCPListenQueueOverflow//2026-10-03T10:39:49Z",
  "starts_at": "2026-10-03T10:39:49Z",
  "duration": "4m7s",
  "cost": "0.0542736192",
  "cost_currency": "USD",
  "prompt_tokens": "1119866",
  "completion_tokens": "20957",
  "total_tokens": "1140823",
  "analysis": "I found a skill named **tuna-network-attacks** и использовал его для разбора…"
}

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

_time:30d {app="holmes-alert-bridge"} "investigation done"
  | stats by (alertname) count() n, sum(cost) total, avg(cost) avg
  | sort by (total) desc

Одной установки toolset’а мало — агенту нужно описать схему полей. Имена зависят от коллектора: у vector namespace лежит в kubernetes.pod_namespace, у fluent-bit — в kubernetes.namespace_name, и модель этого не угадает. Наши инструкции к toolset’у логов по смыслу такие:

Log fields (collector: vector):
  app, cluster, kubernetes.pod_namespace, kubernetes.pod_name, level, _msg
A bare "phrase" is searched in _msg only. To filter by a field write field:="value".
Pass `start` only. Never pass `end`, null or "now".
One failed call does not mean the storage is down: fix the query and retry once.
Do not name a root cause from a tail of 50 lines. Count first:
  ... | stats by (level, app) count() n

Промпты и skills

Знание о проекте у нас разложено по четырём слоям.

Рамки расследования задаёт системный промпт запроса — он встроен в bridge, есть на русском и английском. Смысл: ты расследуешь алерт без человека в диалоге; только чтение; держись namespace алерта; твой вывод — гипотеза; ответ уйдёт одним сообщением в чат — вердикт, наиболее вероятная причина, два-три шага проверки.

Паспорт кластера — skill, который агент читает первым. Это самый полезный текст во всей настройке. Обезличенный пример по структуре наших:

---
name: prod-cluster
description: How the production cluster is built, what is normal here and what is not.
  Read this FIRST for any investigation in this cluster.
---
## What this cluster is
Managed Kubernetes, production of an online store. Node pools: `apps` (stateless
services, autoscaled) and `infra` (monitoring, ingress, ArgoCD).

## Where the workloads live
- `shop` — API and web frontend, the only namespace that serves customers.
- `jobs` — CronJobs: exports, reports, cleanup. A single failed run is rarely urgent.
- `monitoring`, `ingress`, `argocd` — infrastructure.

## Databases are outside the cluster
PostgreSQL and Redis are managed services. You cannot kubectl into them:
use their metrics and `tcp_check`.

## ArgoCD owns the infrastructure
A manual change will be reverted. Never suggest `kubectl edit`.

## What you may read, and what you may not
Application logs may contain customer data. Work with counts and aggregates,
never quote e-mails, phone numbers or order contents.

## What is normal here
- Nodes of the `apps` pool are replaced every night: short NotReady and evictions
  between 02:00 and 03:00 UTC are expected.
- `jobs/report-*` retries up to three times. Only the third failure matters.

## How to fill tool parameters
Time range: pass `start` only. Log fields: see the logs toolset instructions.

## Blaming an external provider is a claim you can check
Before writing "the payment gateway is down", run `tcp_check` against it
and compare the error rate with the previous day.

## What always deserves escalation
Anything in `shop` that lasts longer than 10 minutes; any sign of data loss.

Skills под типы алертов и сервисы. Скелет один и тот же:

---
name: edge-triage
description: Investigate EdgeDown and EdgeHighErrorRate. The edge proxies run on plain
  hosts OUTSIDE Kubernetes, so kubectl is useless here.
---
## What the edge is           # слой → где живёт → systemd-unit → job метрик
## How the alert reaches you  # выражение правила, что значат метки
## Workflow                   # 4–6 шагов с готовыми запросами LogsQL и PromQL
## Synthesize                 # что обязано быть в ответе, ссылки на дашборды

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

- alert: KubernetesJobFailed
  annotations:
    summary: "Job {{ $labels.job_name }} failed"
    ai: >-
      Jobs in the `jobs` namespace are retried by the scheduler. Check whether
      the next run succeeded before calling it an incident.

Что мы поняли, пока это писали:

  • Тексты skills — на английском, ответ — на русском. Это сочетание у нас работает без нареканий.

  • Инструмент, не названный в skill’е, почти не вызывается. Мы подключили агенту ArgoCD, Grafana и TCP-проверки — и он ими практически не пользовался. Поэтому в паспорте кластера стоит держать раздел «инструменты, о которых ты забываешь», а в skills — называть нужный инструмент прямо в шагах.

  • Правило надо класть туда, где модель его точно прочтёт. Запрет передавать end: "now" стоял в инструкциях toolset’а логов, а модель всё равно его писала — десятки упавших вызовов. Помогло только общее правило в паспорте кластера; теперь оно встроено и в системный промпт bridge.

  • Сломанные toolset’ы надо выключать явно. Встроенные наборы в статусе failed всё равно попадают в системный промпт, и агент пытается ими пользоваться.

  • «Виноват внешний сервис» — это проверяемое утверждение. Пока у агента не было инструмента сетевой проверки и требования проверять самому, разборы недоступности партнёрского API заканчивались советом «сделайте curl».

Skill, который месяц ничего не находил

Самая поучительная наша ошибка. У агента есть skill «не разбирали ли мы это уже»: перед расследованием он ищет в логах bridge прошлые разборы того же алерта. Идея хорошая, и в Tuna агент обращался к этому skill’у чаще, чем к любому другому.

В двух контурах из трёх он месяц искал не по тому полю. Запрос в skill’е искал имя алерта голой фразой, а голая фраза в LogsQL ищется только в тексте сообщения — где лежит ровно investigation done и больше ничего:

{app="holmes-alert-bridge"} "investigation done" "KubernetesJobFailed"            → 0 записей
{app="holmes-alert-bridge"} "investigation done" alertname:="KubernetesJobFailed" → 73 записи

Агент выполнил сотни таких запросов и каждый раз честно писал «прошлых разборов нет» — в том числе про эпизод, который за месяц был разобран девятнадцать раз. Никакой ошибки при этом не возникало: пустой ответ — тоже ответ. В третьем контуре запрос был написан с фильтром по полю, и там память работала: агент ссылался на свои прошлые выводы и даже поправлял их.

Исправление — одна строка. Мораль шире: skill для агента надо тестировать как код. Каждый запрос из него стоит хотя бы раз выполнить руками и убедиться, что он что-то находит.

Кейсы

Очередь TCP переполнена, но sysctl тут ни при чём

На одном из серверов Tuna сработал TCPListenQueueOverflow: ядро отбрасывает входящие соединения, потому что очередь слушающего сокета переполнена. В описании алерта зашит стандартный совет — поднять net.core.somaxconn или backlog в приложении.

Telegram: алерт TCPListenQueueOverflow с советом поднять net.core.somaxconn и сообщение о том, что он погас

Алерт в Telegram: сработал в 14:39 и через три минуты погас.

Telegram: разбор Holmes по алерту TCPListenQueueOverflow — пять проверенных шагов, вердикт и что проверить

Разбор Holmes пришёл в 14:44 — в тот же чат, под алертом.

Holmes за четыре минуты и пять центов выяснил другое:

  1. Переполнение есть только на одном хосте, за предыдущие семь дней — ноль. Счётчик SYN cookies почти не вырос, значит, это не SYN-флуд.

  2. Слушатель, который перестал принимать соединения, — sshd. В его журнале нашлись записи о начале MaxStartups throttling и об отброшенных соединениях — около двух тысяч за эпизод.

  3. Источник — два адреса и порядка 180 попыток входа с несуществующими логинами за 15 минут.

  4. Ресурсы хоста в норме, остальные сервисы на нём и соседние хосты не задеты.

  5. К моменту разбора алерт уже погас, порты отвечают.

Вердикт: кратковременная потеря TCP-соединений из-за флуда SSH-подключений. Очередь переполнилась, потому что sshd упёрся в собственное ограничение MaxStartups, а не потому, что хосту не хватило ресурсов; net.core.somaxconn здесь ни при чём. И отдельное наблюдение: fail2ban такой поток не отсекает, потому что соединения рвутся до этапа аутентификации.

Совет из описания алерта увёл бы дежурного в сторону. А на скриншоте заодно видны две наши болячки из этой статьи: фраза «прошлых разборов этого алерта нет» — тот самый сломанный skill — и служебная преамбула агента в начале сообщения.

VirusTotal: шум или сигнал

У нас есть открытый инструмент, который следит за тем, как наши IP-адреса, публикуемые домены и хэш-суммы сборок выглядят в базах антивирусов и спам-листах. Задача — отреагировать раньше, чем пользователь получит предупреждение «ваш софт вредоносный».

Срабатывания у него бывают двух сортов. Чаще всего это шум: один-два малоизвестных движка пометили адрес туннельного сервера. Изредка — настоящий сигнал. Теперь каждое срабатывание дополняется вердиктом Holmes: отдельный skill объясняет ему, какие движки считать шумом и какие сочетания признаков — поводом звать человека.

Сообщение о срабатывании VirusTotal с вердиктом Holmes

Это пример сообщения

В этом примере агент сначала признал, что срабатывание по адресу — шум движков малого охвата. Но заметил в отчёте новый файл с заметным числом детектов, трафик которого идёт на туннельный поддомен, — и эскалировал: через туннель, вероятно, раздаётся вредоносный файл, нужен человек. Заодно проверил, что прошлых вердиктов по этому файлу нет, и что туннель сейчас не используется, — то есть владельца надо искать по учётной записи.

Этот путь идёт к Holmes напрямую, мимо bridge, — и это ещё одна причина держать лимит на ключе провайдера.

Ещё два коротких

  • Агент разобрал собственное падение. После обновления новый Pod Holmes ушёл в CrashLoop, и алерт об этом достался старому Pod’у. Тот нашёл отказ в доступе к новому пути в хранилище секретов, сравнил окружение двух ревизий и отметил, что сервис при этом не лежит, потому что старый Pod жив. Полторы минуты, десять центов.

  • Один алерт — две разные причины. В проекте в российском облаке агент связал нехватку памяти на прерываемом узле с плановой заменой соседнего узла и нашёл Pod, который потреблял заметно больше запрошенного. Через два дня на том же алерте он назвал другую причину — совпавшие расписания CronJob’ов — и явно написал, чем этот эпизод отличается от предыдущего.

Грабли

Висящий алерт разбирается заново каждые сутки. Окно дедупликации по умолчанию — 24 часа, и если алерт горит дольше, он честно разбирается ещё раз. В одном контуре на такие повторы уходила почти половина бюджета, причём половину разборов в проде давали несколько хронически падающих CronJob’ов. Лечится с двух сторон: увеличить окно (dedupWindow) до нескольких суток и наконец починить то, что горит неделями, — счёт за разборы очень мотивирует.

Служебные вставки агента в сообщениях. Holmes оставляет в ответе заготовки под встраивание графиков в интерфейс Robusta: блоки вида << {"type": "promql", ...} >> и картинки со схемой tool-image://. В чате это выглядит как мусор, и встречалось оно больше чем в половине разборов. Начиная с v0.11.1 bridge вырезает их перед отправкой. Преамбулу «I found a skill named…» в начале ответа убрать сложнее: это формат самого Holmes при работе со skills, правило в системном промпте на неё не действует.

Модель ломает аргументы инструментов. Время от времени модель отдаёт аргументы вызова не объектом, а строкой с JSON внутри. Holmes на этом падает с ошибкой валидации, и разбор теряется целиком — в одном контуре это была самая частая причина неудачных разборов. Bridge в таких случаях повторяет разбор сразу, если позволяет бюджет, а с v0.11.1 пишет в лог, когда повтор не состоялся. В конце сентября эти падения прекратились сами, без изменений с нашей стороны.

Алерты на сам bridge. Месяц у нас не было ни одного правила на метрики bridge: о затроттленных алертах и таймаутах можно было узнать только из логов. Минимум:

- alert: HolmesBridgeInvestigationsFailing
  expr: increase(holmes_bridge_investigations_total{status!="ok"}[1h]) > 0
- alert: HolmesBridgeThrottling
  expr: increase(holmes_bridge_alerts_throttled_total[1h]) > 0

В allowlist самого bridge эти алерты добавлять не надо.

Цены в modelList ломают окно контекста. Стоит вписать модели свои цены — и Holmes 0.41 перестаёт понимать размер её окна и падает на первом же вызове инструмента. Обходится переменной OVERRIDE_MAX_CONTENT_SIZE. В 0.42 падения уже нет, но окно по умолчанию — 200 тысяч, и для длинных разборов его всё равно стоит задать явно.

Инструменты падают по памяти. kubectl на больших списках упирался не в лимит Pod’а, а в лимит памяти на один вызов инструмента. Он задаётся отдельно — TOOL_MEMORY_LIMIT_MB.

Парные правила дают два разбора. Если в allowlist стоят и общее правило, и его специализированный двойник, один инцидент разбирается дважды с разницей в секунды. Оставляйте одно из пары.

«Успешный» разбор без единого запроса. На старой модели дважды случилось так: агент прочитал пару skills, не сделал ни одного запроса к данным и уверенно написал, что у него нет доступа к метрикам. Формально разбор успешен, стоит он доли цента. Аномально дешёвый разбор — повод посмотреть на него глазами.

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

Что дальше

Чего мы пока не измерили — и не хотим делать вид, что измерили:

  • качество гипотез — 90–95 % остаются оценкой, пока мы не разметим выборку разборов против реальных причин инцидентов;

  • эффект для людей — насколько сократилось время разбора инцидента.

Что хочется сделать в bridge:

  • метрику потраченной суммы, чтобы бюджет было видно в Grafana рядом с остальными счётчиками;

  • аккуратнее обходиться с висящими алертами — растущий интервал повторного разбора вместо фиксированного окна;

  • ключ API для запросов к Holmes;

  • ссылку на разбор не только в VictoriaLogs, но и в Loki, Elasticsearch и других хранилищах;

  • новые каналы доставки — Mattermost, Microsoft Teams, Discord.

Чего в bridge не будет: параллельных расследований, правил маршрутизации (это работа Alertmanager) и автоматической починки.

Код, чарт и документация — в репозитории holmes-alert-bridge. Если попробуете у себя — расскажите, что получилось: issue и merge request’ы приветствуются.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.