Create enabled: No в LLD-override Zabbix 7.4 не глушит уже обнаруженные Link down
Каждый, кто настраивал low-level discovery сети через Template Net Interfaces SNMP, рано или поздно ловит один и тот же парадокс: в override выставили Create enabled: No для триггер-прототипа Link down, а аварии по портам, обнаруженным до этой правки, как сыпались, так и продолжают сыпаться. Ниже — разбор, как LLD-overrides на самом деле применяются в Zabbix 7.4, и пошаговая схема, которой мы в ITfresh глушим уже обнаруженные интерфейсы без потери истории по метрикам.
Симптом: отключили создание триггеров, а старые Link down не молчат
Классическая ситуация в сетевом мониторинге: коммутатор с десятками портов, часть из которых декоративные (patch-панель, резерв, порты в down по проекту), часть — боевые аплинки. Правило LLD «Discovery of network interfaces» на базе шаблона Template Net Interfaces SNMP находит их все, создаёт item-прототипы и триггер-прототип «Interface {#IFNAME}({#IFALIAS}): Link down». Через неделю эксплуатации дежурный тонет в алертах с портов, которые физически никогда не будут подключены.
Логичное на первый взгляд решение — зайти в override правила LLD и для операции над триггер-прототипом Link down выставить Create enabled: No. Правка сохраняется, интерфейс успокаивается… для новых портов. А по портам, которые были обнаружены раньше этой правки, статус триггеров как был Enabled, так и остаётся — они продолжают открывать проблемы при каждом флапе линка.
Причина не в баге платформы, а в том, что операция override применяется не к правилу LLD целиком и не к уже созданным сущностям, а только к моменту материализации нового экземпляра прототипа для конкретного низкоуровневого объекта, который правило видит впервые (или который заново проходит по условиям override). Уже существующий триггер был создан в прошлом цикле обнаружения, когда действовало другое (или отсутствовавшее) правило override — и Zabbix не пересматривает статус задним числом.
У нас в практике этот тикет приходит в двух видах. Первый — «включили override только что, а старое всё ещё шумит». Второй, более коварный — «включили override месяц назад, вроде помогло, а сейчас снова посыпалось». Второй вариант почти всегда означает, что коммутатор пережил переобнаружение декоративных портов (например, после ребута или смены прошивки поменялись SNMP-индексы или имена интерфейсов), и LLD создало для них новые item/trigger — но уже без учёта override, если он на момент создания не покрывал новое имя порта или был написан под старый формат {#IFNAME}.
Стоит развести две вещи, которые в заявках клиентов обычно путают: отключение триггер-прототипа в самом шаблоне (влияет только на будущие обнаружения объектов, наследующих шаблон целиком) и override — локальную настройку, действующую только на объекты, подпадающие под условие фильтра конкретного override внутри одного LLD-правила. Create enabled: No в override не эквивалентен ни глобальному отключению прототипа, ни ретроактивной правке уже созданных сущностей — это операция, применяемая избирательно и только вперёд по времени.
Create enabled и Discover — два разных рубильника в override
В конфигурации LLD-правила (Data collection → Hosts → Discovery → правило → вкладка Overrides → Operations) для каждого типа прототипа — item, trigger, graph, host — можно задать операцию update. У неё есть два принципиально разных поля, которые на слух звучат похоже, а работают по-разному.
| Поле операции | Что определяет | Когда вычисляется | Эффект на уже созданные сущности |
|---|---|---|---|
| Create enabled | Статус (Enabled/Disabled), с которым будет создан НОВЫЙ item/trigger/graph/host по прототипу для низкоуровневого объекта, подпадающего под условие override | Только в момент первого создания сущности LLD-правилом для конкретного обнаруженного объекта | Никакого — существующая сущность статус не меняет |
| Discover | Будет ли низкоуровневый объект вообще считаться «обнаруженным» в текущем и следующих циклах правила | На каждом цикле опроса LLD-правила (по Update interval) | Переводит связанные сущности в состояние «не обнаруживается», запуская таймеры Disable/Delete lost resources, заданные на самом правиле |
| Discovery rule status | Глобальный вкл/выкл всего правила LLD целиком | Постоянно, пока правило активно или отключено | При отключении правила обнаружение просто не выполняется — ни новых сущностей, ни перевода старых в lost; они замирают в текущем состоянии |
Из таблицы видно ключевой вывод: единственный штатный механизм override, который способен воздействовать на уже обнаруженные интерфейсы, — это Discover, а не Create enabled. Но и он действует не мгновенно, а через жизненный цикл lost resources, который настраивается отдельно на самом правиле LLD, а не в override.
Обработкой правил LLD (в том числе применением overrides) на сервере занимаются отдельные рабочие процессы — начиная с Zabbix 6.4 это выделенный менеджер и пул воркеров, число которых задаётся параметром StartLLDProcessors в zabbix_server.conf (по умолчанию — 2). На инсталляциях, где discovery сети обслуживает десятки коммутаторов с сотнями портов каждый, при недостаточном числе воркеров цикл обработки правила может растягиваться, и правка override долетает до реально применённого состояния с задержкой, которая на глаз выглядит как «override не работает», хотя на деле просто не обработан следующий цикл LLD в очереди.
Анатомия правила: {#IFNAME}, {#IFOPERSTATUS}, {#IFADMINSTATUS} и макрос {$IFCONTROL}
Template Net Interfaces SNMP (в актуальных сборках поставляется в составе Generic SNMP templates под именем Network interfaces discovery) опрашивает IF-MIB и формирует набор LLD-макросов на каждый физический и виртуальный интерфейс: {#IFNAME} — имя интерфейса из ifName/ifDescr, {#IFALIAS} — описание порта, {#IFOPERSTATUS} и {#IFADMINSTATUS} — операционный и административный статус интерфейса на момент цикла discovery (используются прежде всего в условиях фильтра правила и overrides, а не как live-значение в триггере).
Живое значение статуса линка читает отдельный item-прототип с ключом вида net.if.status[ifOperStatus.{#SNMPINDEX}], а на него уже опирается триггер-прототип «Interface {#IFNAME}({#IFALIAS}): Link down». В его выражении заложена проверка контекстного пользовательского макроса {$IFCONTROL:"{#IFNAME}"}. По умолчанию для каждого обнаруженного интерфейса он равен 1 («порт важен, реагировать на обрыв»), и именно поэтому все вновь обнаруженные интерфейсы по умолчанию алертят.
Практическая ценность этого макроса в том, что он резолвится не на этапе discovery, а на каждом вычислении триггера. Если вручную выставить на хосте {$IFCONTROL:"GigabitEthernet0/7"} = 0, уже существующий и давно созданный триггер Link down по этому порту перестанет открывать проблему уже на следующем пересчёте — без пересоздания прототипа, без ожидания цикла LLD и без единой потери точки истории по item.
Ещё два макроса того же семейства, которые обычно настраивают на уровне хоста или шаблона, а не трогают в override: {$NET.IFNAME.MATCHES} и {$NET.IFNAME.NOT_MATCHES} — регулярные выражения, которыми фильтр самого LLD-правила ограничивает набор интерфейсов, попадающих в discovery в принципе (например, исключая loopback, null-интерфейсы и служебные тоннели). Если декоративный порт можно однозначно описать именем на уровне всего хоста, часто проще расширить {$NET.IFNAME.NOT_MATCHES}, чем городить отдельный override — но у этого подхода побочный эффект: порт вообще перестаёт обнаруживаться правилом, то есть подпадает под тот же жизненный цикл lost resources, что и override с Discover: No.
Три уровня, на которых можно погасить уже обнаруженный Link down
На практике мы комбинируем три механизма — они решают разные задачи и по-разному быстро действуют.
- Контекстный макрос {$IFCONTROL:"{#IFNAME}"} = 0 на уровне хоста. Точечное решение для конкретных портов конкретного устройства. Эффект — на следующем вычислении триггера (обычно секунды-минуты), история item не трогается, сам триггер остаётся в статусе Enabled, просто его выражение больше не «взводится».
- Override с условием фильтра и операциями Discover: No + Create enabled: No на тригер-прототипе. Работает группой — например, по regex {#IFNAME} для декоративных портов патч-панели или по {#IFADMINSTATUS}, если порт заведомо выключен административно. Эффект наступает только после следующего цикла правила LLD и только если на самом правиле настроены Disable/Delete lost resources — иначе объект уходит в «не обнаруживается», но триггер остаётся Enabled и физически может продолжать оцениваться, пока не сработает таймер disable.
- Прямое отключение уже существующих триггеров. Mass update в интерфейсе или пакетный вызов API trigger.update. Это единственный способ погасить конкретный уже открытый шторм алертов здесь и сейчас, не дожидаясь ни следующего цикла discovery, ни истечения lost-resources таймеров.
Для сценария «выключили Create enabled, а старое продолжает сыпаться» правильная последовательность — начать с пункта 3 (снять текущую боль), затем пункт 2 (закрыть создание новых по декоративным портам через override), и точечно пункт 1 там, где нужна гранулярность на один конкретный порт без правки override и без ожидания цикла.
Настройка override пошагово: закрываем будущие обнаружения
Путь в интерфейсе: Data collection → Hosts (или Templates) → выбранный хост/шаблон → Discovery → нужное LLD-правило → вкладка Overrides → Create override.
- Name — понятное имя, например «Decorative ports — no link down».
- Filter — условие по LLD-макросу, например {#IFNAME} matches regex, описывающий диапазон декоративных портов, или {#IFALIAS} matches «PATCH|RESERVE» (регулярка зависит от вендора и схемы именования — единую маску под все модели коммутаторов мы не используем, тестируем per-vendor).
- Stop processing — включаем, если после этого override не должны применяться следующие по списку overrides того же правила: иначе более низкий по приоритету override может откатить Create enabled обратно на Yes.
- Operations → Object: Trigger prototype → указываем конкретный прототип «Link down» → Create enabled: No (не создавать новый экземпляр триггера для новых интерфейсов, подпавших под условие) и, если нужно вовсе не плодить item/graph по этим портам, дополнительно Discover: No на уровне host prototype/item prototype, где это применимо.
Важный нюанс: операция Create enabled не наследуется автоматически на graph prototype и host prototype — если для них тоже задан отдельный оператор в этом же override, статус нужно продублировать явно для каждого типа объекта, иначе график или зависимый прототип хоста создастся с настройками по умолчанию.
Тот же override можно завести через API методом discoveryrule.update, передав массив overrides с условиями (filter.conditions по макросу и оператору) и operations (operationobject: trigger prototype, operator: like, value: «Link down», opstatus.status: disabled). На практике так делают, когда список декоративных портов формируется не руками, а выгружается из внешней системы учёта портов (IPAM/учёт патч-панелей) — тогда список условий override пересобирается скриптом и заливается в правило автоматически при каждом изменении схемы коммутации, без ручного похода в веб-интерфейс.
Ещё один практический момент: у override есть порядок (Sort order) — они применяются последовательно сверху вниз, и, как уже отмечено выше, при отсутствии Stop processing на нужном override результат может «перебиваться» следующим по списку. Мы придерживаемся правила: overrides, которые сужают набор обнаруживаемого (декоративные порты, служебные VLAN-интерфейсы), ставим выше по списку и всегда со Stop processing, а overrides, которые только меняют оформление (severity, теги, группировку по хосту), — ниже, без остановки цепочки.
Массовое отключение уже существующих Link down: UI и API
Для немедленного гашения текущего шторма — без правки шаблона и без ожидания цикла discovery — используем два инструмента.
Через интерфейс: Data collection → Hosts → Triggers, в фильтре задаём Name «Link down» (и при необходимости Host groups/Tags, чтобы не задеть чужие площадки), отмечаем нужные строки чекбоксами и внизу списка выбираем Mass update → Status → Disable.
Через API — удобно, когда портов сотни и они размазаны по десяткам хостов. В Zabbix 7.4 токен API передаётся заголовком Authorization: Bearer, параметр auth в теле запроса для токенов больше не обязателен.
curl -s -X POST https://zabbix.example.local/api_jsonrpc.php -H "Content-Type: application/json-rpc" -H "Authorization: Bearer API_TOKEN" -d '{"jsonrpc":"2.0","method":"trigger.get","params":{"output":["triggerid","description","status"],"search":{"description":"Link down"},"filter":{"status":0}},"id":1}'curl -s -X POST https://zabbix.example.local/api_jsonrpc.php -H "Content-Type: application/json-rpc" -H "Authorization: Bearer API_TOKEN" -d '{"jsonrpc":"2.0","method":"trigger.update","params":{"triggerid":"29981","status":1},"id":2}'
Практика: сначала trigger.get с фильтром по description и, если нужно, по hostids конкретной группы устройств, складываем список triggerid в файл, затем прогоняем trigger.update в цикле — это быстрее и безопаснее, чем гадать с regex по списку в UI, особенно когда нужно погасить порты по десяткам однотипных коммутаторов разом.
Здесь важно не спутать два похожих по названию API-метода. trigger.update меняет статус самого триггера (экземпляра, уже созданного из прототипа) — это именно то, что нужно для немедленного гашения. А правка дискавери-правила и его overrides делается методом discoveryrule.update — им меняют условия и операции для будущих циклов, но он не трогает уже созданные триггеры напрямую. Если автоматизировать зачистку регулярно (например, раз в сутки гасить всё, что подпадает под новый список декоративных портов), связку из этих двух методов удобно завернуть в отдельный скрипт и повесить по расписанию, отдельно от самого discovery.
Настройки самого правила LLD: интервал и lost resources
Параметры Disable lost resources и Delete lost resources настраиваются не в override, а в самом LLD-правиле — на той же вкладке, где Update interval и Filters. Именно они определяют, как быстро триггер, переставший «обнаруживаться» из-за override Discover: No (или из-за реального исчезновения порта), реально перейдёт в статус Disabled и когда, если вообще, будет удалена связанная с ним сущность.
| Параметр | Допустимые значения | Наша практика для интерфейсной discovery | Риск при неверной настройке |
|---|---|---|---|
| Update interval правила | время с суффиксом (например 1h — типовое значение по умолчанию для discovery сетевых интерфейсов) | оставляем как есть, не гоняем правило чаще, чтобы не нагружать SNMP-поллер на устройствах с сотнями портов (оценка по практике) | слишком редкий интервал — поздно замечаем новые/пропавшие порты |
| Disable lost resources | Never / Immediately / After (значение должно быть больше update interval правила) | After, с запасом в 2-3 цикла правила — чтобы не ловить ложные disable из-за разовых сбоев опроса (оценка по практике, не значение по умолчанию из документации) | Never — триггер годами висит Enabled без живых данных; Immediately — риск ложного disable при кратковременном сбое SNMP |
| Delete lost resources | Never / Immediately / After (должно быть больше периода disable) | Never или длительный After — интерфейсы имеют свойство временно пропадать из discovery при флапе и переподключаться, терять историю по ним нежелательно | по официальной документации Immediately «не рекомендуется, так как ошибка в фильтре может привести к удалению сущности вместе со всей историей» |
Отдельно подчеркну: для item-прототипов (сырые счётчики трафика, ошибок, статуса интерфейса) мы Delete lost resources почти никогда не ставим агрессивным — удаление item необратимо стирает историю, а именно её по условию задачи требовалось сохранить. Гасить нужно алерт (trigger), а не измерение (item).
Грабли, которые мы словили при внедрении этой схемы
Несколько наблюдений из практики настройки LLD-overrides на сетевом оборудовании разных вендоров — не из документации, а из реальных внедрений.
- Если на правиле LLD несколько overrides подряд и не выставлен Stop processing на нужном, следующий override в списке может переопределить Create enabled обратно на Yes для того же прототипа — итог правки выглядит непредсказуемым, пока не построишь полную цепочку условий.
- Автоматически задизейбленный по Disable lost resources триггер включится сам, как только порт снова начнёт обнаруживаться — это штатное поведение. А вот триггер, отключённый вручную (mass update/API), рекурсивно обнаруженным заново не включится: ручное отключение имеет приоритет и переживает передискаверинг.
- Имя интерфейса в {#IFNAME} у разных вендоров приходит по-разному — где-то полное «GigabitEthernet0/1», где-то сокращённое «Gi0/1» или «eth0». Контекстный макрос {$IFCONTROL:"..."} привязан к точной строке имени, поэтому единый шаблон override под парк из разных производителей коммутаторов не работает — тестируем условие фильтра на каждом типе устройства отдельно.
- Переименование порта после смены прошивки или пересборки конфигурации устройства меняет значение {#IFNAME} — ранее настроенный контекст макроса {$IFCONTROL} для старого имени «отваливается», и порт снова начинает алертить как вновь обнаруженный, пока макрос не переустановят на новое имя.
- Delete lost resources: Immediately на item-прототипах выглядит заманчиво — «пусть база не пухнет от мёртвых портов», — но на нестабильных линках (модемные аплинки, беспроводные мосты между зданиями) интерфейс может регулярно пропадать из ответа SNMP на один цикл опроса из-за таймаута, а не потому что реально исчез. При Immediately это означает регулярную потерю истории по трафику и её пересборку с нуля — заметили это уже после того, как понадобился разбор инцидента месячной давности, а графика за нужный период не оказалось.
Чек-лист внедрения
Порядок действий, которым мы закрываем подобный тикет у клиента за одну сессию.
- Снимаем текущий шторм: Data collection → Hosts → Triggers → фильтр «Link down» → Mass update → Disable (или пакетно через trigger.update по API для больших парков).
- Определяем, какие порты декоративные системно (патч-панель, резерв, административно down), и описываем их регуляркой по {#IFNAME}/{#IFALIAS} — раздельно под каждый вендор.
- Создаём override с этим условием, Stop processing включён, операция на Trigger prototype «Link down»: Create enabled: No (и Discover: No, если не нужны даже item/graph по этим портам).
- Проверяем на самом LLD-правиле, что Disable lost resources стоит на After с разумным запасом, а Delete lost resources — на Never или очень длительный After, чтобы не терять историю по интерфейсам.
- Для единичных исключений (конкретный порт на конкретном устройстве) используем не override, а контекстный макрос {$IFCONTROL:"{#IFNAME}"} = 0 прямо на хосте — эффект мгновенный, без ожидания цикла discovery.
- Через один-два цикла Update interval правила проверяем, что новых Link down по декоративным портам больше не появляется, а история по item сохранена.
Частые вопросы
- Чем Create enabled в override отличается от статуса самого триггер-прототипа в шаблоне?
- Статус в самом прототипе — это значение по умолчанию для всех низкоуровневых объектов, которых не коснулось ни одно override. Create enabled внутри override — точечная подмена этого значения, но только для объектов, подпадающих под условие фильтра override, и только в момент их первого создания. Оба поля не трогают уже существующие созданные сущности.
- Нужно ли перезапускать zabbix-server после правки override или контекстного макроса?
- Нет. Изменения override вступают в силу на следующем цикле опроса LLD-правила (по его Update interval), а изменение контекстного макроса {$IFCONTROL} — на следующем вычислении соответствующего триггера, обычно в пределах интервала обновления item, без перезапуска службы.
- Что произойдёт с историей item, если всё же выставить Delete lost resources: Immediately?
- Сущность (item, а вместе с ней история и тренды) будет удалена сразу после того, как объект перестанет обнаруживаться LLD-правилом. Официальная документация прямо предупреждает, что это рискованно: ошибка в условии фильтра или override может привести к безвозвратной потере истории по объектам, которые на самом деле продолжают существовать.
- Как отличить триггер, унаследованный от старого обнаружения, от пересозданного заново?
- Прямого признака «старый/новый» в UI нет, но можно ориентироваться на поле Discovered in (связь с LLD-правилом и датой последнего обнаружения) в списке триггеров, а также на историю событий по конкретному triggerid — если запись стабильно фигурирует до момента правки override, она относится к объекту, обнаруженному раньше правки.
- Можно ли применить override только к одному хосту, а не ко всему шаблону?
- Да, если LLD-правило определено на уровне хоста (а не унаследовано жёстко без возможности расширения), override можно создать локально для этого хоста в Data collection → Hosts → Discovery конкретного хоста — тогда действие ограничится только им, остальные хосты по шаблону не затронутся.