Utilizzo di TSDuck per monitorare i flussi IP (TS).

Oggi, ad esempio, esistono soluzioni già pronte (proprietarie) per il monitoraggio dei flussi IP(TS). VB и iQ, hanno un insieme abbastanza ricco di funzioni e di solito i grandi operatori che si occupano di servizi televisivi dispongono di tali soluzioni. Questo articolo descrive una soluzione basata su un progetto open source TSDuck, progettato per un controllo minimo dei flussi IP(TS) tramite contatore CC(continuity counter) e bitrate. Una possibile applicazione è il controllo della perdita di pacchetti o dell'intero flusso attraverso un canale L2 affittato (che normalmente non può essere monitorato, ad esempio, leggendo i contatori di perdita nelle code).

Molto brevemente su TSDuck

TSDuck è un software open source (licenza BSD a 2 clausole) (un insieme di utilità della console e una libreria per lo sviluppo di utilità o plug-in personalizzati) per la manipolazione dei flussi TS. Come input, può funzionare con IP (multicast/unicast), http, hls, sintonizzatori dvb, demodulatore dektec dvb-asi, c'è un generatore di flusso TS interno e lettura da file. L'output può essere scritto su un file, IP (multicast/unicast), hls, modulatori dektec dvb-asi e HiDes, player (mplayer, vlc, xine) e drop. Vari processori di traffico possono essere inclusi tra input e output, ad esempio rimappatura PID, scrambling/descrambling, analisi del contatore CC, calcolo del bitrate e altre operazioni tipiche per i flussi TS.

In questo articolo verranno utilizzati flussi IP (multicast) come input, vengono utilizzati i processori bitrate_monitor (dal nome è chiaro di cosa si tratta) e continuità (analisi dei contatori CC). Puoi facilmente sostituire il multicast IP con un altro tipo di input supportato da TSDuck.

Ci sono build/pacchetti ufficiali TSDuck per la maggior parte dei sistemi operativi attuali. Non sono disponibili per Debian, ma siamo riusciti a crearli sotto Debian 8 e Debian 10 senza problemi.

