Uso de TSDuck para monitorear flujos de IP (TS)

Hoy en día, existen soluciones listas para usar (propietarias) para monitorear flujos de IP (TS), por ejemplo VB и iQ, tienen un conjunto bastante rico de funciones y, por lo general, los grandes operadores que se ocupan de los servicios de televisión tienen tales soluciones. Este artículo describe una solución basada en un proyecto de código abierto. TSDuck, diseñado para un control mínimo de flujos de IP (TS) por contador CC (contador de continuidad) y tasa de bits. Una posible aplicación es controlar la pérdida de paquetes o todo el flujo a través de un canal L2 arrendado (que normalmente no se puede monitorear, por ejemplo, leyendo contadores de pérdida en las colas).

Muy brevemente sobre TSDuck

TSDuck es un software de código abierto (licencia BSD de 2 cláusulas) (un conjunto de utilidades de consola y una biblioteca para desarrollar utilidades o complementos personalizados) para manipular flujos de TS. Como entrada, puede funcionar con IP (multidifusión/unidifusión), http, hls, sintonizadores dvb, demodulador dektec dvb-asi, hay un generador de flujo de TS interno y lectura de archivos. La salida se puede escribir en un archivo, moduladores IP (multicast/unicast), hls, dektec dvb-asi y HiDes, reproductores (mplayer, vlc, xine) y drop. Se pueden incluir varios procesadores de tráfico entre la entrada y la salida, por ejemplo, reasignación de PID, codificación/descodificación, análisis de contador CC, cálculo de tasa de bits y otras operaciones típicas para transmisiones TS.

En este artículo, los flujos de IP (multidifusión) se utilizarán como entrada, se utilizarán los procesadores bitrate_monitor (por el nombre, está claro de qué se trata) y la continuidad (análisis de los contadores CC). Puede reemplazar fácilmente la multidifusión IP con otro tipo de entrada compatible con TSDuck.

Hay compilaciones/paquetes oficiales TSDuck para la mayoría de los sistemas operativos actuales. No están disponibles para Debian, pero logramos construirlos bajo Debian 8 y Debian 10 sin ningún problema.

A continuación, se usa la versión TSDuck 3.19-1520, se usa Linux como sistema operativo (se usó debian 10 para preparar la solución, CentOS 7 se usó para uso real)

Preparando TSDuck y OS

Antes de monitorear los flujos reales, debe asegurarse de que TSDuck funcione correctamente y que no haya caídas en la tarjeta de red o en el nivel del sistema operativo (socket). Esto es necesario para no adivinar más tarde dónde ocurrieron las caídas: en la red o "dentro del servidor". Puede verificar las caídas en el nivel de la tarjeta de red con el comando ethtool -S ethX, el ajuste se realiza con el mismo ethtool (generalmente, necesita aumentar el búfer RX (-G) y, a veces, deshabilitar algunas descargas (-K)). Como recomendación general, se puede recomendar utilizar un puerto separado para recibir el tráfico analizado, si es posible, esto minimiza los falsos positivos asociados con el hecho de que la caída ocurrió exactamente en el puerto del analizador debido a la presencia de otro tráfico. Si esto no es posible (se utiliza una minicomputadora/NUC con un puerto), entonces es muy recomendable configurar la priorización del tráfico analizado en relación con el resto en el dispositivo al que está conectado el analizador. Con respecto a los entornos virtuales, aquí debe tener cuidado y poder encontrar caídas de paquetes que comiencen desde un puerto físico y terminen con una aplicación dentro de una máquina virtual.

Generación y recepción de un stream dentro del host

Como primer paso para preparar TSDuck, generaremos y recibiremos tráfico dentro de un solo host usando netns.

Preparando el 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

El ambiente está listo. Iniciamos el analizador de tráfico:

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

donde "-p 1 -t 1" significa que necesita calcular la tasa de bits cada segundo y mostrar información sobre la tasa de bits cada segundo
Arrancamos el generador de tráfico con una velocidad de 10Mbps:

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

donde "-p 7 -e" significa que necesita empaquetar 7 paquetes TS en 1 paquete IP y hacerlo duro (-e), es decir siempre espere 7 paquetes TS desde el último procesador antes de enviar un paquete IP.

