Unifi prometheus exporter

от автора

Всем привет! На днях захотелось сделать графики по всем нашим точкам доступа, у нас их много, часть на базе Mikrotik и с ними нет проблем, он легко опрашивается по SNMP и отдаёт статистику сразу по всем точкам, а вот с Unifi всё сложней, нужно опрашивать каждую точку доступа отдельно, а они у нас иногда меняют, соответственно, нужно какое-то решение, которое будет отслеживать эти изменения автоматически. На просторах Интернет не нашлось ничего готового, поэтому решили написать очередной «велосипед», но хотелось сделать максимально совместимо, чтобы этим могли пользоваться другие.

Наш подход

Мы решили, что напишем приложение/демон, которое может авторизоваться в контроллере Unifi, получить список точек доступа, а дальше по запросу в ручку /metrics будет обращаться ко всем точкам доступа по snmp и отдавать результат наружу в формате prometheus

Реализация

Подробно останавливаться на деталях реализации приложения не буду, всё залил на github и подробно задокументировал. Есть также docker-образ.

Опишу некоторые подходы, которые были использованы:

  • язык разработки Golang

  • для обработки cli-аргументов и переменных окружения использован фреймворк urfave/cli/v2

  • в качестве роутера http-запросов использовали gorilla/mux

  • для ограничения одновременного опроса точек доступа использован примитив синхронизации «семафор«

  • использовали mutex для синхронизации списка точек доступа между горутинами

  • для опроса точек доступа приложение обращается к сторонней реализации snmp-exporter

Настройка окружения

Нам нужно настроить две приложеньки:

  • snmp-exporter — демон, в которого мы будем обращаться из нашей приложеньки

  • unifi-prometheus-exporter — наша приложенька

Я сразу скажу, что я честно закопался в код snmp-exporter, в надежде, что можно будет импортировать код и использовать его функции нативно, но там всё не очень хорошо, код не модульный, его нельзя импортировать, поэтому придётся использовать две приложеньки.

Snmp-exporter

Для начала надо запустить snmp-exporter, к которому мы будем обращаться для опроса наших точек доступа, покажу как это можно запустить в docker-compose и в kubernetes. Установку docker-compose и kubernetes тут рассматривать не буду, это не является целью данного поста.

Экспортер слушает на порту 9116, чтобы опросить удалённый узел по snmp, достаточно послать в экспортер http-запрос в ручку /snmp c параметрами module (по умолчанию if_mib) и target (что опрашиваем), например:

curl http://127.0.0.1:9116/snmp?target=10.0.0.1

docker-compose

version: "2"  services:   nexus:     image: prom/snmp-exporter     ports:       - "9116:9116"

kubernetes

--- apiVersion: apps/v1 kind: Deployment metadata:   name: snmp-exporter   labels:     app: snmp-exporter spec:   replicas: 1   selector:     matchLabels:       app: snmp-exporter   strategy:     rollingUpdate:       maxSurge: 1       maxUnavailable: 0     type: RollingUpdate   template:     metadata:       labels:         app: snmp-exporter     spec:       containers:       - image: prom/snmp-exporter         imagePullPolicy: IfNotPresent         name: exporter         ports:         - containerPort: 9116 --- apiVersion: v1 kind: Service metadata:   name: snmp-exporter spec:   ports:   - port: 9116     protocol: TCP     targetPort: 9116     name: snmp-exporter   selector:     app: snmp-exporter

unifi-prometheus-exporter

теперь запускаем нашу приложеньку, у неё есть ряд cli-аргументов, продублированных переменными окружения:

