Применение Victoriametrics для мониторинга по SNMP
Заметка написана специально для коммунити VictoriaMetrics_ru1 в телеграм.
Выбор tsdb
Развитие облачных сервисов, хайп вокруг kubernetes привели к тому, что с 2017 года популярность стали набирать базы данных временных рядов (time series databases). В таких базах хранят метрики (metrics) приложений, а специально разработанные языки запросов для этих баз позволяют проводить анализ данных, строить графики, информировать об отклонениях от заданных пороговых значений и т.д. Prometheus становится решением по умолчанию для мониторинга kubernetes и все больше разработчиков начинают дописывать свои приложения, чтобы те могли по протоколу http отдавать свои метрики в в формате prometheus:
myapp_status{server="prod1"} 1
Не всегда приложение или какое-то устройство может отдать метрики в нужном формате. Начинается разработка специализированных приложений для интеграции с prometheus, получивших общее название exporter. Список exporter'ов ничем не ограничен. Надо только согласовать с community номер порта, на котором ваш exporter будет принимать запросы. Есть и более простой способ передать метрики без написания exporter'а. Разработчики node_exporter предусмотрели для этого textfile collector. Теперь достаточно скопировать файл с готовыми метриками в определенный каталог.
С заданным интервалом (scrape interval) Prometheus опрашивает приложения, exporter'ы и записывает метрики с базу. При всех достоинствах этого решения, конечно, были ограничения:
- ограниченное время хранения метрик;
- отсутствие горизонтального масштабирования;
- и другие ограничения пулл модели.
Первое ограничение обходится применением удаленных хранилищ, т.н. remote storage. Такой базой данных временных рядов с возможностью длительного хранения данных является VictoriaMetrics (далее VM). Ее разработка началась в 2018 году, а в мае 2019 года появляется open-source версия на github. VM начинают выбирать за быстроту, производительность, простоту установки и возможности горизонтального масштабирования в кластерной версии. Экономия на ресурсах, поддержка языка запросов PromQl и наличие новых востребованных функций в MetricsQl приводит к росту ее популярности. Вокруг VM начинает развиваться экосистема, разрабатываются дополнительные приложения, позволяющие исключить prometheus, как компонент, полностью.
telegraf vs vmagent+snmp_exporter
Протокол snmp все еще активно используется для мониторинга параметров работы телекоммуникационного оборудования, например:
- счетчики объемов переданных данных через интерфейс в байтах, пакетах;
- счетчики ошибок и потерь на интерфейсах;
- состояние сенсоров температуры, питания, вентиляторов;
- счетчики утизизации ресуров, таких как память, cpu;
- состояние сессий протоколов маршрутизации;
- различные счетчики политик, acl.
Ниже будет сравнение telegraf c плагином snmp (T) и vmagent + snmp_exporter (VS). Я работал с обоими, telegraf не использую более года. Не будет сравнения с другим ПО: zabbix, cacti, librenms.
vmagent периодически опрашивает через snmp_exporter устройства по протоколу snmp и записывает полученные метрики в базу VictoriaMetrics. После 0.20.0 заявлены изменения и snmp_exporter возможно пеередет github.com/prometheus-community/snmp
service discovery
Удобно, когда хосты для мониторинга можно добавить на лету и нет необходимости править конфигурацию, релоадить сервис.
T: Нет service discovery. Необходимо после изменения списка агентов делать reload telegraf'у.
VS: Есть service discovery и file_sd в частности. Для каждого модуля можно создавать файл со списком хостов средствами автоматизации.
Статус опрашиваемых устройств
Если устройство недоступно, то это надо видеть как-то и не только по отсутствию метрик в базе.
T: Статус опрашиваемых устройств определить нельзя. Можно посмотреть логи об ошибках.
VS: Есть ручка curl http://vmagent-host:8429/targets
, метрики и официальный dashboard для них с кол-вом targets up/down.
Буфер для опрошенных метрик
При отсуствии связи между агентом и базой метрики будут кешироваться на хосте агента.
T: metric_buffer_limit в настройках telegraf определяет размер кэша для хранения метрик при опросе устройств При недоступности базы метрики хранятся в кэше (оперативной памяти) и при восстановлении связи записываются. При перезапуске telegraf'а, например из-за изменения адреса базы, данные будут потеряны.
VS: remoteWrite.tmpDataPath у vmagent определяет каталог для хранения данных при недоступности базы.
Данные дописываются при восстановлении доступа к базе, каталог очищается, а метрика vmagent_remotewrite_pending_data_bytes
будет с какого-то числа уменьшаться до нуля.
snmp mib'ы
Обычно на хост, с которого осуществляется опрос, требуется установить snmp, все mib'ы с зависимостями. В случае snmp_exporter'а, чей конфиг с oid'ами генерится на отдельном хосте, mib'ы нужны только на рабочем месте или виртуальной машине администратора, работаюшего с генератором.
T: На хосте, который будет мониторинть сетевые устройства устанавливается snmp, mib'ы, telegraf. В конфигурации можно использовать oid без мибов в случае inputs.snmp.field. Необходимы mib'ы на хосте, т.к плагин выполняет snmptranslate при старте и делает проверку oid для таблиц inputs.snmp.table что это действительно таблица.
VS: На хост устанавливается snmp_exporter c конфигурационным файлом snmp.yml, vmagent. Устанавливать snmp, mib'ы не нужно.
Отдельно добавлю про генератор, т.к. он является частью подготовительных работ по настройке. Создать конфигурацию без миба не получится, потому что все индексы - это лейблы.
интервал опроса
interval или scrape interval. Считается хорошей практикой хранить метрики в базе с одним интервалом. Интервал 1 минута хорош для алертинга, а интервал в 5 минут является достаточным для хранения исторических данных и подходит для опроса устройств с медленным control-plane, которые не могут на минуту отдать свои таблицы, либо устройства расположены на каналами с высоким RTT. Во втором случае следует определиться с масштабированием и выносом агента поближе к опрашиваемым устройствам. Необходимо понимать, что устройства отвечают по snmp медленно и необходим компромис между количеством опрашиваемых данных и интервалом опроса.
T: Интервал опроса указывается в каждом конфигурационном файле, либо глобально. Имеет смысл делать множество файлов, т.к. если плагин не успеет завершить работу в указанный интервал, то данные обо всех устройствах будут потеряны. Когда начинается какая-то проблема, то потеря данных мониторинга делает бессмысленным мониторинг.
VS: Рекомендуется один интервал для всех метрик. 1 минута - интервал опроса всех устройств по snmp. Недоступность устройства по snmp или длительное время опроса, превышающее интервал опроса данных не влияет на получения результатов опроса от других устройств.
конфигурационный файл
T: Казалось бы, что настраивать все плагины в одном файле удобно, но из-за ограничений плагина при таймауте интервала их нужно много. Для каждого вендора или модели оборудования или/и региона опроса создается отдельный конфигурационный файл для snmp плагина в /etc/telegraf/telegraf.d/.
VS: У snmp_exporter один конфигурационный файл, в котором для каждого вендора или модели оборудования описывается модуль. Этот конфигурационный файл может быть универсальным для всех устройств на сети и копироваться на все хосты с exporter'ами. В vmagent'е для каждого модуля свой job или иная реализация. Для каждого хоста в регионах будут свои таргеты из service discovery.
кардинальность
При использовании influx об кардинальности вообще не задумываются до тех пор, пока база как-то работает. Это простота использования обманчива и расплачиваться приходится медленной работой базы. В случае БД prometheus уже тот факт что в базу нельзя писать текстовые значения, заставляет смотреть что вообще пишется.
T: Добавление тегов влияет на производительность, но рекомендуется для индексации и поиска. Запишет все полученные значения (value) текст, числа, неотфильтрованный мусор в базу influxdb как одну метрику (measurement) с множеством значений и индексов (tag). Есть мибы, например от polycom, где числа описаны как строки. Такое значение можно преобразовать через conversion = "int". Лишние индексы в oid можно ограничить через oid_index_length в inputs.snmp.table.field. Можно настроить фильтрацию через fieldpass, tagexclude, inputs.snmp.tagdrop в конфигурации плагина.
VS: Метрика с другим набором лейблов - это другой временной ряд. В victoria-metrics и prometheus можно записать только числовые значения, а текст только в индексы/лейблы. Одной метрике будет соотвествовать одно значение с набором лейблов. snmp_exporter умеет преобразовывать string через overrides. Ниже будет пример в конфигураторе с коммутатором extreme, где cpu usage в oid описана как string и по дефолту запишется в label. В конфигурации vmagent'а необходимо настроить labeldrop в metric_relabel_configs. Есть drop_source_indexes в lookup и ignore overrides в snmp_exporter.
GUI для запросов
Для новичков низкий порог входа в язык запросов influx может показаться удобным. Здесь медвежью услугу оказала grafana.
T: В grafana есть конструктор запросов для influxdb, где в качестве условий указываются теги.
VS: Необходимо знать функции promql для составления запросов в базу, указывать в запросе специфичные для метрики лейблы.
rate
Для построения исторических данных в виде графиков загрузки интерфейса используются метрики с 64-битными счетчиками, которые растут, либо сбрасываются в 0. Чтобы определить скорость изменения, необходимо получить из базы несколько точек за указанный интервал.
T: Функции derivative() или non_negative_derivative() испольуются в grafana для отображения графиков загрузки интерфейсов. Интервал для использования в функциях может совпадать с интервалом опроса устройств.
VS: Функции rate() и increase() для счетчиков (counters) 32 и 64 битных. В базе prometheus для rate() необходимо 2.3 точки на интервале. Т.е. при опросе раз в минуту можно задать rate(metric[5m]), но не rate(metric[1m]). В victoria-metrics такого ограничения нет.
telegraf
Ниже будет текст про предыдущий опыт эксплуатации telegraf'а с influxdb. В influxdb можно писать текст, а это уже совсем не про хранение метрик. Еще я пробовал плагин сlickhouse и писал туда дескрипшены интерфейсов, а счетчики iftable писал в VM. Рекомендую пропустить этот абзац для экономии времени.
Информацию по протоколу snmp можно получать разную: счетчики интерфейсов, статус sla, описание интерфейсов, состав оборудования из entity-mib, счетчики политик qos. Все это можно опрашивать с разными интервалами, например, данные по sla нужны раз в минуту, счетчики интерфейсов и политик раз в пять минут, инвентаризацию раз в сутки. Разные интервалы подразумевают разные конфигурационные файлы для плагина snmp, т.к. интервал указывается для плагина, а не для job'а, как в prometheus/vmagent. Опрашивать все с одним интервалом не получится, т.к. нет необходимости хранить одни и те же данные, например, строковые значения инвертацизации, описаний интерфейсов, ip-адреса, arp'ы и т.п. Еще опрос такого количества таблиц занимает приличное время и в минутный интервал просто не успеть. Это легко проверить, запустив в тестовом режиме telegraf.
$ telegraf -config test.cfg -test
- Если какой-то field будет использоваться как tag, то имя в конфиге будет отличаться. Это связано с использованием ранних версий influxdb, где с наименованием в самой базе случалась каная-то каша. Так появлялись ifName и ifName_1 и я уже не помню что из этого что. Опять же в запросах меньше путаницы будет, когда ifName - это field, а Name - это tag. Позже стал использовать fieldpass , в котором не указывал все field, используемые в качестве tag.
- Вместо имени oid'а используется сам oid, а его имя для удобства указано в комментарии выше. Старая версия telegraf'а при старте запускала в shell'е snmptranslate и чем больше было конфигурационных файлов, тем выше была загрузка cpu. Рестарт был очень неудобен и позже алгоритм немного поменяли на однократный запуск snmptranslate. Все таблицы все равно проверяются в мибах и опросить таблицу совсем без миба не получится.
Пример для cisco ip sla, где не опрашивается вся таблица rttMonLatestRttOperTable (закоменнирован oid), а только два значения из нее. Тэг для замены индекса берется из другой таблицы rttMonCtrlAdmintable. На оборудовании cisco настроен следующий sla:
ip sla 1
icmp-echo 10.4.3.1 source-ip 10.4.3.2
tos 96
threshold 200
owner IPSName-vlanid@AddrA-AddrB
ip sla schedule 1 life forever start-time now
[[inputs.snmp]]
interval = "1m"
agents = [ "cisco" ]
version = 2
community = "public"
#CISCO-RTTMON-MIB::rttMonCtrlAdminOwner.5 = STRING: "IPSName-vlanid@AddrA-AddrB"
[[inputs.snmp.table]]
name = "rttsla"
#oid = "CISCO-RTTMON-MIB::rttMonLatestRttOperTable"
[[inputs.snmp.table.field]]
name = "Descr"
#oid = "CISCO-RTTMON-MIB::rttMonCtrlAdminOwner"
oid = "1.3.6.1.4.1.9.9.42.1.2.1.1.2"
is_tag = true
[[inputs.snmp.table.field]]
name = "icmpResults"
#oid = "CISCO-RTTMON-MIB::rttMonLatestRttOperSense"
oid = "1.3.6.1.4.1.9.9.42.1.2.10.1.2"
[[inputs.snmp.table.field]]
name = "rttAvg"
#oid = "CISCO-RTTMON-MIB::rttMonLatestRttOperCompletionTime"
oid = "1.3.6.1.4.1.9.9.42.1.2.10.1.1"
[inputs.snmp.tagpass]
Descr = [ "*@*" ]
[[inputs.snmp]]
interval = "1m"
agents = [ "huawei" ]
version = 2
community = "public"
# for huawei s5720-ei
#NQA-MIB::nqaAdminCtrlTag."1"."1" = STRING: IPSName-vlanid@AddrA-AddrB
#.1.3.6.1.4.1.2011.5.25.111.2.1.1.3.1.49.1.49 = STRING: IPSName-vlanid@AddrA-AddrB
#index is 1.49.1.49. oid_index_length = 4
#NQA-MIB::nqaResultsCompletions."1"."1".186177.1 = INTEGER: success(1)
#s5720-ei has a history index
[[inputs.snmp.table]]
name = "rttsla"
#oid = "NQA-MIB::nqaAdminCtrlTable"
[[inputs.snmp.table.field]]
name = "Descr"
#oid = "NQA-MIB::nqaAdminCtrlTag"
oid = "1.3.6.1.4.1.2011.5.25.111.2.1.1.3"
is_tag = true
[[inputs.snmp.table.field]]
name = "icmpResults"
#NQA-MIB::nqaResultsCompletions
oid = "1.3.6.1.4.1.2011.5.25.111.4.1.1.3"
oid_index_length = 4
[[inputs.snmp.table.field]]
name = "rttAvg"
#NQA-MIB::nqaResultsRttAvg
oid = "1.3.6.1.4.1.2011.5.25.111.4.1.1.26"
oid_index_length = 4
[inputs.snmp.tagpass]
Descr = [ "*@*" ]
[[inputs.snmp]]
interval = "5m"
agents = [ "cisco_router" ]
version = 2
community = "public"
fieldpass = [ "cbQos*", "bgpPeerState", "bgpPeerFsmEstablishedTransitions", "Duplex" , "Speed" , "ifAdminStatus" , "ifOperStatus" , "ifInDiscards" , "ifInErrors" , "ifOutDiscards" , "ifOutErrors" , "ifHCInOctets" , "ifHCInUcastPkts" , "ifHCInMulticastPkts" , "ifHCInBroadcastPkts" , "ifHCOutOctets" , "ifHCOutUcastPkts" , "ifHCOutMulticastPkts" , "ifHCOutBroadcastPkts" ]
tagexclude = [ "ifIndex" ]
[[inputs.snmp.table]]
name = "interface32"
#oid = "IF-MIB::ifTable"
[[inputs.snmp.table.field]]
name = "Name"
#IF-MIB::ifName
oid = "1.3.6.1.2.1.31.1.1.1.1"
is_tag = true
[[inputs.snmp.table.field]]
name = "Speed"
#IF-MIB::ifHighSpeed
oid = "1.3.6.1.2.1.31.1.1.1.15"
[[inputs.snmp.table.field]]
name = "Duplex"
#EtherLike-MIB::dot3StatsDuplexStatus
oid = "1.3.6.1.2.1.10.7.2.1.19"
[[inputs.snmp.table.field]]
name = "ifAdminStatus"
#IF-MIB::ifAdminStatus
oid = "1.3.6.1.2.1.2.2.1.7"
[[inputs.snmp.table.field]]
name = "ifOperStatus"
#IF-MIB::ifOperStatus
oid = "1.3.6.1.2.1.2.2.1.8"
[[inputs.snmp.table.field]]
name = "ifInDiscards"
#IF-MIB::ifInDiscards
oid = "1.3.6.1.2.1.2.2.1.13"
[[inputs.snmp.table.field]]
name = "ifInErrors"
#IF-MIB::ifInErrors
oid = "1.3.6.1.2.1.2.2.1.14"
[[inputs.snmp.table.field]]
name = "ifOutDiscards"
#IF-MIB::ifOutDiscards
oid = "1.3.6.1.2.1.2.2.1.19"
[[inputs.snmp.table.field]]
name = "ifOutErrors"
#IF-MIB::ifOutErrors
oid = "1.3.6.1.2.1.2.2.1.20"
[[inputs.snmp.table]]
name = "interface64"
#oid = "IF-MIB::ifXTable"
[[inputs.snmp.table.field]]
name = "Name"
#IF-MIB::ifName
oid = "1.3.6.1.2.1.31.1.1.1.1"
is_tag = true
[[inputs.snmp.table.field]]
name = "Speed"
#IF-MIB::ifHighSpeed
oid = "1.3.6.1.2.1.31.1.1.1.15"
[[inputs.snmp.table.field]]
name = "ifHCInOctets"
#IF-MIB::ifHCInOctets
oid = "1.3.6.1.2.1.31.1.1.1.6"
[[inputs.snmp.table.field]]
name = "ifHCInUcastPkts"
#IF-MIB::ifHCInUcastPkts
oid = "1.3.6.1.2.1.31.1.1.1.7"
[[inputs.snmp.table.field]]
name = "ifHCInMulticastPkts"
#IF-MIB::ifHCInMulticastPkts
oid = "1.3.6.1.2.1.31.1.1.1.8"
[[inputs.snmp.table.field]]
name = "ifHCInBroadcastPkts"
#IF-MIB::ifHCInBroadcastPkts
oid = "1.3.6.1.2.1.31.1.1.1.9"
[[inputs.snmp.table.field]]
name = "ifHCOutOctets"
#IF-MIB::ifHCOutOctets
oid = "1.3.6.1.2.1.31.1.1.1.10"
[[inputs.snmp.table.field]]
name = "ifHCOutUcastPkts"
#IF-MIB::ifHCOutUcastPkts
oid = "1.3.6.1.2.1.31.1.1.1.11"
[[inputs.snmp.table.field]]
name = "ifHCOutMulticastPkts"
#IF-MIB::ifHCOutMulticastPkts
oid = "1.3.6.1.2.1.31.1.1.1.12"
[[inputs.snmp.table.field]]
name = "ifHCOutBroadcastPkts"
#IF-MIB::ifHCOutBroadcastPkts
oid = "1.3.6.1.2.1.31.1.1.1.13"
[[inputs.snmp.table]]
name = "bgpPeer"
#oid = "1.3.6.1.2.1.15.3"
[[inputs.snmp.table.field]]
name = "bgpPeerIdentifier"
#BGP4-MIB::bgpPeerIdentifier"
oid = "1.3.6.1.2.1.15.3.1.1"
is_tag = true
[[inputs.snmp.table.field]]
name = "bgpPeerState"
#BGP4-MIB::bgpPeerState"
oid = "1.3.6.1.2.1.15.3.1.2"
[[inputs.snmp.table.field]]
name = "bgpPeerFsmEstablishedTransitions"
#BGP4-MIB::bgpPeerFsmEstablishedTransitions
oid = "1.3.6.1.2.1.15.3.1.15"
[[inputs.snmp.table]]
name ="cbQos"
oid = "CISCO-CLASS-BASED-QOS-MIB::cbQosCMStatsTable"
[[inputs.snmp.table.field]]
name = "ConfigIndex"
#oid = "CISCO-CLASS-BASED-QOS-MIB::cbQosConfigIndex"
oid = "1.3.6.1.4.1.9.9.166.1.5.1.1.2"
is_tag = true
[[inputs.snmp.table.field]]
name = "ParentObjectsIndex"
#oid = "CISCO-CLASS-BASED-QOS-MIB::cbQosParentObjectsIndex"
oid = "1.3.6.1.4.1.9.9.166.1.5.1.1.4"
is_tag = true
[inputs.snmp.tagdrop]
Name = [ "SONET*", "Vo*", "Nu0", "E1*", "Lo*", "Vl*", "CH*", "*:*", "St*", "VL*" ]
[[inputs.snmp]]
interval = "4h"
agents = [ "device" ]
version = 2
max_repetitions = 1
retries = 1
community = "public"
fieldpass = [ "Alias", "Name" ]
[[inputs.snmp.table]]
name = "ifalias"
[[inputs.snmp.table.field]]
name = "Ifindex"
#IF-MIB::ifIndex
oid = "1.3.6.1.2.1.2.2.1.1"
is_tag = true
[[inputs.snmp.table.field]]
name = "Name"
#IF-MIB::ifName
oid = "1.3.6.1.2.1.31.1.1.1.1"
[[inputs.snmp.table.field]]
name = "Alias"
#IF-MIB::ifAlias
oid = "1.3.6.1.2.1.31.1.1.1.18"
[[inputs.snmp]]
interval = "24h"
agents = [ "dlink" ]
version = 2
timeout = "2m"
community = "public"
fieldpass = [ "entPhysicalDescr" , "entPhysicalName" , "entPhysicalFirmwareRev" , "entPhysicalHardwareRev" , "entPhysicalSerialNum" , "entPhysicalSoftwareRev" ]
[[inputs.snmp.field]]
name = "location"
#SNMPv2-MIB::sysLocation.0
oid = "1.3.6.1.2.1.1.6.0"
is_tag = true
[[inputs.snmp.field]]
name = "contact"
#SNMPv2-MIB::sysContact.0
oid = "1.3.6.1.2.1.1.4.0"
is_tag = true
[[inputs.snmp.table]]
name = "inventory_entity"
inherit_tags = [ "location", "contact" ]
#oid = "ENTITY-MIB::entPhysicalTable"
index_as_tag = true # stack, access points
[[inputs.snmp.table.field]]
name = "ModelName"
#ENTITY-MIB::entPhysicalModelName
oid = "1.3.6.1.2.1.47.1.1.1.1.13"
is_tag = true
[[inputs.snmp.table.field]]
name = "Class"
#ENTITY-MIB::entPhysicalClass
oid = "1.3.6.1.2.1.47.1.1.1.1.5"
is_tag = true
[[inputs.snmp.table.field]]
name = "entPhysicalName"
#ENTITY-MIB::entPhysicalName
oid = "1.3.6.1.2.1.47.1.1.1.1.7"
[[inputs.snmp.table.field]]
name = "entPhysicalDescr"
#ENTITY-MIB::entPhysicalDescr
oid = "1.3.6.1.2.1.47.1.1.1.1.2"
[[inputs.snmp.table.field]]
name = "entPhysicalHardwareRev"
#ENTITY-MIB::entPhysicalHardwareRev
oid = "1.3.6.1.2.1.47.1.1.1.1.8"
[[inputs.snmp.table.field]]
name = "entPhysicalFirmwareRev"
#ENTITY-MIB::entPhysicalFirmwareRev
oid = "1.3.6.1.2.1.47.1.1.1.1.9"
[[inputs.snmp.table.field]]
name = "entPhysicalSoftwareRev"
#ENTITY-MIB::entPhysicalSoftwareRev
oid = "1.3.6.1.2.1.47.1.1.1.1.10"
[[inputs.snmp.table.field]]
name = "entPhysicalSerialNum"
#ENTITY-MIB::entPhysicalSerialNum
oid = "1.3.6.1.2.1.47.1.1.1.1.11"
[inputs.snmp.tags]
MfgName = "dlink"
obj = "object_id"
[inputs.snmp.tagpass]
ModelName = [ "*" ]
[[processors.strings]]
namepass = ["inventory_entity"]
[[processors.strings.trim]]
tag = "ModelName"
[[processors.strings.trim]]
field = "entPhysicalDescr"
[[processors.strings.trim]]
field = "entPhysicalName"
[[processors.strings.trim]]
field = "entPhysicalFirmwareRev"
[[processors.strings.trim]]
field = "entPhysicalHardwareRev"
[[processors.strings.trim]]
field = "entPhysicalSerialNum"
[[processors.strings.trim]]
field = "entPhysicalSoftwareRev"
[[processors.strings.lowercase]]
tag = "contact"
[[processors.strings.lowercase]]
tag = "location"
inventory_entity,Class=9,MfgName=cisco,ModelName=WIC-2T\ \ \ \ \ \ \ \ \ \ \ \ ������
[[inputs.snmp]]
interval = "1m"
agents = [ "polycom" ]
version = 2
community = "public"
name = "polycom"
[[inputs.snmp.field]]
name = "PercentPacketLoss"
oid = "1.3.6.1.4.1.2684.1.1.21.0"
conversion = "int"
[[inputs.snmp.field]]
name = "Jitter"
oid = "1.3.6.1.4.1.2684.1.1.22.0"
conversion = "int"
[[inputs.snmp.field]]
name = "Latency"
oid = "1.3.6.1.4.1.2684.1.1.23.0"
conversion = "int"
Генератор конфигурации snmp_exporter
В версии 0.20 пока еще нужен генератор для формирования snmp.yml (конфигурационный файл snmp_exporter) из yaml файла generator.yml Из исходиков не получилось собрать в этот раз, поэтому вариант с докером https://hub.docker.com/r/prom/snmp-generator. В примере ниже podman в ubuntu 20.10 Старый генератор искал мибы в домашнем каталоге .snmp/mibs, поэтому архив со всеми необходимыми мибами там. Сам файл generator.yml в .snmp/
cd .snmp/
docker run -it -v "${PWD}:/opt/" prom/snmp-generator generate
$ ls
generator.yml mibs snmp.yml
generator.yml
Для простоты все устройства, описываемые одним модулем имеют одну и ту же версию snmp и коммутити public. В противном случае необходимо писать множество модулей и далее в конфигурации vmagent'а для каждого такого модуля делать отдельный job
Для примера простой модуль для коммутатора в generator.yml:
modules:
devicename:
retries: 1
walk:
- 1.3.6.1.2.1.1.3 #sysUpTime
- 1.3.6.1.2.1.2.2.1.8 #IF-MIB::ifOperStatus
- 1.3.6.1.2.1.2.2.1.14 #IF-MIB::ifInErrors
- 1.3.6.1.2.1.2.2.1.19 #IF-MIB::ifOutDiscards
- 1.3.6.1.2.1.31.1.1.1.6 #IF-MIB::ifHCInOctets
- 1.3.6.1.2.1.31.1.1.1.7 #IF-MIB::ifHCInUcastPkts
- 1.3.6.1.2.1.31.1.1.1.10 #IF-MIB::ifHCOutOctets
- 1.3.6.1.2.1.31.1.1.1.11 #IF-MIB::ifHCOutUcastPkts
- 1.3.6.1.2.1.31.1.1.1.15 #IF-MIB::ifHighSpeed
lookups:
- source_indexes: [ifIndex]
lookup: 1.3.6.1.2.1.31.1.1.1.1 #ifName
drop_source_indexes: true
- source_indexes: [ifIndex]
lookup: 1.3.6.1.2.1.2.2.1.3 #ifType
Используется только то, что нужно и что можно (строки нельзя, но ссылка внизу). Не требуется собирать вообще все и тем более обе таблицы iftable и ifxtable целиком. Таблица с метриками:
что | зачем |
---|---|
sysUpTime | для определения времени работы оборудования |
ifOperStatus | статус интерфейса up/down |
ifInErrors | общий счетчик входящих ошибок |
ifOutDiscards | дискарды на портах из-за speed mismatch, qos, и тп |
ifHCInOctets | 64-битный счетчик входящих байт |
ifHCInUcastPkts | 64-битный счетчик входящих юникастовых пакетов |
ifHCOutOctets | 64-битный счетчик исходящих байт |
ifHCOutUcastPkts | 64-битный счетчик исходящих юникастовых пакетов |
ifHighSpeed | скорость интерфейса для расчета % утилизации |
Таблица с label, которые дополнительно будут у каждой метрики предыдущей таблицы:
что | зачем |
---|---|
ifName | имя интерфейса из ifxtable вместо ifIndex, который удален drop_source_indexes: true |
ifType | тип интерфейса для фильтрации метрик в vmagent'е, чтобы исключить сбор данных по логическим интерфейсам |
Дропать индекс интерфейсов для некоторых маршрутизаторах cisco нельзя. Например, при опросе 7206VXR с модулями может возникнуть ошибка.
У snmp_exporter'а есть функционал overrides, который можеть дропать метрики. Это необходимо для удаления высококардинальных метрик, например, или мусора.
n3k:
retries: 1
walk:
- 1.3.6.1.2.1.1.3 # sysUpTime
- 1.3.6.1.2.1.2.2.1.7 # IF-MIB::ifAdminStatus
- 1.3.6.1.2.1.2.2.1.8 # IF-MIB::ifOperStatus
- 1.3.6.1.2.1.2.2.1.9 # IF-MIB::ifLastChange
- 1.3.6.1.2.1.2.2.1.14 # IF-MIB::ifInErrors
- 1.3.6.1.2.1.2.2.1.19 # IF-MIB::ifOutDiscards
- 1.3.6.1.2.1.31.1.1.1.6 # IF-MIB::ifHCInOctets
- 1.3.6.1.2.1.31.1.1.1.7 # IF-MIB::ifHCInUcastPkts
- 1.3.6.1.2.1.31.1.1.1.10 # IF-MIB::ifHCOutOctets
- 1.3.6.1.2.1.31.1.1.1.11 # IF-MIB::ifHCOutUcastPkts
- 1.3.6.1.2.1.31.1.1.1.15 # IF-MIB::ifHighSpeed
- 1.3.6.1.2.1.10.7.10 # EtherLike-MIB::dot3PauseTable
- 1.3.6.1.2.1.15.3.1.2 # BGP4-MIB::bgpPeerState
- 1.3.6.1.2.1.15.3.1.15 # BGP4-MIB::bgpPeerFsmEstablishedTransitions
- 1.3.6.1.4.1.9.9.91.1.1.1.1.4 # CISCO-ENTITY-SENSOR-MIB::entSensorValue
- 1.3.6.1.4.1.9.9.91.1.1.1.1.5 # CISCO-ENTITY-SENSOR-MIB::entSensorStatus
- 1.3.6.1.4.1.9.9.109.1.1.1.1.7 # CISCO-PROCESS-MIB::cpmCPUTotal1minRev
- 1.3.6.1.4.1.9.9.109.1.1.1.1.12 # CISCO-PROCESS-MIB::cpmCPUMemoryUsed
- 1.3.6.1.4.1.9.9.109.1.1.1.1.13 # CISCO-PROCESS-MIB::cpmCPUMemoryFree
- 1.3.6.1.4.1.9.9.117.1.1.2 # CISCO-ENTITY-FRU-CONTROL-MIB::cefcFRUPowerStatusTable
- 1.3.6.1.4.1.9.9.117.1.2.1 # CISCO-ENTITY-FRU-CONTROL-MIB::cefcModuleTable
- 1.3.6.1.4.1.9.9.117.1.4.1 # CISCO-ENTITY-FRU-CONTROL-MIB::cefcFanTrayStatusTable
- 1.3.6.1.4.1.9.9.42.1.2.10.1.2 # CISCO-RTTMON-MIB::rttMonLatestRttOperSense
lookups:
- source_indexes: [ifIndex]
lookup: 1.3.6.1.2.1.31.1.1.1.1 # ifName
drop_source_indexes: true
- source_indexes: [dot3StatsIndex]
lookup: 1.3.6.1.2.1.31.1.1.1.1 # ifName
drop_source_indexes: true
- source_indexes: [ifIndex]
lookup: 1.3.6.1.2.1.2.2.1.3 # ifType for eth=6
- source_indexes: [entPhysicalIndex]
drop_source_indexes: true
lookup: 1.3.6.1.2.1.47.1.1.1.1.2 # entPhysicalDescr
overrides:
entSensorStatus:
ignore: true
Override DisplayStrings
Бывает что oid имеет тип string, но в него пишутся необходимые для мониторинга значения. Помимо прочего такие oid'ы необходимо отслеживать и игнорировать, либо переделывать. Если оставить как есть, то строки станут label'ми, что плохо скажется на производительности, т.к. увеличивается кардинальность метрик. Решение описано - Numbers from DisplayStrings with the snmp_exporter Ниже пример с коммутаторами extreme, где память и cpu - это string. https://gtacknowledge.extremenetworks.com/articles/Q_A/Is-there-a-way-to-display-the-OID-1-3-6-1-4-1-1916-1-32-1-4-1-9-for-CPU-utilization-to-an-integer-instead-of-a-string EXTREME-SOFTWARE-MONITOR-MIB
exos:
retries: 1
walk:
- 1.3.6.1.2.1.1.3 # sysUpTime
- 1.3.6.1.2.1.2.2.1.7 # IF-MIB::ifAdminStatus
- 1.3.6.1.2.1.2.2.1.8 # IF-MIB::ifOperStatus
- 1.3.6.1.2.1.2.2.1.9 # IF-MIB::ifLastChange
- 1.3.6.1.2.1.2.2.1.14 # IF-MIB::ifInErrors
- 1.3.6.1.2.1.2.2.1.19 # IF-MIB::ifOutDiscards
- 1.3.6.1.4.1.1916.1.4.14.1.1 # EXTREME-PORT-MIB::extremePortCongDropPkts
- 1.3.6.1.2.1.31.1.1.1.6 # IF-MIB::ifHCInOctets
- 1.3.6.1.2.1.31.1.1.1.7 # IF-MIB::ifHCInUcastPkts
- 1.3.6.1.2.1.31.1.1.1.10 # IF-MIB::ifHCOutOctets
- 1.3.6.1.2.1.31.1.1.1.11 # IF-MIB::ifHCOutUcastPkts
- 1.3.6.1.2.1.31.1.1.1.15 # IF-MIB::ifHighSpeed
- 1.3.6.1.2.1.15.3.1.2 # BGP4-MIB::bgpPeerState
- 1.3.6.1.2.1.15.3.1.15 # BGP4-MIB::bgpPeerFsmEstablishedTransitions
- 1.3.6.1.4.1.1916.1.32.1.4.1.8 # EXTREME-SOFTWARE-MONITOR-MIB::extremeCpuMonitorSystemUtilization1min
- 1.3.6.1.4.1.1916.1.32.2.2 # EXTREME-SOFTWARE-MONITOR-MIB::extremeMemoryMonitorSystemTable
- 1.3.6.1.4.1.1916.1.1.1.27.1.2 # EXTREME-SYSTEM-MIB::extremePowerSupplyStatus
- 1.3.6.1.4.1.1916.1.1.1.7 # EXTREME-SYSTEM-MIB::extremeOverTemperatureAlarm
- 1.3.6.1.4.1.1916.1.1.1.8 # EXTREME-SYSTEM-MIB::extremeCurrentTemperature
- 1.3.6.1.4.1.1916.1.1.1.9 # EXTREME-SYSTEM-MIB::extremeFanStatusTable
lookups:
- source_indexes: [ifIndex]
lookup: 1.3.6.1.2.1.31.1.1.1.1 # ifName for ifmib and fcfemib
drop_source_indexes: true
- source_indexes: [ifIndex]
lookup: 1.3.6.1.2.1.2.2.1.3 # ifType for eth=6
- source_indexes: [entPhysicalIndex]
drop_source_indexes: true
lookup: 1.3.6.1.2.1.47.1.1.1.1.2 # entPhysicalDescr
overrides:
extremeCpuMonitorSystemUtilization1min:
regex_extracts:
'':
- regex: '(.*)'
value: '$1'
extremeMemoryMonitorSystemTotal:
regex_extracts:
'':
- regex: '(.*)'
value: '$1'
extremeMemoryMonitorSystemFree:
regex_extracts:
'':
- regex: '(.*)'
value: '$1'
extremeMemoryMonitorSystemUsage:
regex_extracts:
'':
- regex: '(.*)'
value: '$1'
extremeMemoryMonitorUserUsage:
regex_extracts:
'':
- regex: '(.*)'
value: '$1'
Chain lookup indexes
Есть возможность заменить несколько индексов в oid'е на один. Например, было и стало
# TYPE jnxOperatingTemp gauge
jnxOperatingTemp{jnxOperatingContentsIndex="1",jnxOperatingL1Index="0",jnxOperatingL2Index="0",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="12",jnxOperatingL1Index="1",jnxOperatingL2Index="0",jnxOperatingL3Index="0"} 36
jnxOperatingTemp{jnxOperatingContentsIndex="2",jnxOperatingL1Index="1",jnxOperatingL2Index="0",jnxOperatingL3Index="0"} 29
jnxOperatingTemp{jnxOperatingContentsIndex="2",jnxOperatingL1Index="2",jnxOperatingL2Index="0",jnxOperatingL3Index="0"} 32
jnxOperatingTemp{jnxOperatingContentsIndex="4",jnxOperatingL1Index="1",jnxOperatingL2Index="1",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="4",jnxOperatingL1Index="1",jnxOperatingL2Index="2",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="4",jnxOperatingL1Index="2",jnxOperatingL2Index="1",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="4",jnxOperatingL1Index="2",jnxOperatingL2Index="2",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="4",jnxOperatingL1Index="3",jnxOperatingL2Index="1",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="4",jnxOperatingL1Index="3",jnxOperatingL2Index="2",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="7",jnxOperatingL1Index="1",jnxOperatingL2Index="0",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="8",jnxOperatingL1Index="1",jnxOperatingL2Index="1",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="8",jnxOperatingL1Index="1",jnxOperatingL2Index="2",jnxOperatingL3Index="0"} 0
jnxOperatingTemp{jnxOperatingContentsIndex="9",jnxOperatingL1Index="1",jnxOperatingL2Index="0",jnxOperatingL3Index="0"} 41
# TYPE jnxOperatingTemp gauge
jnxOperatingTemp{jnxOperatingDescr="CB 0"} 36
jnxOperatingTemp{jnxOperatingDescr="FPC: MPC @ 0/*/*"} 0
jnxOperatingTemp{jnxOperatingDescr="Fan Tray 0 Fan 0"} 0
jnxOperatingTemp{jnxOperatingDescr="Fan Tray 0 Fan 1"} 0
jnxOperatingTemp{jnxOperatingDescr="Fan Tray 1 Fan 0"} 0
jnxOperatingTemp{jnxOperatingDescr="Fan Tray 1 Fan 1"} 0
jnxOperatingTemp{jnxOperatingDescr="Fan Tray 2 Fan 0"} 0
jnxOperatingTemp{jnxOperatingDescr="Fan Tray 2 Fan 1"} 0
jnxOperatingTemp{jnxOperatingDescr="PEM 0"} 29
jnxOperatingTemp{jnxOperatingDescr="PEM 1"} 32
jnxOperatingTemp{jnxOperatingDescr="PIC: 4XQSFP28 PIC @ 0/0/*"} 0
jnxOperatingTemp{jnxOperatingDescr="PIC: 8XSFPP PIC @ 0/1/*"} 0
jnxOperatingTemp{jnxOperatingDescr="Routing Engine"} 41
jnxOperatingTemp{jnxOperatingDescr="midplane"} 0
mx:
retries: 1
walk:
- 1.3.6.1.2.1.1.3 # sysUpTime
- 1.3.6.1.2.1.2.2.1.14 # IF-MIB::ifInErrors
- 1.3.6.1.2.1.31.1.1.1.6 # IF-MIB::ifHCInOctets
- 1.3.6.1.2.1.31.1.1.1.10 # IF-MIB::ifHCOutOctets
- 1.3.6.1.2.1.15.3.1.2 # BGP4-MIB::bgpPeerState
- 1.3.6.1.2.1.15.3.1.15 # BGP4-MIB::bgpPeerFsmEstablishedTransitions
- 1.3.6.1.4.1.2636.3.5.2.1.4 # JUNIPER-FIREWALL-MIB::jnxFWCounterPacketCount
- 1.3.6.1.4.1.2636.3.5.2.1.5 # JUNIPER-FIREWALL-MIB::jnxFWCounterByteCount
- 1.3.6.1.4.1.2636.3.1.13 # https://kb.juniper.net/InfoCenter/index?page=content&id=KB17526
# https://kb.juniper.net/InfoCenter/index?page=content&id=KB15467
lookups:
- source_indexes: [ifIndex]
lookup: 1.3.6.1.2.1.31.1.1.1.1 # ifName
drop_source_indexes: true
- source_indexes: [ifIndex]
lookup: 1.3.6.1.2.1.2.2.1.3 # ifType
- source_indexes: [jnxOperatingContentsIndex,jnxOperatingL1Index,jnxOperatingL2Index,jnxOperatingL3Index]
lookup: 1.3.6.1.4.1.2636.3.1.13.1.5 # jnxOperatingDescr
drop_source_indexes: true
HEX in string
Бывает необходимость поменять тип, в противном случае данные в label отображаются в hex. Описано тут Why is my SNMP string showing as hexadecimal?. Ниже пример для мониторинга гипервизора esxi
esxi:
retries: 1
walk:
- 1.3.6.1.2.1.25.1.1 # hrSystemUptime
- 1.3.6.1.2.1.25.2.3.1.5 # hrStorageSize
- 1.3.6.1.2.1.25.2.3.1.6 # hrStorageUsed
- 1.3.6.1.2.1.25.3.3 # hrProcessorTable
- 1.3.6.1.2.1.25.5.1.1.2 # hrSWRunPerfMem
- 1.3.6.1.2.1.25.4.2.1.7 # hrSWRunStatus
lookups:
- source_indexes: [hrStorageIndex]
lookup: 1.3.6.1.2.1.25.2.3.1.2 # hrStorageType
drop_source_indexes: false
- source_indexes: [hrSWRunIndex]
lookup: 1.3.6.1.2.1.25.4.2.1.2 # hrSWRunName
drop_source_indexes: false
overrides:
hrSWRunName:
type: DisplayString
hrStorageSize{hrStorageType="1.3.6.1.2.1.25.2.1.2"}
hrStorageUsed{hrStorageType="1.3.6.1.2.1.25.2.1.2"}
проверка работы экспортера
snmp.yml копируется на хост с exporter'ом, reload. Можно самостоятельно делать запросы для тестирования новых модулей без риска записать лишнее в базу.
curl localhost:9116/snmp?module=mx_v2\&target=mx_host
vmagent
snmp_exporter'у нельзя указать несколько параметров. Есть два способа описать job'ы в конфигурации. Первый, где все ограничивается одним job'ом:
- job_name: snmp
honor_timestamps: true
metrics_path: /snmp
scheme: http
http_sd_configs:
- url: xxxx
follow_redirects: false
refresh_interval: 3m
relabel_configs:
...
- source_labels: [__meta_netbox_module]
separator: ;
regex: (.*)
target_label: __param_module
replacement: $1
action: replace
...
- job_name: 'n3k'
metrics_path: /snmp
params:
module: [n3k]
file_sd_configs:
- files:
- 'targets/n3k.yml'
metric_relabel_configs:
- source_labels: [ifType]
regex: '^[^6]$|\d{2,}'
action: drop
- source_labels: [ifName]
regex: 'mgmt0'
action: drop
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: "fdqn.local:9116"
- job_name: 'exos'
metrics_path: /snmp
params:
module: [exos]
file_sd_configs:
- files:
- 'targets/exos.yml'
metric_relabel_configs:
- source_labels: [ifType]
regex: '^[^6]$|\d{2,}'
action: drop
- source_labels: [ifName]
regex: 'Management'
action: drop
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: "fdqn.local:9116"
- job_name: 'mds9k'
metrics_path: /snmp
params:
module: [mds9k]
file_sd_configs:
- files:
- 'targets/mds9k.yml'
metric_relabel_configs:
- source_labels: [ifType]
regex: '^6$'
action: drop
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: "fdqn.local:9116"
- job_name: 'mx'
metrics_path: /snmp
params:
module: [mx]
file_sd_configs:
- files:
- 'targets/mx.yml'
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: "fdqn.local:9116"
---
- labels:
rack: 8
region: 1
targets:
- s0002
- s0003
---
- targets:
- s0002
- s0003
hw
или desc
,
а не собирать их с оборудования. Пример файла, который указан как remoteWrite_relabelConfig:
---
- source_labels: [instance,ifName]
regex: "s0001;Ethernet1/47"
target_label: "hw"
replacement: "s0003"
- source_labels: [instance,ifName]
regex: "s0002;Ethernet1/48"
target_label: "hw"
replacement: "s0004"
- source_labels: [instance,ifName]
regex: "s0007;1:48"
target_label: "hw"
replacement: "m9"
Язык запросов
Expand VictoriaMetrics WITH templates to canonical PromQL
Все описанные метрики получены vmagent'ом с snmp_exporter'а, записаны в базу VictoriaMetrics.
Для вывода всех метрик, полученных по job'у job_name выполнить:
max({job="job_name"}) by (__name__)
Ниже будут выражения (expr) для vmalert с кратким описанием
ifmib
flapping физического интерфейса, который в up, но его состояние изменилось до этого
(changes(ifLastChange{ifType="6"}[15m]) > 0) and (ifOperStatus == 1) and ON(instance) (sysUpTime > 150000)
(max(ifLastChange{ifType="6"} < 20000) by (instance)) and ON(instance) (sysUpTime < 150000)
(ifAdminStatus == 1) and (ifOperStatus == 2)
rate(dot3HCInPauseFrames[5m]) > 5
rate(ifInErrors[15m]) > 5
rate(ifOutDiscards[15m]) > 100
rate(ifHCOutOctets{ifType="6"}[10m]) > ((ifHighSpeed{ifType="6"})*90000)
bgp
flapping сессии
changes(bgpPeerFsmEstablishedTransitions[15m]) > 0
bgpPeerState{bgpPeerRemoteAddr="9.8.7.6",instance="r0001"} < 6
специфичные для extreme метрики
Питание
extremePowerSupplyStatus !=2
extremeFanOperational > 1
extremeOverTemperatureAlarm < 2
extremeCpuMonitorSystemUtilization1min > 15
состояние ИБП APC
Статус. 2 - ОК. 3 - на батареях. 12 - напряжение выше 250В на входе.
upsBasicOutputStatus{instance="apc_ups"} != 2
Enum из миба для понимания о чем речь
1: unknown
2: onLine
3: onBattery
4: onSmartBoost
5: timedSleeping
6: softwareBypass
7: "off"
8: rebooting
9: switchedBypass
10: hardwareFailureBypass
11: sleepingUntilPowerReturn
12: onSmartTrim