На сьогоднішній день існують готові (пропрієтарні) рішення для моніторингу IP(TS)-потоків, наприклад
Дуже коротко про 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 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-помилками та бітрейтом і зробити якісь аварії далі можуть бути наступні варіанти:
- Розпарсувати і сагрегувати (CC) висновок tsp, тобто. перетворити їх у потрібну форму.
- Допиліть сам tsp та/або процесорні плагіни bitrate_monitor і continuity, щоб результат видавався в machine-readable формі, придатній для системи моніторингу.
- Написати свій додаток поверх бібліотеки 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 (
Щоб створити екземпляр сервісу, потрібно виконати команду systemctl enable [захищено електронною поштою]:1234, потім запустити за допомогою systemctl start [захищено електронною поштою]: 1234.
Discovery із Zabbix
Щоб zabbix міг робити дискавері запущених сервісів, зроблено
Шаблон Zabbix
Короткий чекліст (ну а раптом хтось вирішить скористатися)
- Переконатись, що tsp не дропає пакети в «ідеальних» умовах (генератор і аналізатор підключені безпосередньо), якщо є дропи див. п.2 або текст статті з цього приводу.
- Зробити тюнінг максимального буфера сокету (net.core.rmem_max=8388608).
- Скомпілювати tsduck-stat.go (go build tsduck-stat.go).
- Покласти шаблон сервісу /lib/systemd/system.
- Запустити сервіси за допомогою systemctl, перевірити, що почали з'являтися лічильниками (grep «» /dev/shm/tsduck-stat/*). Кількість сервісів за кількістю мультикаст-потоків. Тут може знадобитися створити маршрут до мультикаст-групи, можливо, відключити rp_filter або створити маршрут до source ip.
- Запустити discovery.sh, переконайтеся, що він генерує json.
- Підкласти конфіг zabbix-агента, перезапустити zabbix-агент.
- Завантажити шаблон в zabbix, застосувати його до хоста, на якому здійснюється моніторинг і встановлений zabbix-agent, потиснути близько 5 хвилин, подивитися, що з'явилися нові елементи даних, графіки та тригери.
Результат
Для завдання виявлення втрати пакетів майже достатньо, принаймні це краще, ніж відсутність моніторингу.
Насправді, CC-«втрати» можуть виникати при склеюванні відеофрагментів (наскільки мені відомо, так робляться вставки на місцевих телецентрах в РФ, тобто без перерахунку CC-лічильника), це потрібно пам'ятати. У пропрієтарних рішеннях ця проблема частково обходиться детектуванням міток SCTE-35 (якщо вони додаються генератором потоку).
З погляду моніторингу якості транспорту, бракує моніторингу jitter (IAT), т.к. ТБ-обладнання (будь то модулятори або кінцеві пристрої) має вимоги до цього параметра і не завжди можна роздмухувати jitbuffer до нескінченності. А jitter може попливти, коли на транзиті використовується обладнання з великими буферами і не налаштований або недосить добре налаштований QoS для передачі подібного realtime-трафіку.
Джерело: habr.com