NAME:    exporter - экспортер snmp-метрик от точек доступа unifi  USAGE:    exporter [global options] command [command options] [arguments...]  COMMANDS:    help, h  Shows a list of commands or help for one command  GLOBAL OPTIONS:    --controller-login value               логин от unifi-контроллера [$CONTROLLER_LOGIN]    --controller-password value            пароль от unifi-контроллера [$CONTROLLER_PASSWORD]    --controller-address value             адрес unifi-контроллера (default: "https://127.0.0.1:8443") [$CONTROLLER_ADDRESS]    --snmp-exporter-address value          адрес snmp-экспортера (default: "http://snmp-exporter:9116") [$SNMP_EXPORTER_ADDRESS]    --access-points-update-interval value  интервал обновления списка точек (default: 1h0m0s) [$ACCESS_POINTS_UPDATE_INTERVAL]    --listen-port value                    порт прослушки http-сервера (default: 8080) [$LISTEN_PORT]    --parallel value                       количество потоков для опроса точек-доступа (default: 10) [$PARALLEL]    --poll-timeout value                   таймаут для опроса точек доступа (default: 15s) [$POLL_TIMEOUT]    --help, -h                             show help (default: false)

в принципе, тут, на мой взгляд, всё понятно. запускаем в docker-compose/kubernetes

docker-compose

version: "2"  services:   nexus:     image: maetx777/unifi-prometheus-exporter     environment:     - CONTROLLLER_LOGIN=admin     - CONTROLLER_PASSWORD=123456     ports:       - "9116:9116"

kubernetes

--- apiVersion: apps/v1 kind: Deployment metadata:   name: unifi-snmp-exporter   labels:     app: unifi-snmp-exporter spec:   replicas: 1   selector:     matchLabels:       app: unifi-snmp-exporter   strategy:     rollingUpdate:       maxSurge: 1       maxUnavailable: 0     type: RollingUpdate   template:     metadata:       labels:         app: unifi-snmp-exporter       annotations:         prometheus.io/scrape: 'true'         prometheus.io/port: '8080'         prometheus.io/path: '/metrics'     spec:       containers:       - image: maetx777/unifi-prometheus-exporter         name: exporter         ports:         - containerPort: 8080         env:         - name: CONTROLLER_LOGIN           value: admin         - name: CONTROLLER_PASSWORD           value: 123456       restartPolicy: Always --- apiVersion: v1 kind: Service metadata:   name: unifi-prometheus-exporter spec:   ports:   - port: 8080     protocol: TCP     targetPort: 8080     name: unifi-prometheus-exporter   selector:     app: unifi-prometheus-exporter      

остановлюсь на нюансах

  • CONTROLLER_LOGIN — при необходимости меняем

  • CONTROLLER_PASSWORD — при необходимости меняем 🙂

  • CONTROLLER_ADDRESS — меняем на адрес unifi-контроллера с указанием протокола и порта, например https://1.2.3.4:8443

  • SNMP_EXPORTER_ADDRESS — меняем на адрес snmp-экспортера, что запустили ранее, можно использовать dns-имя

  • передавать секреты в kubernetes указанным способом плохо, для этого есть специальный ресурс Secret, но здесь это не рассматривается, каждый сам под себя допилит

После запуска

После запуска мы увидим в логах unifi-prometheus-controller нечто подобное

INFO[0000] Daemon start INFO[0000] Start http server INFO[0000] Start fatals catcher INFO[0000] Start signals catcher INFO[0000] Start access points updater INFO[0001] Http client authorized INFO[0001] Update access points list INFO[0001] Access point name Room1, ip 10.0.0.10 INFO[0001] Access point name Room2, ip 10.0.0.20

теперь можно обратиться к нашему контроллеру чтобы получить метрики найденных точек

