一文搞定TC流量控制

一文搞定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具有功能强大、配置简单及容易上手等优点。

man7.org/linux/man-p...

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 结构下,对流量进行控制需要进行三个步骤:

  1. 创建 qdisc 队列

  2. 创建 class 分类:class 实际上,就是划分流量策略分类。比如划分两档流量限速 10MBps、20MBbs。

  3. 创建 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 # 创建过滤器

    示例

    bash 复制代码
    up 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
  • 延迟

    bash 复制代码
    tc 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
  • 丢包

    bash 复制代码
    tc 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)
相关推荐
云飞云共享云桌面26 分钟前
8位机械工程师如何共享一台图形工作站算力?
linux·服务器·网络
Peter_chq1 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
一坨阿亮2 小时前
Linux 使用中的问题
linux·运维
dsywws3 小时前
Linux学习笔记之vim入门
linux·笔记·学习
幺零九零零4 小时前
【C++】socket套接字编程
linux·服务器·网络·c++
小林熬夜学编程5 小时前
【Linux系统编程】第四十一弹---线程深度解析:从地址空间到多线程实践
linux·c语言·开发语言·c++·算法
程思扬6 小时前
为什么Uptime+Kuma本地部署与远程使用是网站监控新选择?
linux·服务器·网络·经验分享·后端·网络协议·1024程序员节
sun0077006 小时前
拷贝 cp -rdp 和 cp -a
linux·运维·服务器
wowocpp6 小时前
ubuntu 22.04 server 安装 anaconda3
linux·运维·ubuntu
乡村农夫6 小时前
cuda 环境搭建
linux