Всем привет!

Я работаю в крупной федеральной компании, у которой более 2000 объектов. Для большинства задач необходим стабильный канал интернета с высокой скоростью. Поэтому нам необходимо было сделать систему, которая позволяет отслеживать скорость работы интернет каналов на этих объектах, и в случае проблем информировала бы нас об этом.

Собрав все инструменты, что у меня есть, я решил сделать мониторинг скорости интернета на базе Zabbix. Для замеров скорости используется утилита iperf3. Весь код сделан на python.

Схема работы

В общем случае, схема мониторинга выглядит следующим образом:

Схема работы мониторинга скорости интернет каналов
Схема работы мониторинга скорости интернет каналов

На объектах установлены сетевые шлюзы на Linux, на которых поднят iperf3 в режиме сервера. В каждом филиале есть сервер Zabbix Proxy, на котором запускается iperf3 в режиме клиента и замеряет скорость интернета, после он в Zabbix trap записывает данные замера.

Настройка и запуск iperf3 на удаленном объекте

Как уже писал выше, на объектах установлены сетевые шлюзы на Linux. На них был установлен iperf3 и создана задача на запуск iperf3 при старте шлюза. Запуск осуществляется следующей командой, где <IP_address> интерфейса, через который осуществляется замер скорости:

/usr/bin/iperf3 -B <IP_address> -Ds

Настройка iperf3 на Zabbix Proxy сервере

Для начала необходимо установить следующие пакеты: iperf3, python-pip, python. Из pip нам необходимо установить py-zabbix и protobix.

apt install iperf3 python-pip python
pip install py-zabbix
pip install protobix

Далее создаем папку iperf в /usr/lib/zabbix/externalscripts/. В папке создаем два файла iperf3_speed_testing.py и iperf3_task.py (один для замера скорости из Zabbix, второй для периодических замеров всех). И даем им нужные разрешения. Владельцем папки делаем пользователя zabbix.

mkdir /usr/lib/zabbix/externalscripts/iperf
touch /usr/lib/zabbix/externalscripts/iperf/iperf3_speed_testing.py
touch /usr/lib/zabbix/externalscripts/iperf/iperf3_task.py
chown -R zabbix:zabbix /usr/lib/zabbix/externalscripts/iperf
chmod +x /usr/lib/zabbix/externalscripts/iperf/iperf3_speed_testing.py
chmod +x /usr/lib/zabbix/externalscripts/iperf/iperf3_task.py

Описание и содержимое скриптов

Скрипт /usr/lib/zabbix/externalscripts/iperf/iperf3_speed_testing.py необходим для выполнения запуска замера скорости на текущий момент. Он запускает запуск замеров на выбранном хосте в Zabbix.

#!/usr/bin/python
#- *- coding: utf-8 - *-

import protobix
import argparse
import subprocess
import sys
import json
import time

proxy = subprocess.Popen(['hostname'], stdout=subprocess.PIPE)
proxy = proxy.communicate()[0]

def showResult(_upload, _download):
        print "Отправка: " + str(round(_upload/1000000,2)) + " Мбит/с"
        print "Получение: " + str(round(_download/1000000,2)) + " Мбит/с"

def sendData(_upload, _download):
        host_moria = sys.argv[2]
        host_status = {}
        host_status['speed_upload'] = _upload
        host_status['speed_download'] = _download
        zbx_datacontainer = protobix.DataContainer()
        zbx_datacontainer.server_active = proxy.split('\n')[0]
        zbx_datacontainer.server_port = 10051
        zbx_datacontainer.data_type = "items"
        zbx_datacontainer.add({host_moria: host_status})
#       print json.dumps({host_moria: host_status}, indent=4)
        zbx_datacontainer.send()
        showResult(_upload, _download)