Successivamente, viene utilizzata la versione TSDuck 3.19-1520, Linux viene utilizzato come sistema operativo (debian 10 è stato utilizzato per preparare la soluzione, CentOS 7 è stato utilizzato per l'uso reale)

Preparazione di TSDuck e del sistema operativo

Prima di monitorare i flussi reali, è necessario assicurarsi che TSDuck funzioni correttamente e non ci siano cadute a livello di scheda di rete o sistema operativo (socket). Ciò è necessario per non indovinare in seguito dove si sono verificati i drop: sulla rete o "all'interno del server". Puoi controllare i drop a livello di scheda di rete con il comando ethtool -S ethX, l'ottimizzazione viene eseguita dallo stesso ethtool (di solito, devi aumentare il buffer RX (-G) e talvolta disabilitare alcuni offload (-K)). Come raccomandazione generale, si consiglia di utilizzare una porta separata per la ricezione del traffico analizzato, se possibile, questo riduce al minimo i falsi positivi associati al fatto che il calo è avvenuto esattamente sulla porta dell'analizzatore a causa della presenza di altro traffico. Se ciò non è possibile (viene utilizzato un mini-computer/NUC con una porta), è altamente auspicabile impostare la prioritizzazione del traffico analizzato in relazione al resto sul dispositivo a cui è collegato l'analizzatore. Per quanto riguarda gli ambienti virtuali, qui bisogna stare attenti ed essere in grado di trovare i drop di pacchetti che partono da una porta fisica e finiscono con un'applicazione all'interno di una macchina virtuale.

Generazione e ricezione di un flusso all'interno dell'host

Come primo passo nella preparazione di TSDuck, genereremo e riceveremo traffico all'interno di un singolo host utilizzando netns.

Preparazione dell'ambiente:

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

L'ambiente è pronto. Avviamo l'analizzatore di traffico:

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

dove "-p 1 -t 1" significa che devi calcolare il bitrate ogni secondo e visualizzare le informazioni sul bitrate ogni secondo
Avviamo il generatore di traffico con una velocità di 10Mbps:

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

dove "-p 7 -e" significa che devi impacchettare 7 pacchetti TS in 1 pacchetto IP e farlo duro (-e), cioè attendere sempre 7 pacchetti TS dall'ultimo processore prima di inviare un pacchetto IP.

L'analizzatore inizia a emettere i messaggi previsti:

* 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

Ora aggiungi alcune gocce:

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

e compaiono messaggi come questo:

* 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 

che è previsto. Disabilita la perdita di pacchetti (ip netns exec P iptables -F) e prova ad aumentare il bitrate del generatore a 100Mbps. L'analizzatore riporta una serie di errori CC e circa 75 Mbps invece di 100. Stiamo cercando di capire di chi è la colpa: il generatore non ha tempo o il problema non è in esso, per questo iniziamo a generare un numero fisso di pacchetti (700000 pacchetti TS = 100000 pacchetti 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

Come puoi vedere, sono stati generati esattamente 100000 pacchetti IP (151925460-151825460). Quindi scopriamo cosa sta succedendo con l'analizzatore, per questo controlliamo con il contatore RX su veth1, è strettamente uguale al contatore TX su veth0, quindi guardiamo cosa succede a livello di socket:

# 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 

Qui puoi vedere il numero di drop = 24355. Nei pacchetti TS, questo è 170485 o il 24.36% di 700000, quindi vediamo che lo stesso 25% del bitrate perso sono drop nel socket udp. I drop in un socket UDP di solito si verificano a causa della mancanza di buffer, guarda la dimensione del buffer del socket predefinita e la dimensione massima del buffer del socket:

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

Pertanto, se le applicazioni non richiedono esplicitamente una dimensione del buffer, i socket vengono creati con un buffer di 208 KB, ma se ne richiedono di più, non riceveranno comunque quanto richiesto. Poiché è possibile impostare la dimensione del buffer in tsp per l'input IP (-buffer-size), non toccheremo la dimensione predefinita del socket, ma imposteremo solo la dimensione massima del buffer del socket e specificheremo esplicitamente la dimensione del buffer tramite gli argomenti 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

Con questa messa a punto del buffer del socket, ora il bitrate riportato è di circa 100 Mbps, non ci sono errori CC.

In base al consumo di CPU dell'applicazione tsp stessa. Rispetto a una CPU i5-4260U core a 1.40 GHz, l'analisi del flusso a 10 Mbps richiederà il 3-4% della CPU, 100 Mbps - 25%, 200 Mbps - 46%. Quando si imposta % Packet Loss, il carico sulla CPU praticamente non aumenta (ma può diminuire).

Su hardware più produttivo è stato possibile generare e analizzare stream superiori a 1Gb/s senza problemi.

Test su schede di rete reali

Dopo il test su una coppia veth, è necessario prendere due host o due porte di un host, collegare le porte tra loro, avviare il generatore su uno e l'analizzatore sul secondo. Non ci sono state sorprese qui, ma in realtà tutto dipende dal ferro, più debole, più interessante sarà qui.

Utilizzo dei dati ricevuti dal sistema di monitoraggio (Zabbix)

tsp non ha alcuna API leggibile dalla macchina come SNMP o simili. I messaggi CC devono essere aggregati per almeno 1 secondo (con un'alta percentuale di perdita di pacchetti, possono esserci centinaia/migliaia/decine di migliaia al secondo, a seconda del bitrate).

Pertanto, al fine di salvare sia le informazioni che disegnare grafici per errori CC e bitrate e creare qualche tipo di incidente, potrebbero esserci le seguenti opzioni:

  1. Analizza e aggrega (tramite CC) l'output di tsp, ad es. convertirlo nella forma desiderata.
  2. Termina tsp stesso e/o i plugin del processore bitrate_monitor e continuity in modo che il risultato sia fornito in una forma leggibile dalla macchina adatta al sistema di monitoraggio.
  3. Scrivi la tua applicazione sopra la libreria tsduck.

Ovviamente, l'opzione 1 è la più semplice in termini di impegno, soprattutto considerando che lo stesso tsduck è scritto in un linguaggio di basso livello (per gli standard moderni) (C ++)

Un semplice prototipo di parser bash + aggregatore ha mostrato che su un flusso di 10 Mbps e una perdita di pacchetti del 50% (caso peggiore), il processo bash ha consumato 3-4 volte più CPU rispetto al processo tsp stesso. Questo scenario è inaccettabile. In realtà un pezzo di questo prototipo qui sotto

Tagliatelle in cima

#!/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

Oltre ad essere inaccettabilmente lento, non ci sono thread normali in bash, i lavori bash sono processi separati e ho dovuto scrivere il valore di missingPackets una volta al secondo sull'effetto collaterale (quando si ricevono messaggi di bitrate che arrivano ogni secondo). Di conseguenza, bash è stato lasciato solo ed è stato deciso di scrivere un wrapper (parser + aggregatore) in golang. Il consumo di CPU di un codice golang simile è 4-5 volte inferiore rispetto al processo tsp stesso. L'accelerazione del wrapper dovuta alla sostituzione di bash con golang si è rivelata di circa 16 volte e in generale il risultato è accettabile (overhead della CPU del 25% nel peggiore dei casi). Il file sorgente golang si trova qui.

Esegui l'involucro

Per avviare il wrapper, è stato creato il modello di servizio più semplice per systemd (qui). Il wrapper stesso dovrebbe essere compilato in un file binario (go build tsduck-stat.go) situato in /opt/tsduck-stat/. Si presuppone che tu stia utilizzando golang con il supporto per l'orologio monotono (>=1.9).

Per creare un'istanza del servizio, è necessario eseguire il comando systemctl enable [email protected]:1234 quindi esegui con systemctl start [email protected]: 1234.

Scoperta da Zabbix

Affinché zabbix sia in grado di scoprire i servizi in esecuzione, è fatto generatore di elenchi di gruppi (discovery.sh), nel formato richiesto per la scoperta di Zabbix, si presume che si trovi nello stesso posto - in /opt/tsduck-stat. Per eseguire il rilevamento tramite zabbix-agent, è necessario aggiungere file .conf nella directory di configurazione zabbix-agent per aggiungere il parametro utente.

Modello Zabbix

Modello creato (tsduck_stat_template.xml) contiene la regola di individuazione automatica, i prototipi degli elementi, i grafici e i trigger.

Breve lista di controllo (beh, cosa succede se qualcuno decide di usarlo)

  1. Assicurarsi che tsp non rilasci pacchetti in condizioni "ideali" (generatore e analizzatore sono collegati direttamente), se ci sono cadute, vedere il paragrafo 2 o il testo dell'articolo su questo argomento.
  2. Effettua la messa a punto del buffer massimo del socket (net.core.rmem_max=8388608).
  3. Compila tsduck-stat.go (vai a costruire tsduck-stat.go).
  4. Inserisci il modello di servizio in /lib/systemd/system.
  5. Avvia i servizi con systemctl, controlla che i contatori abbiano iniziato ad apparire (grep "" /dev/shm/tsduck-stat/*). Il numero di servizi per il numero di flussi multicast. Qui potrebbe essere necessario creare un percorso verso il gruppo multicast, forse disabilitare rp_filter o creare un percorso verso l'ip di origine.
  6. Esegui discovery.sh, assicurati che generi json.
  7. Aggiungi la configurazione dell'agente zabbix, riavvia l'agente zabbix.
  8. Carica il modello su zabbix, applicalo all'host che viene monitorato e l'agente zabbix è installato, attendi circa 5 minuti, vedi se ci sono nuovi elementi, grafici e trigger.

risultato

Utilizzo di TSDuck per monitorare i flussi IP (TS).

Per il compito di rilevare la perdita di pacchetti, è quasi sufficiente, almeno è meglio di nessun monitoraggio.

In effetti, possono verificarsi "perdite" CC quando si uniscono frammenti video (per quanto ne so, è così che vengono realizzati gli inserti nei centri televisivi locali della Federazione Russa, ad es. senza ricalcolare il contatore CC), questo va ricordato. Le soluzioni proprietarie aggirano parzialmente questo problema rilevando le etichette SCTE-35 (se aggiunte dal generatore di flusso).

In termini di monitoraggio della qualità del trasporto, manca il monitoraggio del jitter (IAT). Le apparecchiature TV (siano esse modulatori o dispositivi finali) hanno requisiti per questo parametro e non è sempre possibile gonfiare il jitbuffer all'infinito. Inoltre, il jitter può fluttuare quando in transito vengono utilizzate apparecchiature con buffer di grandi dimensioni e la QoS non è configurata o non è sufficientemente configurata per trasmettere tale traffico in tempo reale.

Fonte: habr.com

Aggiungi un commento