Heute gibt es fertige (proprietäre) Lösungen zur Überwachung von IP(TS)-Strömen, zum Beispiel und , die über eine umfangreiche Funktionen verfügen und solche Lösungen normalerweise von großen Betreibern angeboten werden, die mit TV-Diensten arbeiten. In diesem Artikel wird eine Lösung auf Basis des Open-Source-Projekts , vorgestellt, die für eine minimale Überwachung von IP(TS)-Strömen mittels des CC (Continuity Counter) und der Bitrate entwickelt wurde. Ein möglicher Anwendungsfall ist die Kontrolle des Paket- oder Strömungsverlusts über einen angemieteten L2-Kanal (der nicht ordnungsgemäß überwacht werden kann, beispielsweise durch das Ablesen von Verlustzählungen in Warteschlangen).
Kurze Einführung zu TSDuck
TSDuck ist eine Open-Source-Software (Lizenz 2-Clause BSD), die aus einer Sammlung von Konsolenwerkzeugen und einer Bibliothek zur Entwicklung eigener Werkzeuge oder Plugins besteht, um mit TS-Streams zu arbeiten. Sie kann IP (Multicast/Unicast), HTTP, HLS, DVB-Tuner und den Dektec DVB-ASI-Demodulator als Eingangsquelle verwenden und verfügt über einen internen TS-Stream-Generator sowie die Möglichkeit, Daten aus Dateien zu lesen. Als Ausgabeformate bietet sie die Möglichkeit zur Dateischreibung, IP (Multicast/Unicast), HLS, Dektec DVB-ASI und HiDes-Modulatoren, Player (MPlayer, VLC, Xine) sowie Drop. Zwischen Eingang und Ausgang können verschiedene Traffic-Processor integriert werden, wie z. B. PID-Umbenennung, Scrambling/Descrambling, die Analyse von CC-Zählern, die Berechnung der Bitrate und andere typische Operationen für TS-Streams.
In diesem Artikel werden IP-Streams (Multicast) als Eingangsquelle verwendet, sowie die Processor-Bitrate_monitor (was aus dem Namen ersichtlich ist) und continuity (Analyse der CC-Zähler). IP-Multicast kann problemlos durch einen anderen von TSDuck unterstützten Eingangstyp ersetzt werden.
Es gibt von TSDuck für die meisten gängigen Betriebssysteme. Für Debian gibt es diese nicht, aber es war problemlos möglich, sie für Debian 8 und Debian 10 zu kompilieren.
Verwendet wird die Version TSDuck 3.19-1520, als OS kommt Linux zum Einsatz (für die Lösung wurde Debian 10 verwendet, für den realen Einsatz — CentOS 7)
Vorbereitung von TSDuck und dem OS
Bevor Sie echte Streams überwachen, sollten Sie sicherstellen, dass TSDuck korrekt arbeitet und keine Drops auf Netzwerk- oder Betriebssystemebene (Socket) stattfinden. Dies ist notwendig, um später nicht zu rätseln, wo die Drops aufgetreten sind – im Netzwerk oder "innerhalb des Servers". Um Drops auf Netzwerkebene zu überprüfen, können Sie den Befehl ethtool -S ethX verwenden; die Optimierung erfolgt ebenfalls über ethtool (in der Regel sollten Sie den RX-Puffer (-G) erhöhen und manchmal bestimmte Offloads (-K) deaktivieren). Als allgemeine Empfehlung kann ich raten, einen separaten Port für den Empfang des analysierten Traffics zu verwenden, wenn dies möglich ist. Das minimiert falsche Alarme, die damit zusammenhängen, dass ein Drop direkt am Port des Analysators aufgrund anderer Trafficquellen aufgetreten ist. Wenn diese Möglichkeit nicht besteht (z. B. bei Verwendung eines Mini-PCs/NUC mit nur einem Port), sollten Sie unbedingt die Priorisierung des analysierten Traffics im Verhältnis zu anderem Traffic auf dem Gerät, an das der Analysator angeschlossen ist, einstellen. In virtuellen Umgebungen ist Vorsicht geboten, und Sie sollten in der Lage sein, Paketdrops vom physischen Port bis zur Anwendung innerhalb der virtuellen Maschine nachzuvollziehen.
Generierung und Empfang des Datenstroms innerhalb des Hosts
Als ersten Schritt zur Vorbereitung werden wir mit TSDuck den Datenverkehr innerhalb eines Hosts unter Verwendung von netns generieren und empfangen.
Umgebung vorbereiten:
ip netns add P #Erstellen von netns P, in dem die Datenanalyse stattfinden wird
ip link add type veth #Erstellen eines veth-Paares - veth0 bleibt im Standard-netns (auf diesem Interface wird der Verkehr generiert)
ip link set dev veth1 netns P #veth1 - Verschieben in netns P (auf diesem Interface wird der Verkehr empfangen)
ip netns exec P ifconfig veth1 192.0.2.1/30 up #IP auf veth1 aktivieren, es spielt keine Rolle welche genau
ip netns exec P ip ro add default via 192.0.2.2 #Standardroute innerhalb von netns P einstellen
sysctl net.ipv6.conf.veth0.disable_ipv6=1 #Deaktivierung von IPv6 auf veth0 - dies wird gemacht, um sicherzustellen, dass keine unerwünschten Daten in den TX-Zähler gelangen
ifconfig veth0 up #veth0-Schnittstelle aktivieren
ip route add 239.0.0.1 dev veth0 #Erstellen einer Route, damit das OS den Verkehr zu 239.0.0.1 über veth0 leitetDie Umgebung ist bereit. Starten Sie den Traffic-Analysator:
ip netns exec P tsp --realtime -t
-I ip 239.0.0.1:1234
-P continuity
-P bitrate_monitor -p 1 -t 1
-O dropwo "-p 1 -t 1" bedeutet, dass die Bitrate jede Sekunde berechnet und Informationen über die Bitrate jede Sekunde ausgegeben werden sollen
Starten Sie den Traffic-Generator mit einer Geschwindigkeit von 10 Mbit/s:
tsp -I craft
-P regulate -b 10000000
-O ip -p 7 -e --local-port 6000 239.0.0.1:1234wobei «-p 7 -e» bedeutet, dass 7 TS-Pakete in 1 IP-Paket verpackt werden sollen und dies streng (-e) erfolgen muss, d.h. das System wartet immer auf 7 TS-Pakete vom letzten Prozessor, bevor es das IP-Paket bildet und sendet.
Der Analysator beginnt, die erwarteten Nachrichten auszugeben:
* 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/sJetzt fügen wir ein wenig Drop-Pakete hinzu:
ip netns exec P iptables -I INPUT -d 239.0.0.1 -m statistic --mode random --probability 0.001 -j DROPund es erscheinen Nachrichten wie diese:
* 2020/01/03 14:57:11 - continuity: Paketindex: 80.745, PID: 0x0000, fehlen 7 Pakete
* 2020/01/03 14:57:11 - continuity: Paketindex: 83.342, PID: 0x0000, fehlen 7 Pakete was zu erwarten ist. Wir schalten den Paketverlust ab (ip netns exec P iptables -F) und versuchen, die Bitrate des Generators auf 100 Mbit/s zu erhöhen. Der Analysator meldet eine Reihe von CC-Fehlern und etwa 75 Mbit/s anstelle von 100. Wir versuchen herauszufinden, wer schuld ist – ob der Generator zu langsam ist oder ob das Problem anderswo liegt, und starten die Generierung einer festen Anzahl von Paketen (700.000 TS-Paketen = 100.000 IP-Paketen):
# 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 0Wie zu erkennen ist, wurden genau 100.000 IP-Pakete (151925460-151825460) generiert. Das bedeutet, wir untersuchen, was mit dem Analysator passiert. Dazu vergleichen wir mit dem RX-Zähler auf veth1, der exakt dem TX-Zähler auf veth0 entspricht. Anschließend betrachten wir, was auf der Socket-Ebene passiert:
# 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 Hier ist die Anzahl der Drops = 24355 sichtbar. In TS-Paketen sind das 170485 oder 24,36 % von 700000. Damit sehen wir, dass die besagten 25 % des verlorenen Bitrates durch Drops im UDP-Socket verursacht werden. Drops in einem UDP-Socket treten normalerweise aufgrund fehlenden Puffers auf. Lassen Sie uns überprüfen, wie groß die Standardpuffergröße des Sockets und die maximale Puffergröße sind:
# sysctl net.core.rmem_default
net.core.rmem_default = 212992
# sysctl net.core.rmem_max
net.core.rmem_max = 212992Wenn Anwendungen die Puffergröße nicht explizit anfordern, werden Sockets mit einem Puffer von 208 KB erstellt. Wenn jedoch eine größere Größe angefordert wird, erhält man dennoch nicht die angeforderte Menge. Da man in tsp für Eingänge mit IP die Puffergröße (—buffer-size) festlegen kann, werden wir die Standardgröße des Sockets nicht anpassen, sondern lediglich die maximale Puffergröße des Sockets festlegen und die Puffergröße explizit über die Argumente tsp angeben:
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 dropMit dieser Pufferanpassung beträgt die nun gemeldete Bitrate etwa 100 Mbit/s, und es gibt keine CC-Fehler.
Der CPU-Verbrauch durch die Anwendung tsp beträgt im Hinblick auf einen i5-4260U CPU @ 1,40 GHz. Für die Analyse eines 10 Mbit/s Streams werden 3-4 % CPU benötigt, bei 100 Mbit/s sind es 25 %, und bei 200 Mbit/s 46 %. Bei Angabe eines % Paketverlustes steigt die CPU-Last nur geringfügig an (kann aber auch abnehmen).
Auf leistungsstarker Hardware konnten problemlos Streams mit über 1 Gbit/s erzeugt und analysiert werden.
Tests mit realen Netzwerkadaptern
Nach den Tests mit dem veth-Paar müssen zwei Hosts oder zwei Ports eines Hosts miteinander verbunden werden. Auf einem wird der Generator und auf dem anderen der Analyzer gestartet. Überraschungen gab es hier nicht, aber es hängt wirklich alles von der Hardware ab – je schwächer die Hardware, desto interessanter wird es hier.
Verwendung der gesammelten Daten im Überwachungssystem (Zabbix)
Tsp verfügt über keine maschinenlesbare API wie SNMP oder ähnliches. Die CC-Nachrichten müssen mindestens einmal pro Sekunde aggregiert werden (bei hohem Paketverlust können es hunderte, tausende oder zehntausende pro Sekunde sein, abhängig von der Bitrate).
Um sowohl Informationen zu speichern als auch Grafiken zu den CC-Fehlern und zur Bitrate zu erstellen sowie weitere Störungen zu analysieren, könnten die folgenden Optionen in Betracht gezogen werden:
- Den tsp-Ausgabe parsen und aggregieren (nach CC), d.h. ihn in das richtige Format umwandeln.
- Den tsp selbst und/oder die Prozessorkomponenten bitrate_monitor und continuity verfeinern, damit das Ergebnis in einem maschinenlesbaren Format für das Überwachungssystem bereitgestellt wird.
- Eine eigene Anwendung auf der Bibliothek tsduck schreiben.
Offensichtlich ist Variante 1, was den Aufwand betrifft, die einfachste, besonders wenn man bedenkt, dass tsduck in einer heutzutage als niedrigstufig betrachteten Sprache (C++) geschrieben ist.
Ein einfacher Prototyp eines Parsers + Aggregators in Bash hat gezeigt, dass bei einem Stream von 10 Mbit/s und 50% Paketverlust (schlechtester Fall) der Bash-Prozess 3-4 Mal mehr CPU verbrauchte als der tsp-Prozess selbst. Eine solche Entwicklung ist nicht akzeptabel. Der relevante Teil dieses Prototyps ist unten.
Bash-Gewirr
#!/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
doneAbgesehen davon, dass es unangemessen langsam arbeitet, fehlen im Bash normale Threads. Bash-Jobs laufen als separate Prozesse und es musste jede Sekunde eine Aufzeichnung des Wertes missingPackets im Side-Effect gemacht werden (bei Erhalt einer Nachricht über die Bitrate, die jede Sekunde eingeht). Schließlich wurde Bash außer Acht gelassen und beschlossen, eine Wrapper (Parser + Aggregator) in Go zu schreiben. Der CPU-Verbrauch des entsprechenden Codes in Go liegt 4 bis 5 Mal niedriger als der des tsp-Prozesses. Die Beschleunigung des Wrappers durch den Austausch von Bash gegen Go betrug etwa das 16-Fache und das Ergebnis ist insgesamt akzeptabel (CPU-Overhead im schlimmsten Fall von 25%). Die Quelldatei in Go befindet sich .
Wrapper starten
Für den Start der Wrapper wurde eine einfachste Service-Vorlage für systemd erstellt (). Es wird angenommen, dass die Wrapper in eine Binärdatei kompiliert wurde (go build tsduck-stat.go) und im /opt/tsduck-stat/ abgelegt ist. Es wird davon ausgegangen, dass Go mit Unterstützung für die monotone Uhr (>=1.9) verwendet wird.
Um eine Service-Instanz zu erstellen, führen Sie den Befehl systemctl enable tsduck-stat@239.0.0.1:1234 aus und starten Sie sie dann mit systemctl start tsduck-stat@239.0.0.1:1234.
Discovery aus Zabbix
Damit Zabbix die Entdeckung laufender Services durchführen kann, wurde ein (discovery.sh), im für Zabbix Discovery erforderlichen Format, wird angenommen, dass es dort abgelegt ist — in /opt/tsduck-stat. Um Discovery über den Zabbix-Agenten auszuführen, müssen Sie hinzufügen in das Verzeichnis mit den Konfigurationen des Zabbix-Agenten, um den Benutzerparameter hinzuzufügen.
Zabbix-Vorlage
(tsduck_stat_template.xml) enthält die Autoentdeckungsregel, Prototypen von Datenelementen, Diagrammen und Triggern.
Kurze Checkliste (für den Fall, dass jemand sie nutzen möchte)
- Stellen Sie sicher, dass tsp keine Pakete unter „idealen“ Bedingungen fallen lässt (Generator und Analyzer direkt verbunden). Wenn es Paketverluste gibt, siehe Punkt 2 oder den Artikeltext dazu.
- Tuning des maximalen Socket-Puffers durchführen (net.core.rmem_max=8388608).
- tsduck-stat.go kompilieren (go build tsduck-stat.go).
- Die Dienstvorlage in /lib/systemd/system ablegen.
- Dienste mit systemctl starten und überprüfen, dass Zähler beginnen zu erscheinen (grep „“ /dev/shm/tsduck-stat/*). Anzahl der Dienste gemäß der Anzahl der Multicast-Streams. Hier könnte es erforderlich sein, eine Route zur Multicast-Gruppe zu erstellen, möglicherweise rp_filter zu deaktivieren oder eine Route zur Quell-IP zu erstellen.
- discovery.sh ausführen und sicherstellen, dass es JSON generiert.
- Die Konfiguration des Zabbix-Agenten einfügen, Zabbix-Agenten neu starten.
- Laden Sie die Vorlage in Zabbix hoch, wenden Sie sie auf den Host an, auf dem das Monitoring erfolgt und der Zabbix-Agent installiert ist, warten Sie etwa 5 Minuten und prüfen Sie, ob neue Datenelemente, Grafiken und Trigger erschienen sind.
Ergebnis

Für die Aufgabe, Paketverluste zu erkennen, ist das fast ausreichend; immerhin ist es besser als kein Monitoring.
In der Tat können CC-'Verluste' bei der Zusammenfügung von Videoausschnitten auftreten (soweit ich weiß, so werden die Einfügungen in den lokalen TV-Studios in Russland gemacht, ohne die CC-Zählung zu berechnen), daran sollte man denken. In proprietären Lösungen wird dieses Problem teilweise durch die Erkennung von SCTE-35-Markierungen umgangen (wenn sie vom Stream-Generator hinzugefügt werden).
Aus der Perspektive der Qualität des Transportmonitorings fehlt die Überwachung von Jitter (IAT), da TV-Geräte (ob Modulatoren oder Endgeräte) Anforderungen an diesen Parameter haben und es nicht immer möglich ist, den Jitbuffer unbegrenzt zu vergrößern. Jitter kann ins Wanken geraten, wenn auf der Transitstrecke Geräte mit großen Puffern verwendet werden und QoS für die Übertragung solcher Echtzeit-Daten nicht eingestellt oder nicht ausreichend gut konfiguriert ist.
Quelle: habr.com