# curl -s http://127.0.0.1:8080/metrics|grep ifOutOctets ifOutOctets{ap_name="Room1",ap_ip="10.0.0.10",ifAlias="",ifDescr="ath0",ifIndex="6",ifName="ath0"} 0 ifOutOctets{ap_name="Room1",ap_ip="10.0.0.10",ifAlias="",ifDescr="ath1",ifIndex="7",ifName="ath1"} 3.319545249e+09 ifOutOctets{ap_name="Room1",ap_ip="10.0.0.10",ifAlias="",ifDescr="br0",ifIndex="9",ifName="br0"} 2.7572029e+07 ifOutOctets{ap_name="Room1",ap_ip="10.0.0.10",ifAlias="",ifDescr="eth0",ifIndex="2",ifName="eth0"} 4.93001573e+08 ifOutOctets{ap_name="Room1",ap_ip="10.0.0.10",ifAlias="",ifDescr="eth1",ifIndex="3",ifName="eth1"} 0 ifOutOctets{ap_name="Room1",ap_ip="10.0.0.10",ifAlias="",ifDescr="lo",ifIndex="1",ifName="lo"} 3572 ifOutOctets{ap_name="Room1",ap_ip="10.0.0.10",ifAlias="",ifDescr="teql0",ifIndex="5",ifName="teql0"} 0 ifOutOctets{ap_name="Room1",ap_ip="10.0.0.10",ifAlias="",ifDescr="vwire2",ifIndex="8",ifName="vwire2"} 0 ifOutOctets{ap_name="Room2",ap_ip="10.0.0.20",ifAlias="",ifDescr="ath0",ifIndex="6",ifName="ath0"} 0 ifOutOctets{ap_name="Room2",ap_ip="10.0.0.20",ifAlias="",ifDescr="ath1",ifIndex="7",ifName="ath1"} 6.28150693e+08 ifOutOctets{ap_name="Room2",ap_ip="10.0.0.20",ifAlias="",ifDescr="br0",ifIndex="9",ifName="br0"} 2.7178302e+07 ifOutOctets{ap_name="Room2",ap_ip="10.0.0.20",ifAlias="",ifDescr="eth0",ifIndex="2",ifName="eth0"} 4.95262026e+08 ifOutOctets{ap_name="Room2",ap_ip="10.0.0.20",ifAlias="",ifDescr="eth1",ifIndex="3",ifName="eth1"} 0 ifOutOctets{ap_name="Room2",ap_ip="10.0.0.20",ifAlias="",ifDescr="lo",ifIndex="1",ifName="lo"} 8180 ifOutOctets{ap_name="Room2",ap_ip="10.0.0.20",ifAlias="",ifDescr="teql0",ifIndex="5",ifName="teql0"} 0 ifOutOctets{ap_name="Room2",ap_ip="10.0.0.20",ifAlias="",ifDescr="vwire2",ifIndex="8",ifName="vwire2"} 0 ifOutOctets{ap_name="Room2",ap_ip="10.0.0.20",ifAlias="",ifDescr="wifi0",ifIndex="4",ifName="wifi0"} 0

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

также метрики «обогащаются» тегами с именем (ap_name) и ip-адресом (ap_ip) опрошенных точек

Prometheus

Как устанавливать prometheus я тут писать не буду, это не является целью данной статьи, у нас это дело работает на базе kubernetes_sd_config, в представленном конфиге kubernetes задаётся аннотация, которая сообщает системе prometheus порт и ручку для опроса

Grafana

Наконец, покажу простенький дашборд grafana для просмотра графиков

После создания дашборда сразу идём в Dashboard settings, создаём переменную

Query: ifOutOctets{ap_name=~".+"} Regex: /ap_name="([^"]+)"/

смысл этой переменной в том, что мы выбираем все уникальные значения ap_name, чтобы дальше это можно было выбирать из списка:

теперь создаём график и пишем там формулы

A: irate(ifOutOctets{ap_name=~"[[ap_name]]"}[5m])*8 A.Legend: {{ap_name}} {{ap_ip}} {{ifDescr}} out  B: irate(ifInOctets{ap_name=~"[[ap_name]]"}[5m])*-8 B.Legend: {{ap_name}} {{ap_ip}} {{ifDescr}} in

умножение на 8 необходимо по той причине, что ifOutOctets — значение в байтах, а нам нужен график в мегабит/сек

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

и на выходе получаем возможность просматривать график по любой из точек доступа:


ссылка на оригинал статьи https://habr.com/ru/post/658863/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *