linux下使用 TC 对服务器进行流量控制
在linux中,tc 有二种控制方法 CBQ 和 HTB.HTB 是设计用来替换 CBQ 的.HTB比CBQ更加灵活,但是CPU 开销也更大,通常高速的链路会使用CBQ,一般而言HTB使用的更加广泛。HTB 的规则本质上是一个树形结构,包括三个基本的构成块:队列规定 qdisc(queueing discipline) ,类(class)和分类器(Classifiers).
用来实现控制网络的收发速度.通过队列,linux可以将网络数据包缓存起来,然后根据用户的设置,在尽量不中断连接(如 tcp)的前提下来平滑网络流量.需要注意的是,linux 对接收队列的控制不够好,所以我们一般只用发送队列,即"控发不控收".它封装了其他两个主要 tc 组件(类和分类器).内核如果需要通过某个网络接口发送数据包,它都需要按照为这个接口配置的 qdisc 队列规则把数据包加入队列.然后,内核会尽可能多地从 qdisc里面取出数据包,把它们交给网络适配器驱动模块.
最简单的 QDisc 是 pfifo 它不对进入的数据包做任何的处理,数据包采用先入先出的方式通过队列.不过,它会保存网络接口一时无法处理的数据包.常有的队列规则包括 FIFO 先进先出,RED 随机早期探测,SFQ 随机公平队列和令牌桶 Token Bucket,类基队列 CBQ,CBQ 是一种超级队列,即它能够包含其它队列,甚至其它 CBQ.
class 用来表示控制策略.很显然,很多时候,我们很可能要对不同的IP实行不同的流量控制策略,这时候我们就得用不同的class来表示不同的控制策略了.
filter 用来将用户划入到具体的控制策略中(即不同的 class 中).比如,现在,我们想对xxa,xxb两个IP实行不同的控制策略(A,B),这时,我们可用 filter 将 xxa 划入到控制策略 A,将 xxb 划入到控制策略 B,filter 划分的标志位可用 u32 打标功能或 IPtables 的 set-mark (大多使用iptables 来做标记)功能来实现.
目前,tc可以使用的过滤器有:fwmark分类器,u32 分类器,基于路由的分类器和 RSVP 分类器(分别用于IPV6、IPV4)等;其中,fwmark 分类器允许我们使用 Linux netfilter 代码选择流量,而 u32 分类器允许我们选择基于 ANY 头的流量 .需要注意的是,filter (过滤器)是在QDisc 内部,它们不能作为主体.
如果将HTB的分层结构看作树,那么每个节点就被称为一个 class,每个class可以设置一个 qdisc,默认的是 tc-pfifo. 另外HTB还可以设置一些过滤器,通过这些过滤器可以将到来的包分发到指定的 class 上。这里过滤器通常挂载在 root 节点(eth0接口),但匹配只能匹配叶子节点。
数据包->iptables(在通过iptables时,iptables根据不同的ip来设置不同的mark)->tc(class)->tc(queue)
应用
假设 eth0 位是服务器的外网网络接口.开始之前,先要清除 eth0所有队列规则
1) 定义最顶层(根)队列规则,并指定 default 类别编号
tc qdisc add dev eth0 root handle 1: htb default 2
2) 定义第一层的 1:1 类别 (速度)
本来是要多定义第二层叶类别,但目前来看,这个应用中就可以了.
tc class add dev eth0 parent 1:1 classid 1:3 htb rate 1mbit ceil 2mbit prio 2
注:以上就是我们控制输出服务器的速度,一个为98M,一个为 2M.
rate: 是一个类保证得到的带宽值.如果有不只一个类,请保证所有子类总和是小于或等于父类.
prio:用来指示借用带宽时的竞争力,prio越小,优先级越高,竞争力越强.
ceil: ceil是一个类最大能得到的带宽值.
同时为了不使一个会话永占带宽,添加随即公平队列sfq.
tc qdisc add dev eth0 parent 1:2 handle 2: sfq perturb 10tc qdisc add dev eth0 parent 1:3 handle 3: sfq perturb 10
3) 设定过滤器
过滤器可以使用本身的 u32 也可以使用 iptables 来打上标记
指定在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.1 flowid 1:3
如果是所有 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 来配合过滤器
还可以使用这个方法,但需要借助下面的 iptables 的命令来做标记了
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 10iptables -t mangle -A POSTROUTING -d 192.168.0.3 -j MARK --set-mark 20
tc对高速度的控制
Rate ceiling 速率限度
参数ceil指定了一个类可以用的最大带宽, 用来限制类可以借用多少带宽.缺省的ceil是和速率一样,这个特性对于ISP是很有用的, 因为他们一般限制被服务的用户的总量即使其他用户没有请求服务.(ISPS 很想用户付更多的钱得到更好的服务) ,注根类是不允许被借用的, 所以没有指定ceil。
注: ceil的数值应该至少和它所在的类的速率一样高, 也就是说ceil应该至少和它的任何一个子类一样高
Burst 突发
网络硬件只能在一个时间发送一个包这仅仅取决于一个硬件的速率. 链路共享软件可以利用这个能力动态产生多个连接运行在不同的速度. 所以速率和ceil不是一个即时度量只是一个在一个时间里发送包的平均值. 实际的情况是怎样使一个流量很小的类在某个时间类以最大的速率提供给其他类. burst 和cburst 参数控制多少数据可以以硬件最大的速度不费力的发送给需要的其他类.
如果cburst 小于一个理论上的数据包他形成的突发不会超过ceil 速率, 同样的方法TBF的最高速率也是这样.
你可能会问, 为什么需要bursts . 因为它可以很容易的提高向应速度在一个很拥挤的链路上. 比如WWW 流量是突发的. 你访问主页. 突发的获得并阅读. 在空闲的时间burst将再"charge"一次.
注: burst 和cburst至少要和其子类的值一样大.
tc命令格式:
tc qdisc [ add | change | replace | link ] dev DEV [ parent qdisc-id | root ] [ handle qdisc-id ] qdisc [ qdisc specific parameters ]
Qdisc的参数:
parent major:minor 或者 root。 一个qdisc是根节点就是root,否则其他的情况指定parent。其中major:minor是class的handle id,每个class都要指定一个id用于标识。
handle major: ,这个语法有点奇怪,是可选的,如果qdisc下面还要分类(多个class),则需要指定这个hanlde。对于root,通常是"1:"。
注意:对于tc命令中的qdiscs和classes,标识handle(classid)的语法都是x:y,其中x是一个整数用来标识一个 qdisc,y是一个整数,用来标识属于该qdisc的class。qdisc的handle的y值必须是0,class的handle的y值必须是非 0。通常"1:0"简写为"1:",也就是上面看到的写法。
default minor-id,未分类(不能和filter匹配)的流量(默认的)会被送到这个minor所指定的类(class id为major:minor-id)。
tc class ... dev dev parent major:[minor] [ classid major:minor ] htb rate rate [ ceil rate ] burst bytes [ cburst bytes ] [ prio priority ]Class的参数:
parent major:minor,指定这个类的父节点,父节点可以是Qdisc,也可以是Class,如果是Qdisc,那么就不用指定minor,这个是必须的参数。
classid major:minor,classid作为class的标识,这个是可选的。如果这个class没有子节点,就可以不指定。major是父qdisc的handle。
prio 低优先级的class会优先匹配
rate 这个class和其所有子类的速率
ceil 如果父类有空余带宽,最高可以分配给当前class的值,默认是和rate一样。
burst 允许以ceil的速率发送的字节数,应该至少和子类的burst最大值一样。
cburst 允许以网口的最高速率发送的字节数,应该至少和子类的cburst最大值一样。功能类似tbf中的peakrate,当这个值限制很小时,可以避免突发的流量,以避免瞬间速率超过ceil。
quantum 每轮当前的class能发送的字节数,默认的计算quantum = rate / r2q. Quantum必须大于1500 小于 60000。quantum只在class的流量超过了rate但是没超过ceil时使用。quantum越小,带宽共享的效果就越好。 r2q 用来计算quantum,r2q默认是10。
tc filter [ add | change | replace ] dev DEV [ parent qdisc-id | root ] protocol protocol prio priority filtertype [ filtertype specific parameters ] flowid flow-id
显示
tc [-s | -d ] qdisc show [ dev DEV ]
tc [-s | -d ] class show dev DEV tc filter show dev DEV
查看tc的状态
tc -s -d qdisc show dev eth0
tc -s -d class show dev eth0
删除tc规则
tc qdisc del dev eth0 root
实例
使用 tc 对单个IP 进行速度控制
tc qdisc add dev eth0 root handle 1: htb r2q 1tc class add dev eth0 parent 1: classid 1:1 htb rate 30mbit ceil 60mbit
tc filter add dev eth0 parent 1: protocol ip prio 16 u32 match ip dst 192.168.1.2 flowid 1:1
就可以限制192.168.1.2的下载速度为30Mbit最高可以60Mbit ,其中 r2q,是指没有default的root,使整个网络的带宽没有限制
使用 tc 对整段 IP 进行速度控制
tc qdisc add dev eth0 root handle 1: htb r2q 1tc class add dev eth0 parent 1: classid 1:1 htb rate 50mbit ceil 1000mbit
tc filter add dev eth0 parent 1: protocol ip prio 16 u32 match ip dst 192.168.111.0/24 flowid 1:1
就可以限制192.168.111.0 到255 的带宽为3000k了,实际下载速度为200k左右.
这种情况下,这个网段所有机器共享这200k的带宽.
还可以加入一个sfq(随机公平队列)
tc class add dev eth0 parent 1: classid 1:1 htb rate 3000kbit burst 10k
tc qdisc add dev eth0 parent 1:1 handle 10: sfq perturb 10
tc filter add dev eth0 parent 1: protocol ip prio 16 u32 match ip dst 192.168.111.168 flowid 1:1
sfq,他可以防止一个段内的一个ip占用整个带宽.
使用 tc 控制服务器对外的速度为 10M
如下,我要管理一台服务器,只能向外发 10M 的数据
tc qdisc del dev eth0 roottc qdisc add dev eth0 root handle 1: htb
tc class add dev eth0 parent 1: classid 1:1 htb rate 100mbit ceil 100mbit
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 10mbit ceil 10mbit
tc qdisc add dev eth0 parent 1:10 sfq perturb 10
tc filter add dev eth0 protocol ip parent 1: prio 2 u32 match ip dst 220.181.xxx.xx/32 flowid 1:1 # 上面这台,让 220.181.xxx.xx/32 这台跑默认的,主要是为了让这个 ip 连接进来不被控制
tc filter add dev eth0 protocol ip parent 1: prio 50 u32 match ip dst 0.0.0.0/0 flowid 1:10 # 默认让所有的流量都从这个通过
参考:http://blog.chinaunix.net/u3/94771/showart_1906064.html
http://liuleijsjx.javaeye.com/blog/402152
文章摘自:http://www.php-oa.com/2009/06/23/linux_tc.html