Використання TSDuck для моніторингу IP(TS)-потоків

На сьогоднішній день існують готові (пропрієтарні) рішення для моніторингу IP(TS)-потоків, наприклад VB и iQ, Вони мають досить багатий набір функцій і зазвичай подібні рішення є у великих операторів, що мають справу з ТВ-сервісами. У цій статті описується рішення на базі open source проекту TSDuck, призначене для мінімального контролю IP (TS)-потоків за лічильником CC (continuity counter) та бітрейту. Можливий варіант застосування - контроль втрати пакетів або потоку повністю через орендований L2-канал (який немає можливості нормально моніторити, наприклад, шляхом зчитування лічильників втрат у чергах).

Дуже коротко про TSDuck

TSDuck це open source (ліцензія 2-Clause BSD) ПЗ (набір консольних утиліт та бібліотека для розробки своїх утиліт або плагінів) для маніпуляцій з TS-потоками. Як вход вміє працювати з IP (multicast/unicast), http, hls, dvb-тюнерами, dektec dvb-asi демодулятором, є внутрішній генератор TS-потоку та читання з файлів. Як вихід може бути запис у файл, IP (multicast/unicast), hls, dektec dvb-asi та HiDes модулятори, плеєри (mplayer, vlc, xine) та drop. Між входом і виходом можна включити різні процесори трафіку, наприклад перемапінг PID-ів, робити скремблювання/дескремблювання, аналіз CC-лічильників, підрахунок бітрейту та інші типові для TS-потоків операції.

У цій статті як вход будуть IP-потоки (multicast), використані процесори bitrate_monitor (з назви зрозуміло що це таке) і continuity (аналіз CC-лічильників). Без особливих проблем можна замінити IP multicast іншим типом входу, підтримуваний TSDuck.

є офіційні зборки/пакети TSDuck для більшості актуальних операційних систем. Для Debian їх немає, але вдалося без проблем зібрати під debian 8 та debian 10.

Далі використовується версія TSDuck 3.19-1520, як ОС використовується Linux (для підготовки рішення використовувався debian 10, для реального використання - CentOS 7)

Підготовка TSDuck та ОС

Перш ніж моніторити реальні потоки потрібно переконатися, що TSDuck коректно працює і не відбуваються дропи на рівні мережевої карти або ОС (сокету). Це потрібно для того, щоб потім не гадати, де сталися дропи — на мережі або «всередині сервера». Перевіряти дропи на рівні мережевої карти можна командою ethtool -S ethX, тюнінг робиться тією ж ethtool (зазвичай потрібно збільшувати RX-буфер (-G) і іноді відключати деякі offloads (-K)). Як загальну рекомендацію можна порадити використовувати окремий порт для прийому аналізованого трафіку, якщо є така можливість, це мінімізує помилкові спрацьовування пов'язані з тим, що дроп стався кокрентно на порту аналізатора через наявність іншого трафіку. Якщо такої можливості немає (використовується міні-комп'ютер/NUC з одним портом), дуже бажано налаштувати пріоритезацію аналізованого трафіку по відношенню до іншого на пристрої, в який підключається аналізатор. Щодо віртуальних середовищ, тут потрібно бути обережним і вміти знаходити дропи пакета, починаючи від фізичного порту та закінчуючи додатком усередині віртуальної машини.

Генерація та прийом потоку всередині хоста

Як перший крок підготовки TSDuck будемо генерувати та приймати трафік усередині одного хоста з використанням netns.

Готуємо оточення:

ip netns add P #создаём netns P, в нём будет происходить анализ трафика
ip link add type veth #создаём veth-пару - veth0 оставляем в netns по умолчанию (в этот интерфейс будет генерироваться трафик)
ip link set dev veth1 netns P #veth1 - помещаем в netns P (на этом интерфейсе будет приём трафика)
ip netns exec P ifconfig veth1 192.0.2.1/30 up #поднимаем IP на veth1, не имеет значения какой именно
ip netns exec P ip ro add default via 192.0.2.2 #настраиваем маршрут по умолчанию внутри nents P
sysctl net.ipv6.conf.veth0.disable_ipv6=1 #отключаем IPv6 на veth0 - это делается для того, чтобы в счётчик TX не попадал посторонний мусор
ifconfig veth0 up #поднимаем интерфейс veth0
ip route add 239.0.0.1 dev veth0 #создаём маршрут, чтобы ОС направляла трафик к 239.0.0.1 в сторону veth0

