Погодин Евгений — ИТ Заметки
📈Мониторинг Обновлено 10.11.2026

Из практики админа 08. Мониторинг принтеров по SNMP: тонер и расходники в Zabbix

Note

Материал сверен с официальной документацией (RFC и документация Zabbix 7.0; ссылки — в конце статьи). Статья основана на реальном случае, описанном обезличенно: названия, адреса и значения изменены. Примеры вывода — иллюстрации. Перед работой на боевых системах проверьте шаги на тестовой машине.

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

Агента Zabbix на принтер не поставить, но принтеры — типичные устройства для SNMP-мониторинга: так сказано и в документации Zabbix. Подходящего готового шаблона в используемой версии мы не нашли и написали свой. В статье разберём его по шагам и покажем места, от которых пришлось защититься: отрицательные «служебные» значения уровня, процент с делением на ноль, правки, которые не сразу доходят до созданных элементов, маршруты до подсетей принтеров в других офисах. Отдельно — про безопасность: community в SNMP — это пароль, который передаётся без защиты.

В примерах сервер Zabbix 7.0 — lnx1 (192.168.10.20, установка — в статье Zabbix с нуля). Принтеры главного офиса — PRN-ACC1 (192.168.10.61) и PRN-HR1 (192.168.10.62), удалённого офиса — PRN-REM1 (198.51.100.61). Все они в группе узлов «Принтеры» (Printers). Названия шаблона, макросов и ключей условные.

Что нужно знать заранее

  • SNMP (Simple Network Management Protocol) — протокол, по которому система мониторинга спрашивает у устройства его показатели. На устройстве работает SNMP-агент, спрашивает Zabbix. Версии 1 и 2c защищены только строкой community (сообщество), версия 3 добавляет проверку подлинности и шифрование. Проверки SNMP в Zabbix идут только по UDP (User Datagram Protocol — протокол без установления соединения): ответ на ping или удачная TCP-проверка ничего не говорят о том, отвечает ли SNMP-агент.
  • OID (Object Identifier) — числовой «адрес» показателя в дереве устройства. MIB (Management Information Base) — описание этого дерева: какие бывают показатели и что они значат.
  • Printer MIB — стандартная MIB принтеров, описана в RFC 3805. Расходники лежат в таблице prtMarkerSuppliesTable (ветка 1.3.6.1.2.1.43.11.1): одна строка — один расходник (тонер, барабан, контейнер отработки). Нам нужны три столбца:
