从‘只管发包’到精准控流:深入解析tc端口带宽限制的底层逻辑与实战避坑

从‘只管发包’到精准控流:深入解析tc端口带宽限制的底层逻辑与实战避坑
1. 为什么tc限速总是不生效从只管发包说起第一次接触tc流量控制时我踩过最大的坑就是明明按照教程配置了端口限速规则测试时却发现带宽纹丝不动。折腾了一整天直到看到文档里那句tc只管发包不管收包才恍然大悟。这句话看似简单却道出了tc最核心的工作机制。想象一下邮局寄包裹的场景。tc就像邮局里的分拣员只能控制从本地邮局寄出的包裹出站流量而无法拦截别人寄来的包裹入站流量。即使这个包裹是寄给本地另一个地址的比如本地回环lo分拣员也只能在包裹离开邮局时进行处理。这就是为什么在目标机器上网卡设置限速规则毫无效果——因为包裹已经离开源邮局了。理解这一点后我们来看数据包的结构[源IP] | [源端口] | [其他数据] | [目标IP] | [目标端口]当我们在机器A上设置tc规则时实际上是在控制从机器A网卡发出的数据包。即使这个包的目标是机器A自己的另一个端口比如通过lo回环限速动作仍然发生在发包阶段。2. tc端口限速的三大核心组件2.1 队列规则qdisc流量管控的基石qdisc就像邮局的分拣流水线决定了数据包的排队和处理方式。最常用的是HTBHierarchical Token Bucket层次令牌桶它允许创建多级子队列。我常用这个命令初始化网卡队列sudo tc qdisc add dev eth0 root handle 1: htb default 12这里的default 12就像设置了一个未分类包裹的默认处理通道所有没有明确分类的数据包都会进入1:12子队列。2.2 流量类别class精细划分带宽在HTB下可以创建多个class就像给不同优先级包裹开设专用通道。比如我要给视频流、游戏和普通流量分配不同带宽# 父类总带宽100Mbps sudo tc class add dev eth0 parent 1: classid 1:1 htb rate 100mbit # 视频流子类50Mbps sudo tc class add dev eth0 parent 1:1 classid 1:10 htb rate 50mbit # 游戏子类30Mbps sudo tc class add dev eth0 parent 1:1 classid 1:11 htb rate 30mbit # 默认子类20Mbps注意总和不超过父类 sudo tc class add dev eth0 parent 1:1 classid 1:12 htb rate 20mbit2.3 过滤器filter精准路由数据包过滤器就像邮局的自动分拣机根据特征将包裹送到对应通道。端口限速的关键就在这里# 将源端口12345的流量导向1:10子类 sudo tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 \ match ip sport 12345 0xffff flowid 1:10这里有个容易误解的点sport和dport都是针对发包而言的。sport 12345指从本机12345端口发出的包而dport 12345指发往目标12345端口的包仍是从本机发出。3. 实战中的四大典型场景3.1 游戏服务器端口保障假设游戏服务使用UDP端口7777需要保障最低30Mbps带宽# 创建游戏专用class sudo tc class add dev eth0 parent 1:1 classid 1:20 htb rate 30mbit ceil 50mbit # 设置过滤器 sudo tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 \ match ip dport 7777 0xffff flowid 1:20这里用ceil参数允许突发流量当网络空闲时游戏流量最高可到50Mbps。3.2 视频流端口限速限制视频上传端口1935的带宽不超过10Mbpssudo tc class add dev eth0 parent 1:1 classid 1:30 htb rate 10mbit sudo tc filter add dev eth0 protocol ip parent 1:0 prio 2 u32 \ match ip sport 1935 0xffff flowid 1:303.3 本地回环流量控制限制本机服务间通信带宽比如微服务架构# 对lo网卡设置规则 sudo tc qdisc add dev lo root handle 1: htb sudo tc class add dev lo parent 1: classid 1:1 htb rate 100mbit sudo tc class add dev lo parent 1:1 classid 1:10 htb rate 20mbit # 限制发往本机8080端口的流量 sudo tc filter add dev lo protocol ip parent 1:0 prio 1 u32 \ match ip dport 8080 0xffff flowid 1:103.4 云服务器多租户隔离在公有云环境中可以用tc实现租户间的带宽隔离。假设eth0是物理网卡vnet1和vnet2是两个租户的虚拟网卡# 为每个虚拟网卡创建子类 sudo tc class add dev eth0 parent 1:1 classid 1:100 htb rate 50mbit sudo tc class add dev eth0 parent 1:1 classid 1:101 htb rate 30mbit # 基于源IP进行过滤 sudo tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 \ match ip src 192.168.1.100 flowid 1:100 sudo tc filter add dev eth0 parent 1:0 protocol ip prio 2 u32 \ match ip src 192.168.1.101 flowid 1:1014. 那些年我踩过的坑4.1 误用dport过滤入站流量曾经有同事试图用dport限制入站流量sudo tc filter add dev eth0 parent 1:0 u32 match ip dport 80 0xffff flowid 1:10结果发现毫无效果。这是因为dport过滤的是本机发出的、目标端口为80的流量而非入站流量。正确的做法是在发送方的网卡上设置sport规则。4.2 忘记清理旧规则有次调试时发现新规则不生效原来是旧规则残留# 先删除现有规则 sudo tc qdisc del dev eth0 root # 再添加新规则 sudo tc qdisc add dev eth0 root handle 1: htb4.3 带宽分配超出父类限制曾经配置子类时出现这种情况# 父类总带宽100Mbps sudo tc class add dev eth0 parent 1: classid 1:1 htb rate 100mbit # 子类1分配80Mbps sudo tc class add dev eth0 parent 1:1 classid 1:10 htb rate 80mbit # 子类2又分配50Mbps总和超限 sudo tc class add dev eth0 parent 1:1 classid 1:11 htb rate 50mbit这会导致带宽分配异常实际流量可能远低于预期值。4.4 容器环境下的网卡选择在Docker环境中直接对eth0设置规则可能无效因为容器流量实际通过veth设备传输。正确做法是# 先找到容器的veth设备 ip link | grep veth # 对veth设备设置规则 sudo tc qdisc add dev veth12345 root handle 1: htb5. 高级调试技巧5.1 实时监控流量分类查看各个class的实时流量统计watch -n 1 tc -s class show dev eth0输出示例class htb 1:10 root prio 0 rate 50Mbit ceil 50Mbit burst 1600b Sent 12345678 bytes 98765 pkt (dropped 0, overlimits 0 requeues 0)5.2 使用iptables联合标记对于复杂过滤条件可以结合iptables# tc设置过滤器 sudo tc filter add dev eth0 parent 1:0 protocol ip prio 1 handle 10 fw classid 1:10 # iptables标记特定流量 sudo iptables -A OUTPUT -t mangle -p tcp --sport 8080 -j MARK --set-mark 105.3 模拟带宽限制测试用iperf3测试限速效果# 服务端 iperf3 -s -p 12345 # 客户端从服务端拉数据 iperf3 -c server_ip -p 12345 -R -t 30 # 客户端向服务端推数据 iperf3 -c server_ip -p 12345 -t 305.4 动态调整带宽不需要重建整个qdisc直接修改现有classsudo tc class change dev eth0 parent 1:1 classid 1:10 htb rate 20mbit ceil 30mbit理解tc只管发包的特性后在Kubernetes集群中为不同命名空间配置网络QoS时我都是直接在Pod所在节点的veth设备上设置规则。虽然文档里没有明确说明但实测发现这种方案比在CNI插件层实现更稳定高效。