El analizador comienza a generar los mensajes esperados:

* 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

Ahora añade unas gotas:

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

y aparecen mensajes como este:

* 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 

que se espera. Deshabilite la pérdida de paquetes (ip netns exec P iptables -F) e intente aumentar la tasa de bits del generador a 100 Mbps. El analizador informa un montón de errores de CC y alrededor de 75 Mbps en lugar de 100. Estamos tratando de averiguar quién tiene la culpa: el generador no tiene tiempo o el problema no está en él, para esto comenzamos a generar un número fijo de paquetes (700000 paquetes TS = 100000 paquetes 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

Como puede ver, se generaron exactamente 100000 paquetes IP (151925460-151825460). Entonces, averigüemos qué está sucediendo con el analizador, para esto verificamos con el contador RX en veth1, es estrictamente igual al contador TX en veth0, luego observamos lo que sucede a nivel de 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 

Aquí puede ver el número de caídas = 24355. En paquetes TS, esto es 170485 o 24.36 % de 700000, por lo que vemos que ese mismo 25 % de la tasa de bits perdida son caídas en el socket udp. Las caídas en un socket UDP generalmente ocurren debido a la falta de búfer, observe el tamaño de búfer de socket predeterminado y el tamaño máximo de búfer de socket:

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

Por lo tanto, si las aplicaciones no solicitan explícitamente un tamaño de búfer, los sockets se crean con un búfer de 208 KB, pero si solicitan más, aún no recibirán lo solicitado. Dado que puede establecer el tamaño del búfer en tsp para la entrada de IP (-buffer-size), no tocaremos el tamaño del zócalo predeterminado, sino que solo estableceremos el tamaño máximo del búfer del zócalo y especificaremos el tamaño del búfer explícitamente a través de los argumentos 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 este ajuste del búfer de socket, ahora la tasa de bits informada es de aproximadamente 100 Mbps, no hay errores de CC.

Según el consumo de CPU de la propia aplicación tsp. En relación con un CPU i5-4260U de un núcleo a 1.40 GHz, el análisis de flujo de 10 Mbps requerirá un 3-4 % de CPU, 100 Mbps - 25 %, 200 Mbps - 46 %. Al configurar el % de pérdida de paquetes, la carga en la CPU prácticamente no aumenta (pero puede disminuir).

En hardware más productivo, fue posible generar y analizar flujos de más de 1 Gb/s sin ningún problema.

Pruebas en tarjetas de red reales

Después de probar en un par veth, debe tomar dos hosts o dos puertos de un host, conectar los puertos entre sí, iniciar el generador en uno y el analizador en el segundo. Aquí no hubo sorpresas, pero de hecho todo depende del hierro, cuanto más débil, más interesante será aquí.

Usando los datos recibidos por el sistema de monitoreo (Zabbix)

tsp no tiene ninguna API legible por máquina como SNMP o similar. Los mensajes CC deben agregarse durante al menos 1 segundo (con un alto porcentaje de pérdida de paquetes, puede haber cientos/miles/decenas de miles por segundo, según la tasa de bits).

Así, para guardar tanto la información como dibujar gráficas de errores de CC y bitrate y cometer algún tipo de accidente, pueden existir las siguientes opciones:

  1. Analizar y agregar (por CC) la salida de tsp, es decir convertirlo a la forma deseada.
  2. Finalice el propio tsp y/o los complementos del procesador bitrate_monitor y la continuidad para que el resultado se brinde en una forma legible por máquina adecuada para el sistema de monitoreo.
  3. Escriba su aplicación encima de la biblioteca tsduck.

Obviamente, la opción 1 es la más fácil en términos de esfuerzo, especialmente considerando que tsduck en sí está escrito en un lenguaje de bajo nivel (según los estándares modernos) (C ++)

Un prototipo simple de analizador bash+agregador mostró que en un flujo de 10 Mbps y una pérdida de paquetes del 50 % (en el peor de los casos), el proceso bash consumía de 3 a 4 veces más CPU que el propio proceso tsp. Este escenario es inaceptable. En realidad, una parte de este prototipo a continuación.

fideos en la parte superior

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

Además de ser inaceptablemente lento, no hay subprocesos normales en bash, los trabajos de bash son procesos separados y tuve que escribir el valor de los paquetes perdidos una vez por segundo en el efecto secundario (al recibir mensajes de tasa de bits que vienen cada segundo). Como resultado, bash se quedó solo y se decidió escribir un contenedor (analizador + agregador) en golang. El consumo de CPU de un código golang similar es 4-5 veces menor que el propio proceso tsp. La aceleración del envoltorio debido a la sustitución de bash por golang resultó ser unas 16 veces y, en general, el resultado es aceptable (sobrecarga de la CPU en un 25 % en el peor de los casos). El archivo fuente de golang se encuentra aquí.

Ejecutar contenedor

Para iniciar el contenedor, se creó la plantilla de servicio más simple para systemd (aquí). Se supone que el contenedor en sí debe compilarse en un archivo binario (vaya a compilar tsduck-stat.go) ubicado en /opt/tsduck-stat/. Se supone que está utilizando golang con soporte para reloj monotónico (>=1.9).

Para crear una instancia del servicio, debe ejecutar el comando systemctl enable [email protected]:1234 luego ejecutar con systemctl start [email protected]: 1234.

Descubrimiento de Zabbix

Para que zabbix pueda descubrir servicios en ejecución, se hace generador de listas de grupos (discovery.sh), en el formato requerido para el descubrimiento de Zabbix, se supone que está ubicado en el mismo lugar, en /opt/tsduck-stat. Para ejecutar el descubrimiento a través de zabbix-agent, debe agregar archivo .conf al directorio de configuración de zabbix-agent para agregar el parámetro de usuario.

Plantilla Zabbix

plantilla creada (tsduck_stat_template.xml) contiene la regla de detección automática, prototipos de elementos, gráficos y disparadores.

Breve lista de verificación (bueno, ¿y si alguien decide usarla?)

  1. Asegúrese de que tsp no deje caer paquetes en condiciones "ideales" (el generador y el analizador están conectados directamente), si hay caídas, consulte el párrafo 2 o el texto del artículo sobre este tema.
  2. Ajuste el búfer de socket máximo (net.core.rmem_max=8388608).
  3. Compile tsduck-stat.go (vaya a compilar tsduck-stat.go).
  4. Coloque la plantilla de servicio en /lib/systemd/system.
  5. Inicie los servicios con systemctl, verifique que hayan comenzado a aparecer los contadores (grep "" /dev/shm/tsduck-stat/*). El número de servicios por el número de flujos de multidifusión. Aquí es posible que deba crear una ruta al grupo de multidifusión, tal vez deshabilitar rp_filter o crear una ruta a la IP de origen.
  6. Ejecute discovery.sh, asegúrese de que genere json.
  7. Agregue la configuración del agente zabbix, reinicie el agente zabbix.
  8. Cargue la plantilla en zabbix, aplíquela al host que se está monitoreando y el zabbix-agent está instalado, espere unos 5 minutos, vea si hay nuevos elementos, gráficos y disparadores.

resultado

Uso de TSDuck para monitorear flujos de IP (TS)

Para la tarea de detectar la pérdida de paquetes, es casi suficiente, al menos es mejor que no monitorear.

De hecho, las "pérdidas" de CC pueden ocurrir al fusionar fragmentos de video (que yo sepa, así es como se hacen las inserciones en los centros de televisión locales en la Federación Rusa, es decir, sin volver a calcular el contador de CC), esto debe recordarse. Las soluciones patentadas evitan parcialmente este problema al detectar etiquetas SCTE-35 (si las agrega el generador de flujo).

En términos de monitoreo de la calidad del transporte, hay una falta de monitoreo de jitter (IAT). Los equipos de TV (ya sean moduladores o dispositivos finales) tienen requisitos para este parámetro y no siempre es posible inflar el jitbuffer hasta el infinito. Y la fluctuación puede flotar cuando se utilizan equipos con grandes búferes en tránsito y la QoS no está configurada o no está lo suficientemente bien configurada para transmitir dicho tráfico en tiempo real.

Fuente: habr.com

Añadir un comentario