一文搞定TC流量控制
在着手学习TC采用如下单位来描述带宽:
bash
mbps = 1024 kbps = 1024 * 1024 bps => byte/s
mbit = 1024 kbit => kilo bit/s
mb = 1024 kb = 1024 * 1024 b => byte
mbit = 1024 kbit => kilo bit
# 数值以bps和b方式储存
1Mbit = 1024 Kbit = 1024 * 1024 bps => byte/s
# bit=位,相当于一个0 或1;byte=字节,相当于一个英文字母的大小,8bit=1byte
# 带宽是10M(mbit)是指bit,我们看到的下载速度达不到带宽值,因为速率是byte/s,要10M/8(byte)
HTB(HierarchicaI Token Bucket)是一个可分类的队列, 与其他复杂的队列类型相比,HTB具有功能强大、配置简单及容易上手等优点。
Linux 操作系统中的流量控制器 TC(Traffic Control) 用于Linux内核的流量控制,它利用队列规定建立处理数据包的队列,并定义队列中的数据包被发送的方式,从而实现对流量的控制。TC 模块实现流量控制功能使用的队列规定分为两类,一类是无类队列规定,另一类是分类队列规定。无类队列规定相对简单,而分类队列规定则引出了分类和过滤器等概念,使其流量控制功能增强。
流量控制策略
Tc用于Linux内核的流量控制,流量控制包括以下几种方式:
- SHAPING(出限制) 当流量被限制,它的传输速率就被控制在某个值以下。限制值可以大大小于有效带宽,这样可以平滑突发数据流量,使网络更为稳定。shaping(限制)只适用于向外的流量。
- SCHEDULING(调度) 通过调度数据包的传输,可以在带宽范围内,按照优先级分配带宽。SCHEDULING(调度)也只适于向外的流量。
- POLICING(入策略) SHAPING用于处理向外的流量,而POLICIING(策略)用于处理接收到的数据。
- DROPPING(丢弃) 如果流量超过某个设定的带宽,就丢弃数据包,不管是向内还是向外。
控制对象
- qdisc 队列规则(queueing discipline): 用来实现控制网络的收发速度。通过队列,linux可以将网络数据包缓存起来,然后根据用户的设置,在尽量不中断连接(如 tcp)的前提下来平滑网络流量。需要注意的是,linux 对接收队列的控制不够好,所以我们一般只用发送队列,即"控发不控收"。
- Class 类 class 用来表示控制策略。我们很可能要对不同的IP实行不同的流量控制策略,这时候我们就得用不同的class来表示不同的控制策略了。
- Filter 规则 filter 用来将用户划入到具体的控制策略中。 目前,tc可以使用的过滤器有:fwmark分类器,u32 分类器,基于路由的分类器和 RSVP 分类器(分别用于IPV6、IPV4)等;其中,fwmark 分类器允许我们使用 Linux netfilter 代码选择流量,而 u32 分类器允许我们选择基于 ANY 头的流量 .需要注意的是,filter (过滤器)是在QDisc 内部,它们不能作为主体。
tc命令
在TC 中, 使用"major:minor"这样的句柄来标识队列和类别。major在一个网卡的所有队列中必须是惟一的。
对于队列来说,minor总是为0,即"major:0"
在 qdisc-class-filter
结构下,对流量进行控制需要进行三个步骤:
-
创建 qdisc 队列
-
创建 class 分类:class 实际上,就是划分流量策略分类。比如划分两档流量限速 10MBps、20MBbs。
-
创建 filter 过滤:虽然创建了 class 分类,但是并没有将任何的 IP、Port 绑定到 class 上,此时并不会有控制作用。还需要创建 filter 将指定的 IP、Port 绑定到 class 上,才能使流量控制 class 生效于资源。
添加
bash
tc qdisc [ add | change | replace | link ] dev DEV [ parent qdisc-id | root ] [ handle qdisc-id ] qdisc[ qdisc specific parameters ]
tc class [ add | change | replace ] dev DEV parent qdisc-id [ classid class-id ] qdisc [ qdisc specificparameters ]
tc filter [ add | change | replace ] dev DEV [ parent qdisc-id | root ] protocol protocol prio priorityfiltertype [ filtertype specific parameters ] flowid flow-id
查看
bash
tc [-s | -d ] qdisc show [ dev DEV ]
tc [-s | -d ] class show dev DEV tc filter show dev DEV
删除
bash
tc qdisc del dev eth0 root
tc filter
过滤器可以使用本身的 u32 也可以使用 iptables 来打上标记
bash
# 指定在 root 类 1:0 中,对 192..168.0.2 的过滤,使用 1:2 的规则,来给他 98M 的速度,写法就如下
tc filter add dev eth0 protocol ip parent 1:0 u32 match ip src 192.168.0.2 flowid 1:2
# 如果是所有 ip 写法就如
tc filter add dev eth0 protocol ip parent 1: prio 50 u32 match ip dst 0.0.0.0/0 flowid 1:10
使用 Iptables 来配合过滤器
bash
tc filter add dev eth0 parent 1: protocol ip prio 1 handle 2 fw flowid 1:2 tc filter add dev eth0 parent 1: protocol ip prio 1 handle 2 fw flowid 1:3
# iptables 只要打上记号就行了
iptables -t mangle -A POSTROUTING -d 192.168.0.2 -j MARK --set-mark 10 iptables -t mangle -A POSTROUTING -d 192.168.0.3 -j MARK --set-mark 20
查看队列状况
bash
# 简单显示指定设备(eth0)的队列状况
tc qdisc ls dev eth0
# 详细显示指定设备(eth0)的队列状况
tc -s qdisc ls dev eth0
# 查看分类的状况
tc class ls dev eth0
# 显示过滤器的状况
tc -s filter ls dev eth0
tc实践
rate: 是一个类保证得到的带宽值,如果有不只一个类,请保证所有子类总和是小于或等于父类 prio:用来指示借用带宽时的竞争力,prio越小,优先级越高,竞争力越强 ceil: ceil是一个类最大能得到的带宽值
-
限速
bash# 限制上行网速 # 创建队列 tc qdisc add dev eth0 root handle 1:0 htb default 1 # handle:为队列命名或指定某队列;默认匹配1:1 # 创建根类 tc class add dev eth0 parent 1:0 classid 1:1 htb rate 100mbps # 为eth0下的root队列1:0添加一个分类并命名为1:1,类型为htb,带宽为10M # 创建支类限速 tc class add dev eth0 parent 1:1 classid 1:5 htb rate 1500Kbit ceil 2048Kbit prio 1 # 1:5 每5秒钟检查一次 # rate 1500Kbit 代表最大带宽1536Kbit/s # ceil 2048Kbit 代表突发带宽2048Kbit/s # ceil >= rate tc filter add dev eth0 parent 1:0 prio 1 protocol ip handle 5 fw flowid 1:5 # 创建过滤器
示例
bashup tc qdisc add dev eth2 root handle 1: htb up tc class add dev eth2 parent 1:0 classid 1:1 htb rate 10gbit up tc class add dev eth2 parent 1:1 classid 1:10 htb rate 1gbit ceil 2gbit up tc filter add dev eth2 protocol all parent 1:0 prio 1 basic match 'meta(vlan mask 0xfff eq 0xBB8)' flowid 1:10
端口限速
bash#查看现有的队列 tc -s qdisc ls dev eth0 #查看现有的分类 tc -s class ls dev eth0 #创建队列 tc qdisc add dev eth0 root handle 1:0 htb default 1 #添加一个tbf队列,绑定到eth0上,命名为1:0 ,默认归类为1 #handle:为队列命名或指定某队列 #创建分类 tc class add dev eth0 parent 1:0 classid 1:1 htb rate 10Mbit burst 15k #为eth0下的root队列1:0添加一个分类并命名为1:1,类型为htb,带宽为10M #rate: 是一个类保证得到的带宽值.如果有不只一个类,请保证所有子类总和是小于或等于父类. #ceil: ceil是一个类最大能得到的带宽值. #创建一个子分类 tc class add dev eth0 parent 1:1 classid 1:10 htb rate 10Mbit ceil 10Mbit burst 15k #为1:1类规则添加一个名为1:10的类,类型为htb,带宽为10M #为了避免一个会话永占带宽,添加随即公平队列sfq. tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10 #perturb:是多少秒后重新配置一次散列算法,默认为10秒 #sfq,他可以防止一个段内的一个ip占用整个带宽 #使用u32创建过滤器 tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip sport 22 flowid 1:10
对ip进行限速
bash
#!/bin/bash
#针对不同的ip进行限速
#清空原有规则
tc qdisc del dev eth0 root
#创建根序列
tc qdisc add dev eth0 root handle 1: htb default 1
#创建一个主分类绑定所有带宽资源(20M)
tc class add dev eth0 parent 1:0 classid 1:1 htb rate 20Mbit burst 15k
#创建子分类
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 20Mbit ceil 10Mbit burst 15k
tc class add dev eth0 parent 1:1 classid 1:20 htb rate 20Mbit ceil 20Mbit burst 15k
#避免一个ip霸占带宽资源(git1有讲到)
tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10
tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10
#创建过滤器
#对所有ip限速
tc filter add dev eth0 protocol ip parent 1:0 prio 2 u32 match ip dst 0.0.0.0/0 flowid 1:10
#对内网ip放行
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip dst 12.0.0.0/8 flowid 1:20
-
延迟
bashtc qdisc add dev bond0 root netem delay 20ms tc qdisc del dev bond0 root netem delay 20ms tc qdisc add dev eth0 root netem delay 100ms 50ms # 100ms +- 50ms
-
丢包
bashtc qdisc add dev bond0 root netem loss 10% tc qdisc del dev bond0 root netem loss 10%
tc 收发策略实例
服务器egress出口方向流量担保和限速
从接口流出的流量创建流量控制规则较为容易,因为我们可以控制系统何时发送数据,但要控制何时接收数据,需要创建一个额外的中间队列来缓冲传入数据
${MANAGE_VLAN_ID}
:信令网的VLAN号,十六进制表示。echo "obase=16; 217" | bc
比如VLAN号为217,则MANAGE_VLAN_ID=0x0D9
${SPARK_VLAN_ID}
:SPARK网段的VLAN号,十六进制表示${CEPH_VLAN_ID}
:CEPH网段的VLAN号,十六进制表示
bash
# add root qdisc htb,创建一个1:的HTB队列,所有未分类的流量都将分配给类别1:30
tc qdisc add dev eth2 root handle 1: htb default 30
# 10gbit capacity,创建一个1:1的类,限速10g
tc class add dev eth2 parent 1: classid 1:1 htb rate 10gbit ceil 10gbit
# add manage class,创建一个1:10的类,限速1g - 2g
tc class add dev eth2 parent 1:1 classid 1:10 htb rate 1gbit ceil 2gbit
# add ceph&batch class,创建一个1:20的类,限速9g
tc class add dev eth2 parent 1:1 classid 1:20 htb rate 9gbit ceil 9gbit
# add sfq qdisc for manage class,为了不使一个会话永占带宽,1:10添加随即公平队列sfq.
tc qdisc add dev eth2 parent 1:10 sfq perturb 10
# add ceph class,基于1:20,创建一个1:30的类,限速8.5g - 9g
tc class add dev eth2 parent 1:20 classid 1:30 htb rate 8500mbit ceil 9gbit
# add batch class,基于1:20,创建一个1:40的类,限速0.5g - 5g
tc class add dev eth2 parent 1:20 classid 1:40 htb rate 500mbit ceil 5gbit
# add sfq qdisc for ceph class,为了不使一个会话永占带宽,1:30添加随即公平队列sfq.
tc qdisc add dev eth2 parent 1:30 sfq perturb 10
# add sfq qdisc for batch class,为了不使一个会话永占带宽,1:40添加随即公平队列sfq.
tc qdisc add dev eth2 parent 1:40 sfq perturb 10
# filter manage to 1:10,设定过滤器,匹配vlanid转发到1:10规则
tc filter add dev eth2 protocol all parent 1:0 prio 1 basic match 'meta(vlan mask 0xfff eq ${MANAGE_VLAN_ID})' flowid 1:10
# filter batch to 1:40,设定过滤器,匹配vlanid转发到1:30规则
tc filter add dev eth2 protocol all parent 1:0 prio 1 basic match 'meta(vlan mask 0xfff eq ${SPARK_VLAN_ID})' flowid 1:40
# filter ceph to 1:30,设定过滤器,匹配vlanid转发到1:40规则
tc filter add dev eth2 protocol all parent 1:0 prio 1 basic match 'meta(vlan mask 0xfff eq ${CEPH_VLAN_ID})' flowid 1:30
# same operation for eth3
tc qdisc add dev eth3 root handle 1: htb default 30
# 10gbit capacity
tc class add dev eth3 parent 1: classid 1:1 htb rate 10gbit ceil 10gbit
# add manage class
tc class add dev eth3 parent 1:1 classid 1:10 htb rate 1gbit ceil 2gbit
# add ceph&batch class
tc class add dev eth3 parent 1:1 classid 1:20 htb rate 9gbit ceil 9gbit
# add sfq qdisc for manage class
tc qdisc add dev eth3 parent 1:10 sfq perturb 10
# add ceph class
tc class add dev eth3 parent 1:20 classid 1:30 htb rate 8500mbit ceil 9gbit
# add batch class
tc class add dev eth3 parent 1:20 classid 1:40 htb rate 500mbit ceil 5gbit
# add sfq qdisc for ceph class
tc qdisc add dev eth3 parent 1:30 sfq perturb 10
# add sfq qdisc for batch class
tc qdisc add dev eth3 parent 1:40 sfq perturb 10
# filter manage to 1:10
tc filter add dev eth3 protocol all parent 1:0 prio 1 basic match 'meta(vlan mask 0xfff eq ${MANAGE_VLAN_ID})' flowid 1:10
# filter batch to 1:40
tc filter add dev eth3 protocol all parent 1:0 prio 1 basic match 'meta(vlan mask 0xfff eq ${SPARK_VLAN_ID})' flowid 1:40
# filter ceph to 1:30
tc filter add dev eth3 protocol all parent 1:0 prio 1 basic match 'meta(vlan mask 0xfff eq ${CEPH_VLAN_ID})' flowid 1:30
服务器ingress入口方向流量限速
使用tc ingress限速,也叫做ffff,似乎只能选择丢弃,并且不支持分类。实际应用中,我们可以将业务流重定向到ifb设备上,业务流从这个ifb设备中出去,再又相应的端口接收,那我们就可以像正常使用tc对egress限速一样,来对ifb设备进行egress限速,就可以达到对接收方向的限速了
${SPARK_INTERFACE}
: spark网卡名。比如bond1.206
bash
# 加载ifb
modprobe ifb numifbs=1
# set ifb0 up
ip link set dev ifb0 up
# 配置ingress qdisc
tc qdisc add dev ${SPARK_INTERFACE} handle ffff: ingress
# mirror,启用一个fb口做流入方向的重定向
tc filter add dev ${SPARK_INTERFACE} parent ffff: protocol all u32 match u32 0 0 action mirred egress redirect dev ifb0
# 配置限速
tc qdisc add dev ifb0 root handle 1: htb default 10
tc class add dev ifb0 parent 1: classid 1:1 htb rate 20gbit
tc class add dev ifb0 parent 1:1 classid 1:10 htb rate 1gbit ceil 6gbit
验证
bash
$ sudo tc qdisc show dev eth2
qdisc htb 1: root refcnt 65 r2q 10 default 0x30 direct_packets_stat 5 direct_qlen 1000
qdisc sfq 8001: parent 1:10 limit 127p quantum 1514b depth 127 divisor 1024 perturb 10sec
qdisc sfq 8003: parent 1:40 limit 127p quantum 1514b depth 127 divisor 1024 perturb 10sec
qdisc sfq 8002: parent 1:30 limit 127p quantum 1514b depth 127 divisor 1024 perturb 10sec
qdisc ingress ffff: parent ffff:fff1 ingress_block 11 ----------------
$ sudo tc class show dev eth3
class htb 1:1 root rate 10Gbit ceil 10Gbit burst 0b cburst 0b
class htb 1:10 parent 1:1 leaf 8004: prio 0 rate 1Gbit ceil 2Gbit burst 1375b cburst 1250b
class htb 1:20 parent 1:1 rate 9Gbit ceil 9Gbit burst 0b cburst 0b
class htb 1:30 parent 1:20 leaf 8005: prio 0 rate 8500Mbit ceil 9Gbit burst 0b cburst 0b
class htb 1:40 parent 1:20 leaf 8006: prio 0 rate 500Mbit ceil 5Gbit burst 1500b cburst 625b
$ sudo tc filter show dev eth2
filter parent 1: protocol all pref 1 basic chain 0
filter parent 1: protocol all pref 1 basic chain 0 handle 0x3 flowid 1:30
meta(vlan mask 0x00000fff eq 219)
filter parent 1: protocol all pref 1 basic chain 0 handle 0x2 flowid 1:40
meta(vlan mask 0x00000fff eq 216)
filter parent 1: protocol all pref 1 basic chain 0 handle 0x1 flowid 1:10
meta(vlan mask 0x00000fff eq 217)