Usando o TSDuck para monitorar fluxos de IP (TS)

Hoje, existem soluções prontas (proprietárias) para monitoramento de fluxos IP(TS), por exemplo VB и iQ, eles têm um conjunto bastante rico de funções e, geralmente, grandes operadoras que lidam com serviços de TV têm essas soluções. Este artigo descreve uma solução baseada em um projeto de código aberto TSDuck, projetado para controle mínimo de fluxos IP(TS) por contador CC (contador de continuidade) e taxa de bits. Uma possível aplicação é controlar a perda de pacotes ou todo o fluxo através de um canal L2 alugado (que não pode ser monitorado normalmente, por exemplo, lendo contadores de perdas em filas).

Muito brevemente sobre TSDuck

TSDuck é um software de código aberto (licença BSD de 2 cláusulas) (um conjunto de utilitários de console e uma biblioteca para desenvolver utilitários ou plug-ins personalizados) para manipular fluxos de TS. Como entrada, pode funcionar com IP (multicast/unicast), http, hls, sintonizadores dvb, demodulador dektec dvb-asi, existe um gerador interno de fluxo TS e leitura de arquivos. A saída pode ser escrita em um arquivo, IP (multicast/unicast), hls, dektec dvb-asi e moduladores HiDes, players (mplayer, vlc, xine) e drop. Vários processadores de tráfego podem ser incluídos entre a entrada e a saída, por exemplo, remapeamento de PID, embaralhamento/decodificação, análise de contador CC, cálculo de taxa de bits e outras operações típicas para fluxos de TS.

Neste artigo, os fluxos IP (multicast) serão usados ​​​​como entrada, os processadores bitrate_monitor (pelo nome está claro o que é) e continuidade (análise de contadores CC). Você pode substituir facilmente o IP multicast por outro tipo de entrada suportado pelo TSDuck.

Existem compilações/pacotes oficiais TSDuck para a maioria dos sistemas operacionais atuais. Eles não estão disponíveis para o Debian, mas conseguimos criá-los no debian 8 e no debian 10 sem problemas.

Em seguida, a versão TSDuck 3.19-1520 é usada, Linux é usado como sistema operacional (debian 10 foi usado para preparar a solução, CentOS 7 foi usado para uso real)

Preparando o TSDuck e o sistema operacional

Antes de monitorar os fluxos reais, você precisa garantir que o TSDuck funcione corretamente e que não haja quedas na placa de rede ou no nível do sistema operacional (soquete). Isso é necessário para não adivinhar mais tarde onde ocorreram as quedas - na rede ou “dentro do servidor”. Você pode verificar quedas no nível da placa de rede com o comando ethtool -S ethX, o ajuste é feito pelo mesmo ethtool (geralmente, você precisa aumentar o buffer RX (-G) e às vezes desabilitar alguns offloads (-K)). Como recomendação geral, pode-se aconselhar a utilização de uma porta separada para recebimento do tráfego analisado, se possível, isso minimiza falsos positivos associados ao fato da queda ter ocorrido exatamente na porta do analisador devido à presença de outro tráfego. Se isso não for possível (um minicomputador/NUC com uma porta é usado), então é altamente desejável configurar a priorização do tráfego analisado em relação ao restante no dispositivo ao qual o analisador está conectado. Em relação aos ambientes virtuais, aqui você precisa ter cuidado e conseguir encontrar quedas de pacotes partindo de uma porta física e terminando com um aplicativo dentro de uma máquina virtual.

Geração e recepção de um stream dentro do host

Como primeiro passo na preparação do TSDuck, iremos gerar e receber tráfego dentro de um único host usando netns.

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

O ambiente está pronto. Iniciamos o analisador de tráfego:

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

onde "-p 1 -t 1" significa que você precisa calcular a taxa de bits a cada segundo e exibir informações sobre a taxa de bits a cada segundo
Iniciamos o gerador de tráfego com uma velocidade de 10Mbps:

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

onde "-p 7 -e" significa que você precisa empacotar 7 pacotes TS em 1 pacote IP e fazer isso com força (-e), ou seja, sempre espere 7 pacotes TS do último processador antes de enviar um pacote IP.

