В днешно време съществуват готови (проприетарни) решения за мониторинг на IP(TS)-потоките, например и , те разполагат с доста богат набор от функции и обикновено подобни решения се предлагат от крупни оператори, които работят с ТВ услуги. В тази статия се описва решение на базата на open source проекта , предназначено за минимален контрол на 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) и понякога да изключите някои офлоуди (-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 #настройваме маршрута по подразбиране в netns 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Мбит/s. Анализаторът докладва куп CC-ошибки и около 75 Мбит/s вместо 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 IP-пакета са били генерирани (151925460-151825460). Следователно разследваме какво се случва с анализатора, за което сверяваме със счетчика RX на veth1, той строго съответства на счетчика TX на veth0, след което наблюдаваме какво се случва на ниво сокет:
# 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Мбит/s, CC-ошибки няма.
По употребление на CPU от самото приложение tsp. За анализ на поток от 10Мбит/с при един процесор i5-4260U @ 1.40GHz ще са нужни 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. Такъв вариант за развитие е неприемлив. Всъщност част от този прототип е по-долу
Лапша на bash
#!/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 са самостоятелни процеси и е трябвало да записваме стойността на missingPackets на страничния ефект (при получаване на съобщение за битрейта, които идват на всеки втори). В крайна сметка, bash бе оставен настрана и беше решено да се напише обвивка (парсер + агрегатор) на golang. Потреблението на CPU на аналогичен код на golang е 4-5 пъти по-малко от самия процес tsp. Ускорението на обвивката с преминаването от bash на golang е около 16 пъти и като цяло, резултатът е приемлив (надхвърлянето на CPU в най-лошия случай е 25%). Изходният файл на golang се намира .
Стартиране на обвивката
За стартиране на обвивката е направен най-прост шаблон на сервиз за systemd (). Предполага се, че самата обвивка е компилирана в бинарен файл (go build tsduck-stat.go), разположен в /opt/tsduck-stat/. Предполага се, че се използва golang с поддръжка на монотонен часовник (>=1.9).
За да създадете инстанция на сервиза, трябва да изпълните команда systemctl enable tsduck-stat@239.0.0.1:1234, след което да стартирате с помощта на systemctl start tsduck-stat@239.0.0.1:1234.
Откритие от Zabbix
За да може zabbix да извършва откритие на стартирани услуги, е направен (discovery.sh), в необходимия формат за откритие в Zabbix, предполага се, че е разположен там също — в /opt/tsduck-stat. За да стартирате откритие чрез zabbix-agent, трябва да добавите в директорията с конфигурации на zabbix-agent за добавяне на потребителски параметри.
Шаблон Zabbix
(tsduck_stat_template.xml) съдържа правило за автоматично откритие, прототипи на елементи от данни, графики и триггери.
Кратък контролен списък (пък ако някой реши да се възползва)
- Уверете се, че tsp не губи пакети при «перфектни» условия (генераторът и анализаторът са свързани директно), ако има загуби, вижте т.2 или текста на статията по този въпрос.
- Настройте максималния буфер на сокета (net.core.rmem_max=8388608).
- Скомпилирайте tsduck-stat.go (go build tsduck-stat.go).
- Поставете шаблона на сервиза в /lib/systemd/system.
- Стартирайте услугите с помощта на systemctl, проверете дали започват да се появяват регистриращи броячи (grep «» /dev/shm/tsduck-stat/*). Броят на услугите отговаря на броя на multicast потоковете. Може да се наложи да създадете маршрут до multicast групата, възможно е да деактивирате rp_filter или да създадете маршрут до source ip.
- Стартирайте discovery.sh, уверете се, че генерира json.
- Подложете конфигурация на zabbix-агента и рестартирайте zabbix-агента.
- Качете шаблон в zabbix, приложете го към хоста, на който се извършва мониторинг и е инсталиран zabbix-agent, изчакайте около 5 минути, след което вижте дали са се появили нови елементи от данни, графики и тригери.
Резултат

За задачата за откриване на загуба на пакети почти е достатъчно, всъщност това е по-добре от липсата на мониторинг.
Наистина, CC-«загубите» могат да възникнат при слепването на видеофрагменти (наскоро разбрах, че така се правят вмъквания в местните телевизионни центрове в Русия, т.е. без препроизчисляване на CC-брояча), важно е да се помни. В собственически решения този проблем се заобикаля частично чрез детектиране на SCTE-35 маркировки (ако те се добавят от генератора на потока).
От гледна точка на мониторинга на качеството на предаването, липсва мониторинг на jitter (IAT), тъй като телевизионното оборудване (независимо дали става въпрос за модулиратори или крайни устройства) има изисквания към този параметър и не винаги е възможно да се разширява jitter буферът до безкрайност. А jitter може да се промени, когато в транзита се използва оборудване с големи буфери и QoS за предаване на такъв реално-временен трафик не е настроен или не е настроен достатъчно добре.
Източник: habr.com
