今天,有现成的(专有的)解决方案用于监控 IP(TS) 流,例如
非常简单地介绍 TSDuck
TSDuck 是一个用于操作 TS 流的开源(2-Clause BSD 许可证)软件(一组控制台实用程序和一个用于开发自定义实用程序或插件的库)。 作为输入,它可以与 IP(多播/单播)、http、hls、dvb 调谐器、dektec dvb-asi 解调器一起使用,有一个内部 TS 流生成器并从文件中读取。 输出可以写入文件、IP(多播/单播)、hls、dektec dvb-asi 和 HiDes 调制器、播放器(mplayer、vlc、xine)和 drop。 输入和输出之间可以包含各种流量处理器,例如,PID 重映射、加扰/解扰、CC 计数器分析、比特率计算以及其他典型的 TS 流操作。
在本文中,IP 流(多播)将用作输入,使用处理器 bitrate_monitor(从名称中可以清楚地看出它是什么)和连续性(CC 计数器的分析)。 您可以轻松地将 IP 多播替换为 TSDuck 支持的另一种输入类型。
有
接下来使用版本TSDuck 3.19-1520,操作系统为Linux(准备方案使用debian 10,实际使用CentOS 7)
准备 TSDuck 和操作系统
在监控真实流量之前,您需要确保 TSDuck 正常工作并且在网卡或 OS(套接字)级别没有掉线。 这是必需的,以免以后猜测丢失发生的位置 - 在网络上或“服务器内部”。 您可以使用 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 #настраиваем маршрут по умолчанию внутри 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
环境准备好了。 我们启动流量分析器:
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”表示你需要每秒计算一次码率并每秒显示有关码率的信息
我们以 10Mbps 的速度启动流量生成器:
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),即在发送 IP 数据包之前,总是等待来自最后一个处理器的 7 个 TS 数据包。
分析器开始输出预期的消息:
* 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) 并尝试将生成器比特率增加到 100Mbps。 分析器报告了一堆 CC 错误和大约 75 Mbps 而不是 100 Mbps。我们试图找出谁应该受到指责 - 生成器没有时间或问题不在其中,为此我们开始生成固定数量的数据包(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)。 所以让我们弄清楚分析器发生了什么,为此我们检查 veth1 上的 RX 计数器,它严格等于 veth0 上的 TX 计数器,然后我们看看在套接字级别发生了什么:
# 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 KB 缓冲区的套接字,但如果它们请求更多,它们仍然不会收到请求的内容。 由于您可以为 IP 输入 (-buffer-size) 以 tsp 为单位设置缓冲区大小,因此我们不会触及默认套接字大小,而只会设置最大套接字缓冲区大小并通过 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
通过调整套接字缓冲区,现在报告的比特率约为 100Mbps,没有 CC 错误。
根据tsp应用本身的CPU消耗。 相对于一颗核心 i5-4260U CPU @ 1.40GHz,10Mbps 流量分析需要 3-4% CPU,100Mbps - 25%,200Mbps - 46%。 设置 % Packet Loss 时,CPU 的负载实际上不会增加(但可能会减少)。
在更高效的硬件上,可以毫无问题地生成和分析超过 1Gb/s 的流。
在真实网卡上测试
在对一个 veth 对进行测试后,你需要拿两台主机或一台主机的两个端口,将端口相互连接,在一个上启动生成器,在第二个上启动分析器。 这里没有惊喜,但实际上全靠铁,越弱的这里就越有趣。
使用监控系统(Zabbix)接收到的数据
tsp 没有任何机器可读的 API,如 SNMP 或类似的。 CC 消息必须聚合至少 1 秒(丢包率很高,每秒可能有数百/数千/数万,具体取决于比特率)。
因此,为了保存信息并绘制 CC 错误和比特率的图表并制造某种事故,可能有以下选项:
- 解析并聚合(通过 CC)tsp 的输出,即将其转换为所需的形式。
- 完成 tsp 本身和/或处理器插件 bitrate_monitor 和连续性,以便以适合监控系统的机器可读形式给出结果。
- 在 tsduck 库之上编写您的应用程序。
显然,就人工成本而言,选项 1 是最简单的,尤其是考虑到 tsduck 本身是用低级(按现代标准)语言(C++)编写的
一个简单的 bash 解析器 + 聚合器原型表明,在 10Mbps 流和 50% 数据包丢失(最坏情况)下,bash 进程消耗的 CPU 比 tsp 进程本身多 3-4 倍。 这种情况是不可接受的。 实际上是下面这个原型的一部分
面条在上面
#!/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 写一个包装器(parser + aggregator)。 类似golang代码的CPU消耗比tsp进程本身少4-5倍。 由于用 golang 替换 bash 而导致的包装器加速大约是 16 倍,并且通常结果是可以接受的(在最坏的情况下 CPU 开销为 25%)。 golang源文件所在
运行包装器
为了启动包装器,制作了最简单的 systemd 服务模板(
要创建服务实例,需要运行 systemctl enable 命令 [电子邮件保护]:1234 然后用 systemctl start 运行 [电子邮件保护]:1234。
来自 Zabbix 的发现
为了让zabbix能够发现正在运行的服务,完成了
Zabbix模板
简要清单(好吧,如果有人决定使用它怎么办)
- 确保 tsp 在“理想”条件下(生成器和分析器直接连接)不会丢包,如果有丢包,请参阅第 2 项或关于此问题的文章正文。
- 调整最大套接字缓冲区 (net.core.rmem_max=8388608)。
- 编译 tsduck-stat.go (go build tsduck-stat.go)。
- 将服务模板放在 /lib/systemd/system 中。
- 使用 systemctl 启动服务,检查计数器是否已开始出现 (grep "" /dev/shm/tsduck-stat/*)。 服务的数量由多播流的数量决定。 在这里您可能需要创建到多播组的路由,可能禁用 rp_filter 或创建到源 ip 的路由。
- 运行 discovery.sh,确保它生成 json。
- 添加zabbix agent配置,重启zabbix agent。
- 将模板上传到zabbix,应用到被监控的主机上,安装了zabbix-agent,等待5分钟左右,看是否有新的item、graphs和trigger。
导致
对于检测丢包的任务来说,差不多就够了,至少比没有监控要好。
事实上,合并视频片段时可能会发生 CC“丢失”(据我所知,这是俄罗斯联邦当地电视中心的插入方式,即不重新计算 CC 计数器),必须记住这一点。 专有解决方案通过检测 SCTE-35 标签(如果由流生成器添加)来部分规避此问题。
在传输质量监控方面,缺乏抖动监控(IAT)。 电视设备(调制器或终端设备)对此参数有要求,并不总是可以将 jitbuffer 膨胀到无穷大。 当在传输过程中使用具有大缓冲区的设备并且 QoS 未配置或配置不够好以传输此类实时流量时,抖动可能会浮动。
来源: habr.com