O analisador começa a emitir as mensagens esperadas:

* 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

Agora adicione algumas gotas:

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

e mensagens como esta aparecem:

* 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 é esperado. Desative a perda de pacotes (ip netns exec P iptables -F) e tente aumentar a taxa de bits do gerador para 100 Mbps. O analisador relata vários erros CC e cerca de 75 Mbps em vez de 100. Estamos tentando descobrir quem é o culpado - o gerador não tem tempo ou o problema não está nele, para isso começamos a gerar um número fixo de pacotes (700000 pacotes TS = 100000 pacotes 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 você pode ver, exatamente 100000 pacotes IP foram gerados (151925460-151825460). Então vamos descobrir o que está acontecendo com o analisador, para isso verificamos com o contador RX em veth1, é estritamente igual ao contador TX em veth0, então olhamos o que acontece no nível do soquete:

# 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 

Aqui você pode ver o número de quedas = 24355. Em pacotes TS, isso é 170485 ou 24.36% de 700000, então vemos que esses mesmos 25% da taxa de bits perdida são quedas no soquete udp. Quedas em um soquete UDP geralmente ocorrem devido à falta de buffer, observe o tamanho padrão do buffer do soquete e o tamanho máximo do buffer do soquete:

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

Assim, se as aplicações não solicitarem explicitamente um tamanho de buffer, serão criados sockets com um buffer de 208 KB, mas se solicitarem mais ainda não receberão o que foi solicitado. Como você pode definir o tamanho do buffer em tsp para a entrada IP (-buffer-size), não tocaremos no tamanho do soquete padrão, mas apenas definiremos o tamanho máximo do buffer do soquete e especificaremos o tamanho do buffer explicitamente por meio dos 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

Com esse ajuste do buffer de soquete, agora a taxa de bits relatada é de cerca de 100 Mbps, não há erros de CC.

De acordo com o consumo de CPU do próprio aplicativo tsp. Em relação a um núcleo i5-4260U CPU @ 1.40 GHz, a análise de fluxo de 10 Mbps exigirá 3-4% da CPU, 100 Mbps - 25%, 200 Mbps - 46%. Ao definir % Packet Loss, a carga na CPU praticamente não aumenta (mas pode diminuir).

Em hardware mais produtivo, foi possível gerar e analisar streams de mais de 1Gb/s sem problemas.

Testando em placas de rede reais

Depois de testar em um par veth, você precisa pegar dois hosts ou duas portas de um host, conectar as portas entre si, iniciar o gerador em um e o analisador no segundo. Não houve surpresas aqui, mas na verdade tudo depende do ferro, quanto mais fraco, mais interessante será aqui.

Utilizando os dados recebidos pelo sistema de monitoramento (Zabbix)

tsp não possui nenhuma API legível por máquina como SNMP ou similar. As mensagens CC devem ser agregadas por pelo menos 1 segundo (com uma alta porcentagem de perda de pacotes, pode haver centenas/milhares/dezenas de milhares por segundo, dependendo da taxa de bits).

Assim, para salvar as informações e desenhar gráficos para erros de CC e taxa de bits e cometer algum tipo de acidente, podem haver as seguintes opções:

  1. Analisar e agregar (por CC) a saída de tsp, ou seja, convertê-lo para a forma desejada.
  2. Termine o próprio tsp e/ou plug-ins do processador bitrate_monitor e continuidade para que o resultado seja fornecido em um formato legível por máquina adequado para o sistema de monitoramento.
  3. Escreva seu aplicativo no topo da biblioteca tsduck.

Obviamente, a opção 1 é a mais fácil em termos de esforço, especialmente considerando que o próprio tsduck é escrito em uma linguagem de baixo nível (para os padrões modernos) (C ++)

Um simples protótipo de analisador + agregador bash mostrou que em um fluxo de 10 Mbps e 50% de perda de pacotes (pior caso), o processo bash consumia 3-4 vezes mais CPU do que o próprio processo tsp. Este cenário é inaceitável. Na verdade, um pedaço deste protótipo abaixo

Macarrão no topo

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

Além de ser inaceitavelmente lento, não há threads normais no bash, os trabalhos do bash são processos separados e tive que escrever o valor de missingPackets uma vez por segundo no efeito colateral (ao receber mensagens de taxa de bits que chegam a cada segundo). Como resultado, o bash foi deixado sozinho e decidiu-se escrever um wrapper (analisador + agregador) em golang. O consumo de CPU de código golang semelhante é 4-5 vezes menor que o próprio processo tsp. A aceleração do wrapper devido à substituição de bash por golang acabou sendo cerca de 16 vezes e, em geral, o resultado é aceitável (sobrecarga de CPU em 25% no pior caso). O arquivo de origem golang está localizado aqui.

Executar wrapper

Para iniciar o wrapper, foi criado o modelo de serviço mais simples para o systemd (aqui). O wrapper em si deve ser compilado em um arquivo binário (vá construir tsduck-stat.go) localizado em /opt/tsduck-stat/. Presume-se que você esteja usando golang com suporte para relógio monotônico (>=1.9).

Para criar uma instância do serviço, você precisa executar o comando systemctl enable [email protegido]:1234 então execute com systemctl start [email protegido]: 1234.

Descoberta do Zabbix

Para que o zabbix consiga descobrir os serviços em execução, é feito gerador de lista de grupos (discovery.sh), no formato necessário para a descoberta do Zabbix, presume-se que esteja localizado no mesmo local - em /opt/tsduck-stat. Para executar a descoberta via zabbix-agent, você precisa adicionar arquivo .conf para o diretório de configuração do zabbix-agent para adicionar o parâmetro do usuário.

Modelo Zabbix

Modelo criado (tsduck_stat_template.xml) contém a regra de descoberta automática, protótipos de itens, gráficos e acionadores.

Breve lista de verificação (bem, e se alguém decidir usá-la)

  1. Certifique-se de que o tsp não derrube pacotes em condições "ideais" (gerador e analisador estão conectados diretamente), se houver quedas, consulte o parágrafo 2 ou o texto do artigo sobre o assunto.
  2. Faça o ajuste do buffer de soquete máximo (net.core.rmem_max=8388608).
  3. Compile tsduck-stat.go (vá construir tsduck-stat.go).
  4. Coloque o modelo de serviço em /lib/systemd/system.
  5. Inicie os serviços com systemctl, verifique se os contadores começaram a aparecer (grep "" /dev/shm/tsduck-stat/*). O número de serviços pelo número de fluxos multicast. Aqui você pode precisar criar uma rota para o grupo multicast, talvez desabilitar rp_filter ou criar uma rota para o ip de origem.
  6. Execute o discovery.sh, verifique se ele gera json.
  7. Adicione a configuração do agente zabbix, reinicie o agente zabbix.
  8. Carregue o template no zabbix, aplique no host que está sendo monitorado e o zabbix-agent está instalado, aguarde cerca de 5 minutos, veja se há novos itens, gráficos e triggers.

resultado

Usando o TSDuck para monitorar fluxos de IP (TS)

Para a tarefa de detecção de perda de pacotes, é quase o suficiente, pelo menos é melhor do que nenhum monitoramento.

De fato, "perdas" CC podem ocorrer ao mesclar fragmentos de vídeo (pelo que eu sei, é assim que as inserções são feitas nos centros de TV locais na Federação Russa, ou seja, sem recalcular o contador CC), isso deve ser lembrado. As soluções proprietárias contornam parcialmente esse problema detectando rótulos SCTE-35 (se adicionados pelo gerador de fluxo).

Em termos de monitoramento da qualidade do transporte, há uma falta de monitoramento de jitter (IAT). Os equipamentos de TV (seja moduladores ou dispositivos finais) possuem requisitos para esse parâmetro e nem sempre é possível inflar o jitbuffer ao infinito. E o jitter pode flutuar quando o equipamento com buffers grandes é usado em trânsito e o QoS não está configurado ou não está bem configurado o suficiente para transmitir esse tráfego em tempo real.

Fonte: habr.com

Adicionar um comentário