Оточення готове. Запускаємо аналізатор трафіку:

ip netns exec P tsp --realtime -t 
 -I ip 239.0.0.1:1234 
 -P continuity 
 -P bitrate_monitor -p 1 -t 1 
 -O drop

де "-p 1 -t 1" означає що потрібно розраховувати бітрейт кожну секунду і виводити інформацію про бітрейт кожну секунду
Запускаємо генератор трафіку зі швидкістю 10Мбіт/с:

tsp -I craft 
 -P regulate -b 10000000 
 -O ip -p 7 -e --local-port 6000 239.0.0.1:1234

де «-p 7 -e» означає, що потрібно пакувати по 7 TS-пакетів в 1 IP-пакет і це жорстко (-e), тобто. завжди чекати 7 TS-пакетів від останнього процесора перед відправкою формуванням IP-пакету.

Аналізатор починає виводити очікувані повідомлення:

* 2020/01/03 14:55:44 - bitrate_monitor: 2020/01/03 14:55:44, TS bitrate: 9,970,016 bits/s
* 2020/01/03 14:55:45 - bitrate_monitor: 2020/01/03 14:55:45, TS bitrate: 10,022,656 bits/s
* 2020/01/03 14:55:46 - bitrate_monitor: 2020/01/03 14:55:46, TS bitrate: 9,980,544 bits/s

Тепер додаємо трохи дропів:

ip netns exec P iptables -I INPUT -d 239.0.0.1 -m statistic --mode random --probability 0.001 -j DROP

і з'являються повідомлення тип таких:

* 2020/01/03 14:57:11 - continuity: packet index: 80,745, PID: 0x0000, missing 7 packets
* 2020/01/03 14:57:11 - continuity: packet index: 83,342, PID: 0x0000, missing 7 packets 