Столбец Что в нём OID столбца
prtMarkerSuppliesDescription текстовое описание расходника 1.3.6.1.2.1.43.11.1.1.6
prtMarkerSuppliesMaxCapacity наибольшая ёмкость в единицах измерения расходника 1.3.6.1.2.1.43.11.1.1.8
prtMarkerSuppliesLevel текущий уровень в тех же единицах 1.3.6.1.2.1.43.11.1.1.9
  • Как устроен индекс. К OID столбца добавляют индекс строки. По RFC 3805 он состоит из двух частей: индекс устройства (hrDeviceIndex) и индекс расходника. У наших принтеров индекс устройства был равен 1 (практика автора; у вашего устройства он может быть другим), поэтому уровень второго расходника лежит в 1.3.6.1.2.1.43.11.1.1.9.1.2.
  • Служебные значения. Уровень и максимум не всегда обычные неотрицательные числа. Значение −1 означает «другое» и говорит, что ограничений нет; −2 — «неизвестно»; для уровня есть ещё −3 — принтер знает, что некоторое количество расходника есть (для ёмкости — некоторое свободное место), но сколько, не знает. Для ёмкости, которая наполняется (контейнер отработки), уровень — это оставшееся место, а не заполненность; поэтому малый «уровень» тревожен и у тонера, и у контейнера отработки. Описание расходника выдаётся в локализации, выбранной на устройстве, поэтому годится как подпись, а не как условие.
  • LLD (low-level discovery, низкоуровневое обнаружение) — механизм Zabbix: он обходит таблицу и по прототипам создаёт элементы данных и триггеры для каждой найденной строки. Номер строки хранится во встроенном макросе {#SNMPINDEX}, а текст описания мы положим в свой макрос {#SUPPLYDESC}.
  • Вычисляемый элемент (calculated item) — элемент, значение которого считается по формуле из других элементов. Пользовательские макросы ({$ИМЯ}) подставляются с приоритетом: сначала узел, затем шаблоны, затем глобальные.
  • Среда. Zabbix 7.0; пункты меню даны по английскому интерфейсу. Обнаружение мы строили через discovery[] — так было в нашем случае. В документации 7.0 этот способ назван устаревшим (legacy); для нового шаблона предпочтительнее walk[] с зависимыми элементами (подробнее — во врезке «Состояние на ноябрь 2026 года»).

Симптомы

  • Нужно было заранее видеть остатки, а не узнавать о конце тонера из заявки «принтер не печатает».
  • В первой версии шаблона часть элементов с уровнем у некоторых принтеров переходила в состояние «Not supported» (не поддерживается).
  • У принтеров других офисов данных не было совсем: сервер мониторинга находился в другой сети, а маршрутов до подсетей принтеров через каналы между офисами не было.
  • Когда триггеры заработали, они были видны только в интерфейсе Zabbix: писем и сообщений не приходило.

Как искали решение

Шаг 1. Что принтер отвечает. Начинают не с Zabbix, а с запроса к устройству. С lnx1 (утилита snmpwalk входит в net-snmp; если команды нет, установите утилиты net-snmp):

snmpwalk -v 2c -c '<community>' -On 192.168.10.61 1.3.6.1.2.1.43.11.1.1.6
snmpwalk -v 2c -c '<community>' -On 192.168.10.61 1.3.6.1.2.1.43.11.1.1.8
snmpwalk -v 2c -c '<community>' -On 192.168.10.61 1.3.6.1.2.1.43.11.1.1.9

<community> — условная строка: подставьте свою и помните, что в командной строке она попадает в историю оболочки и список процессов (практика автора).

.1.3.6.1.2.1.43.11.1.1.6.1.1 = STRING: "Black Toner"
.1.3.6.1.2.1.43.11.1.1.6.1.2 = STRING: "Imaging Drum"
.1.3.6.1.2.1.43.11.1.1.6.1.3 = STRING: "Waste Container"
.1.3.6.1.2.1.43.11.1.1.8.1.1 = INTEGER: 100
.1.3.6.1.2.1.43.11.1.1.8.1.2 = INTEGER: 100
.1.3.6.1.2.1.43.11.1.1.8.1.3 = INTEGER: -2
.1.3.6.1.2.1.43.11.1.1.9.1.1 = INTEGER: 4
.1.3.6.1.2.1.43.11.1.1.9.1.2 = INTEGER: 87
.1.3.6.1.2.1.43.11.1.1.9.1.3 = INTEGER: -3

Пример вывода (вымышленный). Из него видно три вещи. Расходников у разных принтеров разное число и состав, значит, список нужно искать автоматически. Хвост OID (.1, .2, .3) связывает описание, максимум и уровень одного расходника. И встречаются отрицательные значения: здесь у контейнера максимум −2, а уровень −3.

Шаг 2. Схема шаблона. Правило обнаружения обходит столбец описаний и находит все расходники. Для каждого расходника создаются три элемента: уровень, максимум и вычисляемый процент уровень × 100 / максимум. На процент вешается триггер.

Шаг 3. Тип данных. Zabbix описывает Numeric (unsigned) как 64-битное целое без знака, а Numeric (float) — как число с плавающей точкой, допускающее отрицательные значения. Отрицательное −3 в беззнаковый тип не поместится, поэтому уровень и максимум мы храним как Numeric (float).

Шаг 4. Доступность. Сервер мониторинга находился в другой сети, чем часть принтеров. Пока до подсетей принтеров не было маршрутов через каналы между офисами, SNMP молчал. Проверять нужно самим snmpwalk с lnx1. Если запрос с lnx1 проходит, а в Zabbix SNMP-интерфейс узла помечен недоступным, проверьте адрес, версию и community в настройках интерфейса узла.

Что не помогло

Целочисленный тип. Для «числа от 0 до N» естественно целое. На принтерах, которые возвращают −3 или −2, такой элемент уходит в «Not supported» (практика автора). Вычисляемому элементу с типом Numeric (unsigned) тоже нельзя: Zabbix усекает дробный результат такого элемента до целого.

Процент «в лоб». Формула уровень × 100 / максимум без защиты хрупка. При максимуме 0 получается деление на ноль, и вычисляемый элемент становится неподдерживаемым. Допустим, у расходника максимум −2, а уровень −3: формула выдаст 150 %; при максимуме −2 и неотрицательном уровне процент окажется отрицательным. Значит, нужна защита в условии триггера: максимум больше нуля, уровень неотрицателен.

Правка прототипа без ожидания. Мы меняли тип данных, OID и имя триггера в прототипах, а уже созданные элементы оставались прежними: изменения доезжают до них при следующем обнаружении, а интервал у нас был около часа (практика автора). Открытые проблемы при этом не переименовываются, меняются только новые. Помогла принудительная проверка, о ней ниже.

Ожидание писем без Action. Триггер сам по себе только показывает проблему в интерфейсе. Чтобы получить письмо или сообщение, нужны действие (Action) и тип оповещения (media type): порядок описан в статье про Zabbix.

Что сработало

Рабочий шаблон назван «Printer Supplies Lab» (название условное). Шаги 1–4 выполняются в Data collection → Templates, пока шаблон не привязан к узлам.

Warning

Шаблон, привязанный ко многим узлам, меняют осторожно: ошибка в прототипе размножится на все принтеры. Перед правкой экспортируйте шаблон в файл (практика автора: кнопка Export в списке шаблонов) и сначала проверьте изменения на одном узле или на клоне шаблона. Откат — импорт сохранённого файла; при включённом «Delete missing» макросы, которых нет в файле, будут удалены, а как поведут себя добавленные позже прототипы, проверьте по правилам импорта своей версии. Либо отвяжите шаблон от узла. Значения макросов типа Secret text при экспорте не выгружаются, их придётся ввести заново.

Шаг 1. Макросы шаблона (вкладка Macros): {$SNMP_COMMUNITY} — тип Secret text: в интерфейсе значение скрыто звёздочками, а в поле SNMP community подставляется настоящее; задавайте его на узлах или глобально, а не вписывайте в шаблон; {$PRN.SUPPLY.WARN} — порог в процентах (допустим, 5).

Шаг 2. Правило обнаружения. Тип SNMP agent, ключ любой уникальный (например printer.supplies.discovery), поле SNMP OID:

discovery[{#SUPPLYDESC},1.3.6.1.2.1.43.11.1.1.6.1]

Правило обходит столбец описаний, для каждой найденной строки задаёт {#SUPPLYDESC} (текст) и {#SNMPINDEX} (номер расходника). Последняя единица в OID правила — индекс устройства (hrDeviceIndex), см. выше. Интервал обнаружения — час: список расходников меняется редко. Для медленно меняющихся объектов (на примере файловых систем) документация Zabbix советует после отладки ставить 30 минут и больше.

Шаг 3. Прототипы элементов. Ключи, тип данных и OID:

Элемент Тип Ключ OID / формула Тип данных
Уровень SNMP agent printer.supply.level[{#SNMPINDEX}] 1.3.6.1.2.1.43.11.1.1.9.1.{#SNMPINDEX} Numeric (float)
Максимум SNMP agent printer.supply.max[{#SNMPINDEX}] 1.3.6.1.2.1.43.11.1.1.8.1.{#SNMPINDEX} Numeric (float)
Процент Calculated printer.supply.pct[{#SNMPINDEX}] см. ниже Numeric (float), единицы %

Формула процента; хост в ссылках опущен (//), как разрешено для вычисляемых элементов. Она разбита на две строки только ради ширины страницы, в поле её можно записать одной строкой:

last(//printer.supply.level[{#SNMPINDEX}])
    *100/last(//printer.supply.max[{#SNMPINDEX}])

В названиях прототипов используйте {#SUPPLYDESC}, чтобы в списке было видно «Black Toner: уровень». Интервал опроса расходников — допустим, 30 минут: они меняются медленно.

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

last(/Printer Supplies Lab/printer.supply.pct[{#SNMPINDEX}])
    <{$PRN.SUPPLY.WARN}
and last(/Printer Supplies Lab/printer.supply.max[{#SNMPINDEX}])>0
and last(/Printer Supplies Lab/printer.supply.level[{#SNMPINDEX}])>=0

Имя триггера: {HOST.NAME}: мало расходника «{#SUPPLYDESC}», осталось {ITEM.LASTVALUE1}. Макрос с номером 1 берёт значение первого элемента выражения, то есть процента. Знак «%» в конце имени не дописывайте: у элемента заданы единицы %, и Zabbix, вероятно, подставит значение вместе с ними (вывод из исходного кода Zabbix 7.0, не из документации; проверьте на тестовом триггере, практика автора). Макрос порога подставляется, потому что стоит на месте константы; and пишется строчными и отделяется пробелами. Отрицательный уровень или нулевой максимум делают одно из условий ложным, и триггер не срабатывает. Ограничение: столбец prtMarkerSuppliesSupplyUnit (1.3.6.1.2.1.43.11.1.1.7) шаблон не использует; если единица — проценты, а максимум −2, сам уровень осмыслен, но триггер молчит.

Шаг 5. Узлы. Группа «Принтеры», интерфейс SNMP версии 2 (v2c), поле community — {$SNMP_COMMUNITY}. Привяжите шаблон. На отдельном принтере с другим community определите макрос с тем же именем на самом узле: узел перебивает шаблон. Так же переопределяется и порог {$PRN.SUPPLY.WARN} для принтера, у которого расходники нужно менять раньше. Если в журнале Zabbix есть ошибки вида «does not contain all of the requested variable bindings», отключите для узла «Use combined requests»; если обход таблицы упирается в тайм-аут, уменьшите Max repetitions в интерфейсе.

Шаг 6. Принудительное обнаружение. После правок не ждите часа: на узле откройте Discovery, отметьте правило и нажмите Execute now. Сначала подождите, пока Zabbix подхватит изменения конфигурации: немедленный запуск не обновляет кэш конфигурации, и результат может не учитывать свежие правки. То же делает вызов API task.create с типом 6 («выполнить сейчас») для идентификатора правила на узле. Тело запроса сохраните в файл task.json:

{"jsonrpc": "2.0", "method": "task.create", "id": 1,
 "params": [{"type": 6, "request": {"itemid": "<ID_ПРАВИЛА>"}}]}
curl --request POST \
  --url 'https://lnx1.lab.example/zabbix/api_jsonrpc.php' \
  --header "Authorization: Bearer $(cat ~/.zbx-token)" \
  --header 'Content-Type: application/json-rpc' \
  --data @task.json

Токен передаётся в заголовке Authorization: только по HTTPS, иначе его увидит любой в сети. Его создают в Users → API tokens для пользователя с минимально нужными правами и держат в файле ~/.zbx-token с правами 600, а не вписывают в команду; на время выполнения curl значение всё равно видно в списке процессов этого сервера (практика автора). Идентификатор правила виден в адресной строке при его открытии (параметр itemid; практика автора).

Шаг 7. Оповещения. Создайте Action и тип оповещения — порядок в статье про Zabbix. Начните с одного получателя и триггеров уровня Warning, чтобы не завалить почту.

Безопасность: community — это пароль

Warning

Community в версиях 1 и 2c — единственная «защита», и передаётся она открытым текстом: SNMPv1 и SNMPv2c поддерживают лишь тривиальную проверку по строкам community и поэтому принципиально небезопасны (RFC 3410, раздел 8.2). Любой, кто видит трафик SNMP, видит и community. Не публикуйте её в статьях, заявках и на скриншотах.

Что делать (практика автора, если не сказано иное):

  • Не оставляйте public: в документации Zabbix он назван обычным значением, его знает каждый.
  • Ограничьте источники запросов: на принтере или межсетевом экране между сегментами пропускайте SNMP только с адреса сервера мониторинга. Наружу, в интернет, SNMP не публикуется.
  • Используйте community только для чтения: для мониторинга запись не нужна, а в Printer MIB значения уровня и ёмкости расходника, по RFC 3805, могут быть доступны на запись.
  • Храните community в макросе типа Secret text: после сохранения значение в интерфейсе уже не показывается.
  • Если устройство поддерживает SNMPv3, предпочитайте его: уровень AuthPriv включает проверку подлинности и шифрование; настройки задаются в интерфейсе узла.
  • Прежде чем менять community на принтере, выясните, кто ещё обращается к нему по SNMP: серверы печати и утилиты управления могут перестать видеть статус.

Счётчик страниц: наметка

Этот раздел не из нашего случая, а наметка по тому же принципу: рабочего рецепта здесь нет. В Printer MIB есть счётчик prtMarkerLifeCount (1.3.6.1.2.1.43.10.2.1.4): это число единиц, сосчитанных за всё время жизни устройства; единица задана соседним столбцом prtMarkerCounterUnit (например, листы или оттиски), а хранить значение рекомендуется постоянно. Индекс — тоже пара «устройство, маркер»: проверьте его snmpwalk, прежде чем заводить элемент. Это 32-разрядный неотрицательный счётчик (Counter32), поэтому подойдёт Numeric (unsigned). Он накопительный: на графике будет растущая линия, а скорость печати даёт расчёт изменения за период (предобработка Zabbix; в статье не разбирается).

Выводы и чек-лист

  • Сначала snmpwalk с сервера мониторинга, потом шаблон: так видно, какие значения отдаёт принтер на самом деле.
  • Уровень и максимум храните как Numeric (float): Printer MIB допускает отрицательные служебные значения.
  • Процент защищайте условиями триггера: максимум больше нуля, уровень неотрицателен.
  • Порог выносите в макрос со значением по умолчанию и переопределяйте на узле.
  • После правки прототипов запускайте обнаружение принудительно (Execute now или task.create с типом 6).
  • Убедитесь, что до подсетей принтеров есть маршруты; проверяйте SNMP, а не ping.
  • Подключите оповещения (Action и media type).
  • Community — пароль: ограничьте источники, используйте только чтение, не оставляйте public, не публикуйте.

Состояние на ноябрь 2026 года (проверьте актуальность). Статья написана по документации Zabbix 7.0; на сайте документации в списке версий текущей помечена 8.0, поэтому пункты меню и готовые шаблоны в вашей версии могут отличаться: перед созданием своего шаблона просмотрите стандартные. Ветка 7.0 остаётся в списке поддерживаемых; способ discovery[] отмечен как устаревший (legacy) и в документации 7.0, и в документации 8.0. Способ на основе walk[] и предобработки «SNMP walk to JSON» поддерживается с версии 6.4 и делает один SNMP-обход за цикл. Вызов task.create в 7.0 доступен пользователям типов Admin и User начиная с 7.0.19; права на него настраиваются в ролях пользователей.

Типичные ошибки и недоразумения

  • Принимают −3 за 0 % или за ошибку. Это «некоторое количество есть, сколько — неизвестно»; триггер на такие значения не должен срабатывать.
  • Ставят целочисленный тип. Элементы уходят в «Not supported», как только принтер вернёт отрицательное число.
  • Забывают про ёмкости, которые наполняются. У контейнера отработки уровень — оставшееся место.
  • Парсят текст описания. Он выдаётся на языке устройства; ключом служит индекс, а не слова.
  • Видят в Zabbix шестнадцатеричные значения (HEX) вместо текста. Документация Zabbix рекомендует установить MIB-файлы на сервер: без них встречается показ в HEX вместо UTF-8 и наоборот.
  • Ждут, что правка прототипа сразу изменит старые элементы и проблемы. Для этого нужно обнаружение, а уже открытые проблемы остаются под старым именем.
  • Проверяют доступность ping-ом. SNMP идёт по UDP; нужен snmpwalk.
  • Отправляют токен API по http://. Токен — секрет: только HTTPS.
  • Оставляют public и права на запись. Community — пароль без шифрования.

Проверьте себя

  1. Почему уровень и максимум расходника хранят как число с плавающей точкой, а не как целое без знака? По RFC 3805 они могут быть отрицательными (−1, −2, −3), а Numeric (unsigned) — 64-битное беззнаковое целое: такой элемент станет «Not supported». Типа Numeric (float) достаточно.
  2. Что означают −2 и −3 у уровня расходника и почему триггер на них не должен срабатывать? −2 — «неизвестно», −3 — «есть некоторое количество (или свободное место), но сколько, неизвестно». Это не нуль, а отсутствие измерения, поэтому в условие добавлено «уровень ≥ 0».
  3. Как работает макрос {$SNMP_COMMUNITY} и как задать другое значение для одного принтера? Он стоит в поле community интерфейса; значение берётся по приоритету «узел → шаблоны → глобальные», поэтому на узле достаточно определить макрос с тем же именем.
  4. Как заставить Zabbix сразу применить изменённый прототип? Запустить правило обнаружения принудительно: Execute now на узле или task.create с типом 6 для идентификатора правила; сначала нужно дать Zabbix обновить конфигурацию.
  5. Почему community в SNMP v2c нельзя считать надёжным паролем? Она защищает лишь строкой community, переданной открытым текстом, и поэтому схема принципиально небезопасна; защиту даёт SNMPv3 и ограничение источников запросов.

Что дальше

Источники

Ещё по теме «Мониторинг»