start_time = time.time()
result = subprocess.Popen(['/usr/bin/iperf3', '-c', sys.argv[1], '-J', '-P', '5'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

wait = time.time()-start_time
while wait < 45 and result.poll() == None:
        wait = time.time()-start_time
        if wait > 40 and wait < 45 :
                result.kill()
                stdout_value = {"error":"Timeout error"}
                stdout_json = stdout_value
                stdout_value = str(stdout_value)
                break

else:
        stdout_value, stderr_value = result.communicate()
        stdout_json = json.loads(stdout_value)

while stdout_value.find('error') != -1 and stdout_json['error'] == 'error - unable to receive results: Resource temporarily unavailable':
                result = subprocess.Popen(['/usr/bin/iperf3', '-c', sys.argv[1], '-J', '-P', '5'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                stdout_value, stderr_value = result.communicate()
                stdout_json = json.loads(stdout_value)

if stdout_value.find('error') == -1:
        sendData(stdout_json['end']['sum_sent']['bits_per_second'],stdout_json['end']['sum_received']['bits_per_second'])
else:
        print stdout_json['error']

Скрипт /usr/lib/zabbix/externalscripts/iperf/iperf3_task.py необходим для запуска задачи на замер скорости в выбранных группах хостов Zabbix. Для работы данного скрипта также необходимо создать пользователя с доступом к Zabbix API. В данном скрипте используются следующие переменные:

  • zabbix_user - пользователь с доступом к API

  • zabbix_password - пароль пользователя API

  • zabbix_server - адрес сервера zabbix

  • group - группа или группы в Zabbix, для которых необходимо делать замер скорости интернета

#!/usr/bin/python
# - *- coding: utf- 8 - *-
from pyzabbix.api import ZabbixAPI
import protobix
import argparse
import subprocess
import sys
import json
import time

zabbix_user = <zabbix_api_user>
zabbix_password = <zabbix_api_password>
group = ('Gateway_branch_1','Gateway_branch_2')

start_time_script = time.time()

proxy = subprocess.Popen(['hostname'], stdout=subprocess.PIPE)
proxy = proxy.communicate()[0]

def sendData(host, upload, download):
        host_moria = host
        host_status = {}
        host_status['speed_upload'] = upload
        host_status['speed_download'] = download
        zbx_datacontainer = protobix.DataContainer()
        zbx_datacontainer.server_active = proxy.split('\n')[0]
        zbx_datacontainer.server_port = 10051
        zbx_datacontainer.data_type = "items"
        zbx_datacontainer.add({host_moria: host_status})
#       print json.dumps({host_moria: host_status}, indent=4)
        zbx_datacontainer.send()

def getZabbixGroupID(groupName):
        groups = zapi.hostgroup.get(filter={'name':groupName})
        for group in groups:
                return group['groupid']


zapi = ZabbixAPI(url=zabbix_server, user=zabbix_user, password=zabbix_password)

hosts = zapi.host.get(groupids=[getZabbixGroupID(group[0]),getZabbixGroupID(group[1])], selectInterfaces=['ip'])

for host in hosts:

#       print (host['host'])
        start_time = time.time()
        result = subprocess.Popen(['/usr/bin/iperf3', '-c', host['interfaces'][0]['ip'], '-J', '-P', '5'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

        wait = time.time()-start_time
        while wait < 45 and result.poll() == None:
                wait = time.time()-start_time
                if wait > 40 and wait < 45 :
                        result.kill()
                        stdout_value = {"error":"Timeout error"}
                        stdout_json = stdout_value
                        stdout_value = str(stdout_value)
                        break

        else:
                stdout_value, stderr_value = result.communicate()
                stdout_json = json.loads(stdout_value)

        while stdout_value.find('error') != -1 and stdout_json['error'] == 'error - unable to receive results: Resource temporarily unavailable':
                result = subprocess.Popen(['/usr/bin/iperf3', '-c', host['interfaces'][0]['ip'], '-J', '-P', '5'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                stdout_value, stderr_value = result.communicate()
                stdout_json = json.loads(stdout_value)

        if stdout_value.find('error') == -1:
                sendData(host['host'], stdout_json['end']['sum_sent']['bits_per_second'], stdout_json['end']['sum_received']['bits_per_second'])

zapi.user.logout()

related_time = time.time() - start_time_script

host_moria = proxy.split('\n')[0]
host_status = {}
host_status['iperf_task_time'] = float(round(related_time,2))
zbx_datacontainer = protobix.DataContainer()
zbx_datacontainer.server_active = 'hqpv-zbxcl1.megafon-retail.ru'
zbx_datacontainer.server_port = 10051
zbx_datacontainer.data_type = "items"
zbx_datacontainer.add({host_moria: host_status})
zbx_datacontainer.send()

Создаем задачу в crontab

0 2,6,10,14,18,22 * * * /usr/lib/zabbix/externalscripts/iperf/iperf3_task.py

Мы делаем замер раз в 4 часа. Нам этого хватает.

Если у вас используется просто Zabbix сервер, то на нем нужно сделать то же самое.

Настройка шаблонов и скриптов в Zabbix

Для начала необходимо создать шаблон с элементами данных замеров скорости интернета. Я назвал шаблон Internet Speed Testing

Шаблон Inernet speed testing
Шаблон Inernet speed testing

В данном шаблоне необходимо создать следующие элементы данных:

  • Speed Upload

  • Speed Download

Триггеры:

  • Нет данных замеров скорости более 48 часов

  • Низкая скорость загрузки

  • Низкая скорость выгрузки

Триггеры
Триггеры

Макросы:

  • {$LOW_SPEED} - для записи скорости, при которой срабатывает триггер

Также добавляем Скрипт для замера скорости вручную (Администрирование -> Скрипты -> Создать скрипт)

Данные для настройки скрипта ручного замера
Данные для настройки скрипта ручного замера

В поле команда вставляем путь к скрипту в следующем виде /usr/lib/zabbix/externalscripts/iperf/iperf3_speed_testing.py {HOST.CONN} '{HOST.HOST}'

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

Нажимаем на нужный хост и выбираем Скорость интернета. Получаем ответ:

Результат замера скорости
Результат замера скорости

Далее проверяем элементы данных обновились. Ждем некоторое время и можем получаем такую статистику для каждого хоста:

Статистика замеров скорости интернета
Статистика замеров скорости интернета

На этом всё. Спасибо за внимание! ????

Комментарии (19)


  1. shibanovan
    22.11.2021 18:35

    Но ведь если канал чем-то занят, то такой мониторинг покаже необъективно низкую скороть?


    1. Basyuk93 Автор
      22.11.2021 18:46

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


  1. eov
    22.11.2021 20:25
    +2

    На мой взгляд - это деструктивный метод получения нужного знания - скорости канала. При измерении Iperf-ом выжирается полезная полоса и в момент измерения она недоступна для основной задачи - прокачки трафика. Я бы сделал мониторинг загрузки полосы и задержки на какой-нибудь стабильный ресурс, чтобы понимать процент потери пакетов и средний отклик.


    1. Basyuk93 Автор
      22.11.2021 20:41

      Замер происходит быстро. Сотрудники на объекте даже не замечают, что он прошёл. Замер происходит всего раз в 4 часа. А для отслеживания потерь пакетов и времени отклика есть ping.

      Ну и в целом описанная мною реализация мониторинга не претендует на идеальную:)


      1. eov
        22.11.2021 21:04
        +1

        Тогда я перестал понимать зачем нужны эти замеры (раз в четыре часа они мало что показывают)...

        На мой взгляд, проще сделать график по загрузке канала (uplink/downlink), второй график по среднему отклику (в ms) от нужного ресурса, а третий график по количеству потерянных пакетов от нужно ресурса. Все это можно сделать с 5-и минутным интервалом и это будет куда более показательным, чем раз в 4-е часа грузить канал мусорном трафиком.


        1. Gray_Wolf
          22.11.2021 22:16

          Основная задача подобных(но только сертифицированных) систем это оптимизация расходов на каналы связи.

          Т.е. система фиксирует различные нарушения SLA и помимо отправки этих данных провайдеру, так-же включает их в различные отчёты.

          И в случае если компания закупает пару сотен различных каналов, то с одной стороны понемногу набираются претензии на существенные суммы, перекрывающие стоимость системы мониторинга, а с другой позволяют относительно быстро определять косяки провайдера, когда он вместо закупленного расширения ранее купленных каналов расширил другие каналы, или не расширил ничего, а может и расширил, но не на заявленные объёмы.


        1. ksastan
          23.11.2021 00:55

          Согласен, что раз в 4 часа - слишком большой интервал. А вот полезность данной проверки в том, что можно отловить перегруз на маршруте от точки А к Б, например после перестроения у провайдера маршрута на не самый оптимальный. Это актуально для длинных маршрутов


  1. Alexsey
    22.11.2021 22:02

    Странный подход к мониторингу честно говоря. Как уже выше написали - для таких ситуаций обычно мониторят среднюю скорость исходящего/входящего трафика и ставят алерты на резкое падение в течение длительного времени. Мониторинг почти всегда должен быть non-intrusive.

    Нет, если работает и решает задачу то слава богу, но подход странный.


    1. Basyuk93 Автор
      23.11.2021 03:48

      Iperf позволяет нагрузить канал на максимум. При обычной работе канал не нагружается на максимум и не покажет фактическую скорость.


  1. bpof
    23.11.2021 03:41

    А зачем крон использовать, если можно выполнять какой-то item и им запускать замер?


    1. Basyuk93 Автор
      23.11.2021 03:42

      Крон как пример. На текущий момент задача на замер запускается из item'а.


  1. Antiscer
    23.11.2021 09:06

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


    1. Basyuk93 Автор
      23.11.2021 09:08

      Именно поэтому на конечных узлах поднимает iperf в режиме сервера и замер идёт поочерёдно от сервера в режиме клиента.


  1. user0k
    24.11.2021 04:33

    Можно прикрутить smokeping и наблюдать визуально за каналами интернета. Очень удобная вещь.


    1. Basyuk93 Автор
      24.11.2021 04:36

      Не знаком с таким инструментом. Но судя по тому, что удалось найти, всё это умеет делать zabbix. И красивые графики можно выводить в Grafana.


  1. sanhces7
    25.11.2021 07:17

    У Cisco есть IP SLA, у Huawei - NQA. На основе постоянных автоматизированных тестов можно мониторить задержку, джиттер, и потери, и далее по функционалу несколько вариантов, данные забирать по SNMP. Единственный минус - не всё оборудование это поддерживает.
    Но, с минимальным занятием полосы (30-60 Кбит/с, в зависимости от настроек, можно и больше, и меньше) - вполне объективная картина, и мгновенная реакция на обрыв именно измеряемого канала, если до объекта есть резервные каналы.


    1. Basyuk93 Автор
      25.11.2021 16:37

      Давайте рассмотрим ситуацию, когда провайдер вместо купленных 40 Мбит/с отдаёт 5 Мбит/с. Как с помощью описанного Вами инструмента это отследить?


      1. sanhces7
        25.11.2021 19:43

        Косвенно по потерям, например, хотя это и неточный тест. Обычно при покупке делается проверка канала тем же иперфом, и потом канал просто мониторится по трафику и по IP SLA. Если вдруг при превышении 5 Мбит/с появляются потери, то, очевидно, там проблемы, и начинается работа с провайдером по устранению. Это не единственный вариант решения данной задачи. Ваш вариант очень интересный, но я бы делал его, скажем, в 5 утра раз в сутки (это зависит от особенностей конкретной сети, и непринципиально). Такого рода проблемы с провайдером я редко вижу по падению скорости, а вот полные прерывания сервиса - регулярно, и тогда ip sla лучше подходит, там данный функционал зашит.


  1. slam007
    02.12.2021 09:58
    +1

    Спасибо за статью, попробую применить у себя с некоторыми "НО".

    Вы замеряете скорость при помощи TCP, которое зависит от многих факторов: tcp windows size, buffer size, length of buffer, parallel TCP-streams и т.д. И при идеально работающем Интернете можно замерить скорость и в 10 Мб/с и в 100 Мб/с, поэтому может возникнуть ложный вывод, что в низкой скорости Интернета виноват провайдер.

    Лучше замерять не скорость, а ширину канала при помощи UDP, указывая ключ bandwidth в размере 80-90% от договорной скорости Интернета. Данные тест покажет наиболее точно реальную пропускную способность. И по опыту общения с ТП, они принимают показания iperf по UDP и приступают к поиску проблем на своей стороне.

    По договору какую услугу вам продают: VPN или Интернет? Если VPN, то провайдер гарантирует ширину канала, если Интернет, то гарантирует только доступ, но не кач-во канала.