що є очікуваним. Відключаємо втрату пакетів (ip netns exec P iptables -F) і пробуємо збільшити бітрейт генератора до 100 Мбіт/с. Аналізатор рапортує купу CC-помилок і близько 75 Мбіт/с замість 100. Намагаємося розібратися хто винен - ​​не встигає генератор або проблема не в ньому, для цього запускаємо генерацію фіксованої кількості пакетів (700000 TS-пакетів = 100000 IP-пак

# ifconfig veth0 | grep TX
       TX packets 151825460  bytes 205725459268 (191.5 GiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
# tsp -I craft -c 700000 -P regulate -b 100000000 -P count -O ip -p 7 -e --local-port 6000 239.0.0.1:1234
* count: PID    0 (0x0000):    700,000 packets
# ifconfig veth0 | grep TX
        TX packets 151925460  bytes 205861259268 (191.7 GiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Як видно, було згенеровано рівно 100000 151925460 IP-пакетів (151825460-1). Значить розуміємо що відбувається з аналізатором, при цьому звіряємо з лічильником RX на veth0, він суворо дорівнює лічильнику TX на vethXNUMX, далі дивимося що відбувається лише на рівні сокета:

# ip netns exec P cat /proc/net/udp                                                                                                           
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode ref pointer drops             
  133: 010000EF:04D2 00000000:0000 07 00000000:00000000 00:00000000 00000000     0        0 72338 2 00000000e0a441df 24355 

Тут видно кількість дропів = 24355. У TS-пакетах це 170485 або 24.36% від 700000, таким чином бачимо, що ті самі 25% втраченого бітрейту це дропи в udp-сокеті. Дропа в UDP-сокеті зазвичай виникають через брак буфера, дивимося чому дорівнює розмір буфера сокета за замовчуванням і максимальний розмір буфера сокету:

# sysctl net.core.rmem_default
net.core.rmem_default = 212992
# sysctl net.core.rmem_max
net.core.rmem_max = 212992

Таким чином, якщо програми не вимагають розмір буфера явно, сокети створюються з буфером розміром 208 Кб, але якщо запросять більше, то все одно не отримають запитуване. Оскільки в tsp для IP-входу можна задати розмір буфера (buffer-size), то розмір сокета за замовчуванням чіпати не будемо, а лише задамо максимальний розмір буфера сокету і вкажемо розмір буфера явно через аргументи tsp:

sysctl net.core.rmem_max=8388608
ip netns exec P tsp --realtime -t -I ip 239.0.0.1:1234 -b 8388608 -P continuity -P bitrate_monitor -p 1 -t 1 -O drop

З таким тюнінгом буфера сокету тепер бітрейт, що рапортується, становить приблизно 100Мбіт/с, CC-помилок немає.

По споживанню CPU самим додатком tsp. Щодо одного ядра i5-4260U CPU @ 1.40GHz для аналізу потоку 10Мбіт/с потрібно 3-4% CPU, 100 Мбіт/с - 25%, 200Мбіт/с - 46%. При заданні % втрати пакетів навантаження на CPU практично не збільшується (але може зменшуватися).

На більш продуктивному залозі вдавалося без проблем генерувати та аналізувати потоки понад 1Гб/с.

Тестування на реальних мережевих картах

Після тестування на veth-парі потрібно взяти два хости або два порти одного хоста, з'єднати порти між собою, на одному запустити генератор, на другому аналізатор. Тут несподіванок не сталося, але насправді все залежить від заліза, що слабше, то цікавіше тут буде.

Використання отриманих даних системою моніторингу (Zabbix)

У tsp немає будь-якого machine-readable API типу SNMP або подібного. Повідомлення CC потрібно агрегувати хоча б по 1 секунді (за високого відсотка втрати пакетів, їх може бути сотні/тисяч/десятки тисяч на секунду, залежить від бітрейту).

Таким чином, щоб зберігати інформацію та намалювати графіки за CC-помилками та бітрейтом і зробити якісь аварії далі можуть бути наступні варіанти:

  1. Розпарсувати і сагрегувати (CC) висновок tsp, тобто. перетворити їх у потрібну форму.
  2. Допиліть сам tsp та/або процесорні плагіни bitrate_monitor і continuity, щоб результат видавався в machine-readable формі, придатній для системи моніторингу.
  3. Написати свій додаток поверх бібліотеки tsduck.

Очевидно, що з точки трудовитрат варіант 1 найпростіший, особливо враховуючи те, що сам tsduck написаний низькорівневою (за сучасними мірками) мовою (C++)

Простий прототип парсера + агрегатора на bash показав, що на потоці 10Мбіт/с і 50% втрати пакетів (найгірший варіант), процес bash споживав у 3-4 рази більше CPU, ніж сам процес tsp. Такий варіант розвитку подій є неприйнятним. Власне шматок цього прототипу нижче

Локшина на баші

#!/usr/bin/env bash

missingPackets=0
ccErrorSeconds=0
regexMissPackets='^* (.+) - continuity:.*missing ([0-9]+) packets$'
missingPacketsTime=""

ip netns exec P tsp --realtime -t -I ip -b 8388608 "239.0.0.1:1234" -O drop -P bitrate_monitor -p 1 -t 1  -P continuity 2>&1 | 
while read i
do
    #line example:* 2019/12/28 23:41:14 - continuity: packet index: 6,078, PID: 0x0100, missing 5 packets
    #line example 2: * 2019/12/28 23:55:11 - bitrate_monitor: 2019/12/28 23:55:11, TS bitrate: 4,272,864 bits/s
    if [[ "$i" == *continuity:* ]] 
    then
        if [[ "$i" =~ $regexMissPackets ]]
        then
            missingPacketsTimeNew="${BASH_REMATCH[1]}" #timestamp (seconds)
            if [[ "$missingPacketsTime" != "$missingPacketsTimeNew" ]] #new second with CC error
            then
                ((ccErrorSeconds += 1))
            fi
            missingPacketsTime=$missingPacketsTimeNew
            packets=${BASH_REMATCH[2]} #TS missing packets
            ((missingPackets += packets))
        fi
    elif [[ "$i" == *bitrate_monitor:* ]]
    then
        : #...
    fi
done

Крім того, що це працює неприпустимо повільно, в bash відсутні нормальні нитки, bash jobs є самостійними процесами і довелося робити запис разів на секунду значення missingPackets на сайд-ефекті (при отриманні повідомлення про бітрейт, який приходить кожну секунду). У підсумку, bash був залишений у спокої та було вирішено написати обгортку (парсер+агрегатор) на golang. Споживання CPU аналогічного коду на golang у 4-5 разів менше, ніж самого процесу tsp. Прискорення обгортки рахунок заміни bash на golang вийшло приблизно 16 раз і загалом результат прийнятний (оверхед по CPU на 25% у найгіршому разі). Вихідний файл на golang знаходиться тут.

Запуск обгортки

Для запуску обгортки зроблено найпростіший шаблон сервісу для systemd (тут). Передбачається, що сама обгортка скомпільована в бінарний файл (go build tsduck-stat.go), розміщений у /opt/tsduck-stat/. Передбачається, що використовується golang з підтримкою monotonic clock (>=1.9).

Щоб створити екземпляр сервісу, потрібно виконати команду systemctl enable [захищено електронною поштою]:1234, потім запустити за допомогою systemctl start [захищено електронною поштою]: 1234.

Discovery із Zabbix

Щоб zabbix міг робити дискавері запущених сервісів, зроблено генератор списку груп (discovery.sh), у форматі необхідному для Zabbix discovery, передбачається, що він розміщений там же - в /opt/tsduck-stat. Щоб запускати discovery через zabbix-agent, потрібно додати .conf-файл до директорії з конфігураціями zabbix-agent'а для додавання user-параметра.

Шаблон Zabbix

Створений шаблон (tsduck_stat_template.xml) містить правило автовиявлення, прототипи елементів даних, графіків та тригерів.

Короткий чекліст (ну а раптом хтось вирішить скористатися)

  1. Переконатись, що tsp не дропає пакети в «ідеальних» умовах (генератор і аналізатор підключені безпосередньо), якщо є дропи див. п.2 або текст статті з цього приводу.
  2. Зробити тюнінг максимального буфера сокету (net.core.rmem_max=8388608).
  3. Скомпілювати tsduck-stat.go (go build tsduck-stat.go).
  4. Покласти шаблон сервісу /lib/systemd/system.
  5. Запустити сервіси за допомогою systemctl, перевірити, що почали з'являтися лічильниками (grep «» /dev/shm/tsduck-stat/*). Кількість сервісів за кількістю мультикаст-потоків. Тут може знадобитися створити маршрут до мультикаст-групи, можливо, відключити rp_filter або створити маршрут до source ip.
  6. Запустити discovery.sh, переконайтеся, що він генерує json.
  7. Підкласти конфіг zabbix-агента, перезапустити zabbix-агент.
  8. Завантажити шаблон в zabbix, застосувати його до хоста, на якому здійснюється моніторинг і встановлений zabbix-agent, потиснути близько 5 хвилин, подивитися, що з'явилися нові елементи даних, графіки та тригери.

Результат

Використання TSDuck для моніторингу IP(TS)-потоків

Для завдання виявлення втрати пакетів майже достатньо, принаймні це краще, ніж відсутність моніторингу.

Насправді, CC-«втрати» можуть виникати при склеюванні відеофрагментів (наскільки мені відомо, так робляться вставки на місцевих телецентрах в РФ, тобто без перерахунку CC-лічильника), це потрібно пам'ятати. У пропрієтарних рішеннях ця проблема частково обходиться детектуванням міток SCTE-35 (якщо вони додаються генератором потоку).

З погляду моніторингу якості транспорту, бракує моніторингу jitter (IAT), т.к. ТБ-обладнання (будь то модулятори або кінцеві пристрої) має вимоги до цього параметра і не завжди можна роздмухувати jitbuffer до нескінченності. А jitter може попливти, коли на транзиті використовується обладнання з великими буферами і не налаштований або недосить добре налаштований QoS для передачі подібного realtime-трафіку.

Джерело: habr.com

Додати коментар або відгук