Docker容器内使用TC流量控制低带宽限速结果不稳定如何解决
问题根因
低带宽场景下TC限速出现锯齿状波动、首测速率超标,不是TC调度算法稳定性问题,是默认配置参数和Docker虚拟网络特性不匹配导致的,核心原因有三个:
- tcconfig默认调用TBF(令牌桶过滤器)做带宽整形时,自动计算的
burst(突发容限)参数过大。低带宽下大burst会允许攒积的令牌一次性放通,出现速率远高于限速值的突发流量,令牌耗尽后流量直接归零,等待令牌重新生成后再放通下一波,就会出现观测到的0~数倍限速值循环跳变的现象。 - 容器veth虚拟网卡默认
txqueuelen(发送队列长度)为1000,低带宽下长队列会引发缓冲膨胀,进一步放大流量波动。 - 单侧配置TC规则时,iperf3发送端统计的是应用层写入内核协议栈的速率,不是网卡实际发送的整形后速率,会出现发送端统计值高于设定限速的情况,属于正常统计偏差。
高带宽场景下波动不明显,是因为默认burst对应的传输时间极短(百兆以上带宽下通常小于10ms),iperf3默认1秒的统计粒度会直接平滑掉短周期波动,并非问题不存在。
优化方案
按以下顺序调整配置,可解决低带宽下的限速波动问题:
- 调小TBF的burst参数,显式指定延迟阈值
不要使用tcset自动计算的默认burst值,手动将burst设置为限速值对应10ms传输量的大小(约等于1个标准MTU到数个MTU),同时限制最大排队延迟不超过10ms,避免攒包。
以1Mbps限速为例,执行命令:tcset eth0 --rate 1mbps --burst 1500 --latency 10ms --overwrite注意:tcset的burst单位为字节,1Mbps带宽下10ms可传输1250字节,设置1500字节刚好匹配单个以太网帧大小;100Mbps限速场景下burst设置为12KB~16KB即可。
- 调小虚拟网卡的发送队列长度
配置TC规则前先修改eth0的txqueuelen,避免长队列引发缓冲膨胀:ip link set eth0 txqueuelen 10
低带宽场景下队列长度设置为5~20即可,过大会增加排队延迟,过小会导致非必要丢包。 - 替换默认排队规则为fq_codel
配置TBF作为根队列后,挂载fq_codel作为子队列做主动队列管理,自动丢弃队列中超时的数据包,消除长队列导致的锯齿波动。如果tcset支持--codel参数可直接追加,也可以直接调用原生tc命令配置:# 清空原有TC规则 tc qdisc del dev eth0 root # 配置TBF限速 tc qdisc add dev eth0 root tbf rate 1mbit burst 1500 latency 10ms # 挂载fq_codel队列管理 tc qdisc add dev eth0 parent 1:1 fq_codel - 校验生效配置
配置完成后执行tc qdisc show dev eth0,确认rate、burst、latency参数和设置值一致,避免tcconfig自动覆盖参数。
测试注意事项
- TC默认仅对网卡出方向流量生效,如果需要限制入方向(下载)流量,需要先加载ifb内核模块,将入方向流量镜像到ifb接口后再配置TC规则,否则入方向流量不会被整形。
- 首次测试的前12秒速率超标是正常现象:初始状态下令牌桶是满的,第一波流量会直接放通,等令牌存量消耗到稳定水位后速率就会回归设定值,测试时建议预留35秒预热时间再统计数据。
- iperf3测试时建议追加
-l 1400指定TCP段大小匹配MTU,避免大包分片带来的统计偏差。
内容的提问来源于stack exchange,提问作者Mellgood
相关产品推荐
相关产品推荐

