网络流量控制从未如此清晰,从内核原理到生产环境实战
引言
在日常开发和运维中,你是否遇到过这些情况?
- Git拉取代码时,整个办公室网络都被拖慢
- 数据库同步占满带宽,影响线上服务
- 需要为不同业务分配不同的网络优先级
- 防止某些IP地址滥用网络资源
所有这些问题的解决方案,都指向Linux内核中一个强大而神秘的工具------TC(Traffic Control,流量控制)。本文将深入浅出地讲解TC的原理、核心概念,并通过实际案例展示如何应用它解决各种网络流量管理问题。
一、TC原理:Linux内核流量控制的核心
1.1 TC在网络栈中的位置
TC是Linux内核实现流量控制的核心机制。要理解TC,首先要明白它在网络数据包处理流程中的位置:
接收包 → 流量限制 → 输入多路分配器 → 本机处理 或 转发处理
具体来说:
-
接收阶段:数据包从网络接口进入,TC首先进行流量限制,丢弃不符合规定的包
-
分发决策:经过输入多路分配器判断:
- 目的地址是本机 → 送往上层协议栈(TCP/UDP)
- 目的地址是其他主机 → 进入转发流程
-
发送控制:这是TC发挥作用的主要环节,对出站(egress)流量进行队列管理和整形
关键点 :TC主要控制发送(egress)流量,而非接收(ingress)流量。这是因为在发送端控制更为有效和直接。
1.2 为什么TC如此重要?
- 公平性:防止单一连接独占带宽
- 服务质量:保证关键业务的网络质量
- 带宽管理:合理分配有限的网络资源
- 流量整形:平滑突发流量,避免网络拥塞
二、TC核心概念解析
2.1 四种流量控制方式
TC提供了四种基本的流量控制方式:
| 方式 | 作用方向 | 描述 |
|---|---|---|
| SHAPING(限制) | 出向 | 平滑流量,限制峰值速率,类似"水坝" |
| SCHEDULING(调度) | 出向 | 按优先级分配带宽,类似"交通信号灯" |
| POLICING(策略) | 入向 | 检查入站流量,丢弃违规包 |
| DROPPING(丢弃) | 双向 | 超出限制时直接丢弃数据包 |
2.2 三大核心对象
TC的配置围绕三个核心对象展开,理解它们的关系至关重要:
python
qdisc 队列规则--> class 类别1 --> filter 过滤器1
| | --> filter 过滤器2
|--> C[class 类别2] --> filter 过滤器3
2.2.1 qdisc(排队规则)
qdisc是流量控制的基础,每个网络接口都必须有一个qdisc(默认为pfifo_fast)。
不可分类qdisc(简单队列):
bash
# 创建FIFO队列(基于包数量)
tc qdisc add dev eth0 root pfifo limit 100
# 创建FIFO队列(基于字节数)
tc qdisc add dev eth0 root bfifo limit 10mbit
| 类型 | 描述 | 适用场景 |
|---|---|---|
| pfifo_fast | 默认qdisc,三个优先级波段 | 一般用途 |
| sfq(随机公平队列) | 按会话(session)循环调度 | 保证多个TCP连接的公平性 |
| tbf(令牌桶过滤器) | 精确控制速率 | 需要稳定速率的场景 |
可分类qdisc(高级队列):
csharp
# HTB(分层令牌桶)- 推荐使用
tc qdisc add dev eth0 root handle 1: htb default 30
# CBQ(基于类别的排队)
tc qdisc add dev eth0 root handle 1: cbq bandwidth 100Mbit avpkt 1000
2.2.2 class(类)
在可分类qdisc中,可以创建多个class实现精细控制:
kotlin
# 创建HTB根类
tc class add dev eth0 parent 1: classid 1:1 htb rate 100Mbit
# 创建子类
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 30Mbit ceil 50Mbit
tc class add dev eth0 parent 1:1 classid 1:20 htb rate 20Mbit ceil 30Mbit
参数说明:
rate:保证带宽,必须满足ceil:最大带宽,可借用空闲带宽burst:突发流量容量
2.2.3 filter(过滤器)
过滤器决定数据包进入哪个class:
python
# 基于端口过滤
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip dport 22 0xffff flowid 1:10
# 基于IP地址过滤
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip dst 192.168.1.0/24 flowid 1:20
2.3 流量分类的三种方式
- tc filter:最灵活,支持各种匹配条件
- 服务类型(ToS) :根据IP头部的ToS字段
- skb->priority:应用程序通过socket选项设置
三、TC命令完全指南
3.1 基本命令格式
css
tc [选项] 对象 命令 设备 参数
对象类型:
qdisc:排队规则class:类别filter:过滤器
常用命令:
| 命令 | 描述 | 示例 |
|---|---|---|
add |
添加规则 | tc qdisc add dev eth0 root htb |
del |
删除规则 | tc qdisc del dev eth0 root |
change |
修改规则 | tc class change dev eth0 parent 1: classid 1:1 htb rate 50Mbit |
replace |
替换规则 | tc filter replace dev eth0 parent 1:0 protocol ip u32 match ip dst 192.168.1.1 flowid 1:10 |
show |
显示规则 | tc -s qdisc show dev eth0 |
3.2 单位换算要点
常见错误:混淆 bit(比特)和 Byte(字节)
ini
# 正确示例
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 1mbit # 1 Mbps = 125 KB/s
tc class add dev eth0 parent 1:1 classid 1:20 htb rate 1024kbit # 1024 Kbps = 128 KB/s
# 带宽单位换算
1 Gbit = 1000 Mbit = 1000000 Kbit
1 GB = 1024 MB = 1048576 KB
1 Byte = 8 bits
四、实战应用:从入门到精通
4.1 Git仓库限速方案
场景:Git拉取代码占满带宽,影响其他业务
方案一:基于端口限速(Git over SSH)
bash
#!/bin/bash
# git-limit-port.sh
# Git SSH端口(22)限速方案
DEV="eth0"
RATE="1mbit" # 限制为1 Mbps
CEIL="1.5mbit" # 最大1.5 Mbps
BURST="15k"
# 清理现有规则
tc qdisc del dev $DEV root 2>/dev/null
# 创建HTB队列
tc qdisc add dev $DEV root handle 1: htb default 999
# 创建默认类别(不限速)
tc class add dev $DEV parent 1: classid 1:999 htb rate 1000mbit ceil 1000mbit
# 创建限速类别
tc class add dev $DEV parent 1: classid 1:10 htb rate $RATE ceil $CEIL burst $BURST
# 添加SFQ防止单一连接独占
tc qdisc add dev $DEV parent 1:10 handle 10: sfq perturb 10
# 创建过滤器:Git SSH端口(22)进入限速类别
tc filter add dev $DEV protocol ip parent 1: prio 1 u32 \
match ip sport 22 0xffff flowid 1:10
# 其他所有流量走默认类别
tc filter add dev $DEV protocol ip parent 1: prio 2 u32 \
match ip dst 0.0.0.0/0 flowid 1:999
echo "Git SSH限速已设置:${RATE},保存到/etc/rc.local实现开机自启"
方案二:基于IP地址限速
bash
#!/bin/bash
# git-limit-ip.sh
# 针对特定IP的Git限速
DEV="eth0"
GIT_SERVER="github.com" # 可替换为实际Git服务器IP
LIMIT_RATE="512kbit" # 限制速率
MAX_RATE="1mbit" # 最大速率
LOCAL_NET="192.168.1.0/24" # 内网网段
# 获取Git服务器IP(如果使用域名)
GIT_IP=$(dig +short $GIT_SERVER | head -1)
[ -z "$GIT_IP" ] && GIT_IP="192.168.1.100" # 备用IP
# 清理规则
tc qdisc del dev $DEV root 2>/dev/null
# 创建HTB队列
tc qdisc add dev $DEV root handle 1: htb default 9999
# 创建根类(总带宽100Mbps)
tc class add dev $DEV parent 1: classid 1:1 htb rate 100mbit
# 创建各类别
# 1. Git服务器限速
tc class add dev $DEV parent 1:1 classid 1:10 htb rate $LIMIT_RATE ceil $MAX_RATE burst 15k
# 2. 内网不限速
tc class add dev $DEV parent 1:1 classid 1:20 htb rate 90mbit ceil 100mbit burst 15k
# 3. 其他流量
tc class add dev $DEV parent 1:1 classid 1:9999 htb rate 10mbit ceil 100mbit burst 15k
# 为每个类别添加SFQ
for classid in 10 20 9999; do
tc qdisc add dev $DEV parent 1:$classid handle ${classid}0: sfq perturb 10
done
# 设置过滤器(注意优先级prio)
# 规则1:内网不限速(最高优先级)
tc filter add dev $DEV parent 1: protocol ip prio 1 u32 \
match ip dst $LOCAL_NET flowid 1:20
# 规则2:Git服务器限速
tc filter add dev $DEV parent 1: protocol ip prio 2 u32 \
match ip dst $GIT_IP flowid 1:10
# 规则3:默认规则
tc filter add dev $DEV parent 1: protocol ip prio 3 u32 \
match ip dst 0.0.0.0/0 flowid 1:9999
echo "Git IP限速配置完成"
echo "Git服务器: $GIT_IP 限速: $LIMIT_RATE"
echo "内网网段: $LOCAL_NET 不限速"
关键技巧:
- 过滤器优先级(
prio)很重要,数字越小优先级越高 - 内网规则应该放在前面(prio值小)
- 使用
sfq防止单个连接独占带宽
4.2 生产环境多主机流量控制
场景:为不同服务器分配不同带宽
bash
#!/bin/bash
# multi-host-limit.sh
# 为不同目标主机分配不同带宽
DEV="eth0"
TOTAL_BW="10mbit"
# 目标主机及带宽配置
declare -A HOSTS=(
["192.168.1.10"]="3mbit" # 数据库服务器
["192.168.1.20"]="2mbit" # 文件服务器
["192.168.1.30"]="1mbit" # 监控服务器
)
# 清理并初始化
tc qdisc del dev $DEV root 2>/dev/null
tc qdisc add dev $DEV root handle 1: htb default 999
tc class add dev $DEV parent 1: classid 1:1 htb rate $TOTAL_BW
# 为每个主机创建类别
CLASS_ID=10
for HOST in "${!HOSTS[@]}"; do
BW=${HOSTS[$HOST]}
tc class add dev $DEV parent 1:1 classid 1:$CLASS_ID htb rate $BW ceil $BW burst 15k
tc qdisc add dev $DEV parent 1:$CLASS_ID handle ${CLASS_ID}0: sfq perturb 10
tc filter add dev $DEV parent 1: protocol ip prio 5 u32 \
match ip dst $HOST flowid 1:$CLASS_ID
((CLASS_ID++))
done
# 默认类别
tc class add dev $DEV parent 1:1 classid 1:999 htb rate 4mbit ceil 4mbit
tc qdisc add dev $DEV parent 1:999 handle 9990: sfq perturb 10
4.3 MySQL数据库同步限速
场景:数据库主从同步不影响线上业务
bash
#!/bin/bash
# mysql-sync-limit.sh
# MySQL双向同步限速方案
# 节点配置
LOCAL_IP="10.9.57.162"
PEER_IP="10.12.1.45"
LIMIT_RATE="8mbit" # 限制为8 Mbps
DEV="eth0"
# 本机限速配置
tc qdisc add dev $DEV root handle 1: htb default 1
tc class add dev $DEV parent 1: classid 1:1 htb rate 1000mbit
tc class add dev $DEV parent 1:1 classid 1:10 htb rate $LIMIT_RATE ceil $LIMIT_RATE burst 15k
tc qdisc add dev $DEV parent 1:10 handle 10: sfq perturb 10
tc filter add dev $DEV parent 1: protocol ip prio 1 u32 \
match ip dst $PEER_IP flowid 1:10
echo "MySQL同步限速配置完成:$LIMIT_RATE"
五、监控与调试
5.1 查看TC配置
python
# 查看队列状态
tc -s qdisc show dev eth0
# 查看类别状态
tc -s class show dev eth0
# 查看过滤器
tc -s filter show dev eth0
# 详细统计信息
tc -s -d qdisc show dev eth0
tc -s -d class show dev eth0
5.2 关键指标解读
csharp
# 示例输出
qdisc htb 1: root refcnt 2 r2q 10 default 1 direct_packets_stat 0 ver 3.17
Sent 123456 bytes 789 pkt (dropped 0, overlimits 0 requeues 0)
backlog 0b 0p requeues 0
- Sent:发送的数据量和包数
- dropped:丢弃的包数,非零表示有丢包
- overlimits:超过限制的次数
- backlog:队列中积压的数据
5.3 带宽测试方法
csharp
# 安装测速工具
yum install -y iperf3 # CentOS
apt-get install -y iperf3 # Ubuntu
# 服务器端
iperf3 -s
# 客户端
iperf3 -c 服务器IP -t 30 -P 10
# 简单的网络测试
speedtest-cli # 测试公网带宽
tcpping 目标IP # 测试延迟和抖动
六、高级技巧与最佳实践
6.1 动态调整带宽
bash
#!/bin/bash
# dynamic-bandwidth.sh
# 根据时间动态调整带宽
DEV="eth0"
DAY_RATE="100mbit"
NIGHT_RATE="20mbit"
adjust_bandwidth() {
HOUR=$(date +%H)
if [ $HOUR -ge 8 ] && [ $HOUR -lt 20 ]; then
RATE=$DAY_RATE
echo "工作时间:设置带宽为 $DAY_RATE"
else
RATE=$NIGHT_RATE
echo "非工作时间:设置带宽为 $NIGHT_RATE"
fi
# 调整默认类别带宽
tc class change dev $DEV parent 1: classid 1:999 htb rate $RATE ceil $RATE
# 记录日志
echo "$(date): 带宽调整为 $RATE" >> /var/log/tc-adjust.log
}
# 添加到crontab,每小时执行一次
# 0 * * * * /path/to/dynamic-bandwidth.sh
6.2 多网卡负载均衡
bash
#!/bin/bash
# multi-wan-balance.sh
# 多WAN口负载均衡与限速
WAN1="eth1"
WAN2="eth2"
TOTAL_BW="200mbit"
# 为每个WAN口设置HTB
for DEV in $WAN1 $WAN2; do
tc qdisc add dev $DEV root handle 1: htb default 1
tc class add dev $DEV parent 1: classid 1:1 htb rate $(($TOTAL_BW/2))mbit
# 为不同服务分配带宽
tc class add dev $DEV parent 1:1 classid 1:10 htb rate 20mbit ceil 30mbit # HTTP
tc class add dev $DEV parent 1:1 classid 1:20 htb rate 10mbit ceil 20mbit # SSH
tc class add dev $DEV parent 1:1 classid 1:30 htb rate 50mbit ceil 70mbit # 默认
# 添加过滤器
tc filter add dev $DEV parent 1: protocol ip prio 1 u32 \
match ip dport 80 0xffff flowid 1:10
tc filter add dev $DEV parent 1: protocol ip prio 2 u32 \
match ip dport 22 0xffff flowid 1:20
done
七、常见问题与解决方案
Q1: TC规则重启后丢失?
A: 将配置保存到启动脚本
ini
# CentOS/RHEL
echo "/path/to/tc-script.sh" >> /etc/rc.local
chmod +x /etc/rc.local
# 或者使用systemd
cat > /etc/systemd/system/tc-traffic-control.service << EOF
[Unit]
Description=Traffic Control Service
After=network.target
[Service]
Type=oneshot
ExecStart=/path/to/tc-script.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
EOF
Q2: 如何查看实时流量?
ini
# 使用iftop查看实时流量
iftop -i eth0 -n
# 使用nethogs查看进程流量
nethogs eth0
# TC监控脚本
watch -n 1 "tc -s qdisc show dev eth0; echo '---'; tc -s class show dev eth0"
Q3: 限速不生效?
- 检查单位:确保使用正确的单位(mbit vs mbyte)
- 检查设备:确保在正确的网络接口上配置
- 检查方向:TC主要控制出站(egress)流量
- 检查优先级:过滤器的prio值可能影响匹配顺序
八、总结
TC是Linux系统中强大的流量控制工具,虽然学习曲线较陡峭,但一旦掌握,就能精细控制网络流量。关键点:
- 理解架构:qdisc-class-filter三级结构
- 选对队列:HTB适合大多数场景,简单且功能强大
- 精细分类:根据业务需求创建合理的类别
- 正确过滤:使用u32或fw等过滤器精确匹配流量
- 监控调试:通过统计信息验证效果,及时调整
通过合理的TC配置,可以实现:
- 关键业务带宽保障
- 防止带宽滥用
- 提高网络稳定性
- 优化用户体验