1 应用场景

tc(Traffic Control) 顾名思义主要用来做流量控制的,linux 内核支持的 Traffic Control 主要包括:流量整形(SHAPING)、流量调度(SCHEDULING)、策略(POLICING)、丢弃(DROPPING)。policing 和 dropping 主要是在入方向 ingress 做,shaping 和 scheduling 在 出方向 egress 方向做。在 linux 需要做带宽限速或 qos 的可以使用 tc 来实现。

2 Traffic Control介绍

linux 中 tc 是由 iproute2 实现的,iproute2 还包括了 ip link,ipset 等一些网络相关的实现,iproute2 使用 netlink 来实现 userspace 和 kernelspace 的交互,从而来实现以上的功能。这里有一个对 iproute2 的 golang 封装推荐一下 netlink

2.1 一个 tc htb 限速的配置例子

iptables -t mangle -I PREROUTING -s 192.168.88.0/24 -m comment --comment "xxxx" -j MARK --set-mark 0xa  # 这边的mark 0xa 跟后面 tc filter 的handle 0xa要对应上

tc qdisc add dev ppp2181 root handle 1:0 htb default 1
tc class add dev ppp2181 parent 1:0 classid 1:1 htb rate 1000Mbit ceil 1000Mbit prio 0
tc class add dev ppp2181 parent 1:1 classid 1:10 htb rate 4Mbit ceil 8Mbit prio 1 burst 96kbit
tc qdisc add dev ppp2181 parent 1:10 handle 10: sfq perturb 10
tc filter add dev ppp2181 parent 1:0 protocol ip prio 1 handle 0xa fw classid 1:10  # classid 1:10 不一定要对应 iptable mangle 的 mark,关键是handle 0xa 对应即可

这个例子实现的功能是对源IP为 192.168.88.0/24 的流量限速为基础速率为 4Mbit(可突破到8Mbit)。大概的实现流程是使用 iptables 对源IP为 192.168.88.0/24 的流量打 mark, 然后 tc 根据 iptables 的这个 mark 将流量 filter 到某个 class, 在这个 class 里面配置限速的策略。

2.2 tc 简介

tc 主要是通过排队的方式,通过制定不同的排队策略(对应不同的算法比如 CBQ、HTB)来对要发送的包进行整形限速。所以 tc 的策略是针对出方向egress的。要对入方向ingress进行限速,理论上是需要发送方进行配合的,但是我们可通过一些变通的方式来实现ingress的限速。

从上面的例子我们看到 tc 的实现主要通过 qdisc、class、filter 来实现,qdisc、class、filter 组件形成了一个树形结构来对这个限速策略进行描述。
先来了解一下这几个术语:

  • qdisc(Queueing Discipline)排队规则,管理设备队列的算法。
  • class 可以简单的理解为对要限速的流量进行分类,比如我们将不同的进程进行分类,分成不同的 class,然后每个 class 里面配置对应的限速策略
  • filter 过滤器,分类过程可以通过过滤器(filter)完成。过滤器包含许多的判 断条件,匹配到条件之后就算 filter 匹配成功了。
  • handle,每个 qdisc 和 class 都会分配一个相应的 handle(句柄),可以指定 handle 对 qdisc 进行配置。有了 handle 我们可以将 qdisc、class 组成一个树形的结构。每个 handle 由两部分组成,:,major,minor取值范围是0-65535,书写规范上协作16进制。按照惯例,root qdisc 的 handle 为 1:,这是 1:0 的简写,每个 qdisc 的 minor number 永远是 0。
                     1:   root qdisc
                      |
                     1:1    child class
                   /  |  \
                  /   |   \
                 /    |    \
                 /    |    \
              1:10  1:11  1:12   child classes
               |      |     |
               |     11:    |    leaf class
               |            |
               10:         12:   qdisc
              /   \       /   \
           10:1  10:2   12:1  12:2   leaf classes

那么 tc 的策略是在作用在包流转的哪个环节呢,从下面这张经典的 netfilter package flow 可以看出 tc 的 ingress qdisc 是作用在 prerouting 之前,egress qdisc 作用在 postrouting 之后。在多网卡的情况下,每个网卡都有自己的 ingress 和 egress hooks。

在这里插入图片描述

2.3 qdisc

qdisc 有 classless qdisc 和 class qdisc 两种。从字面上来理解 classless qdisc 是指无类别的排队规则,即不通过 tc 配置来对流量进行分类,而是使用 qdisc 预定义的规则来进行分类排队,比如 pfifo_fast qdisc 。 class qdisc 则指可以通过 tc 配置来将流经接口的流量进行分类,然后对不同的类运用对应的规则。通过 man tc 可以看到 tc 支持非常多中算法的 qdisc,我们各挑一个比较经典的来介绍一下。

2.3.1 pfifo_fast qdisc

pfifo_fast qdisc 是大部分 linux 网口默认的配置,是一个先入先出队列,对所有包都一视同仁。

在这里插入图片描述

pfifo_fast 有三个所谓的 “band”(可理解为三个队列),编号分别为 0、1、2:

  • 每个 band 上分别执行 FIFO 规则。
  • 如果 band 0 有数据,就不会处理 band 1;同理,band 1 有数据时, 不会去处理 band 2。
  • 内核会检查数据包的 TOS 字段,将“最小延迟”的包放到 band 0

所以配置了 pfifo_fast 的网口怎么确定哪个包要进入那个 band 呢?并不是随机的,而是由 TOS(Type of Service) 决定的。TOS 是 IP 报文头里面约定的,TOS 的值和 band 0-2 的映射关系由 priomap 来决定

TOS     Bits  Means                    Linux Priority    Band
  ------------------------------------------------------------
  0x0     0     Normal Service           0 Best Effort     1
  0x2     1     Minimize Monetary Cost   1 Filler          2
  0x4     2     Maximize Reliability     0 Best Effort     1
  0x6     3     mmc+mr                   0 Best Effort     1
  0x8     4     Maximize Throughput      2 Bulk            2
  0xa     5     mmc+mt                   2 Bulk            2
  0xc     6     mr+mt                    2 Bulk            2
  0xe     7     mmc+mr+mt                2 Bulk            2
  0x10    8     Minimize Delay           6 Interactive     0
  0x12    9     mmc+md                   6 Interactive     0
  0x14    10    mr+md                    6 Interactive     0
  0x16    11    mmc+mr+md                6 Interactive     0
  0x18    12    mt+md                    4 Int. Bulk       1
  0x1a    13    mmc+mt+md                4 Int. Bulk       1
  0x1c    14    mr+mt+md                 4 Int. Bulk       1
  0x1e    15    mmc+mr+mt+md             4 Int. Bulk       1
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |Version|  IHL  |Type of Service|          Total Length         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         Identification        |Flags|      Fragment Offset    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Time to Live |    Protocol   |         Header Checksum       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                       Source Address                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Destination Address                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                    Example Internet Datagram Header
tc -s qdisc show dev eth0 # 查看 eth0 网口配置的 qdisc,priomap为 TOS 映射到 band 的规则
qdisc pfifo_fast 0: root refcnt 2 bands 3 priomap  1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
 Sent 26183696597 bytes 201000920 pkt (dropped 0, overlimits 0 requeues 0)
 rate 0bit 0pps backlog 0b 0p requeues 0

2.3.2 HTB(Hierarchical Token Bucket)

HTB是比较推荐的 linux 上的限速策略,能够实现以下几个功能:

  • 有一个固定总带宽,想将其分割成几个部分,分别用作不同目的
  • 每个部分的带宽是有保证的(guaranteed bandwidth)
  • 还可以指定每个部分向其他部分借带宽

借助 Token 来实现流量整形大概的原理是,按照一定的速率来生成 token(这样 token 的数量就跟速率相关),然后给每个包分配 token,有 token 的包可以排队出队,从而达到限速的效果。HTB 是一个分层级令牌桶,从而实现分层的限速效果。比如给网口限制一个总的限速策略,然后将流量按照一定规则分成不同 class,对每个 class 分别配置 保证带宽和可以像其他部分借的带宽(通过 htb 的 rate 和 ceil 参数来配置)。

在这里插入图片描述

一个具体的配置实例:

                    root 1:
                      |
                    _1:1_  # htb rate 1000Mbit ceil 1000Mbit
                   /     \
                  /       \
                 /         \
               10:         11:  # handle 10:,11: 对应两个 class 的handle
tc qdisc add dev ppp2181 root handle 1:0 htb default 1 # root handle
tc class add dev ppp2181 parent 1:0 classid 1:1 htb rate 1000Mbit ceil 1000Mbit prio 0  # root handle 的限速策略,配置成网卡带宽
tc class add dev ppp2181 parent 1:1 classid 1:10 htb rate 4Mbit ceil 8Mbit prio 1 burst 96kbit # classid 1:10 这个类的限速策略配置
tc qdisc add dev ppp2181 parent 1:10 handle 10: sfq perturb 10
tc class add dev ppp2181 parent 1:1 classid 1:11 htb rate 6Mbit ceil 7Mbit prio 1 burst 96kbit # classid 1:11 这个类的限速策略配置
tc filter add dev ppp2181 parent 1:0 protocol ip prio 1 handle 0xa fw classid 1:10  # classid 1:10 不一定要对应 iptable mangle 的 mark,关键是handle 0xa。filter将包根据过滤规则分到了对应的 class
tc filter add dev ppp2181 parent 1:0 protocol ip prio 1 handle 0xb fw classid 1:11  # classid 1:11 

2.4 filter

filter 在 tc 内负责将包分类到对应的 class,tc 支持非常丰富的 filter 来对包进行分类。以下简要说明几个常用的 filter

  • u32,u32非常强大,可以根据五元组(源ip,源端口,协议,目的ip,目的端口)等信息来过滤分类数据包
  • bpf 可以根据 eBPF 的信息来过滤数据包
  • cgroup 根据 cgroup id 来过滤包,这个就能够将 docker 配置的 cgroup id 跟 tc结合起来,对容器进行限速
  • fw 根据 fwmark 来进行过滤,这样能够跟 iptables 结合来实现丰富的过滤策略。

2.5 ingress 流量整形

tc 的流量整形是针对 egress 方向的,如果要对 ingress 方向流量做整形,比较常见的解决方案是使用 IFB(Intermediate Functional Block Device)或 IMQ(Intermediate Queueing Device)。 IFB作为IMQ的替代品,实现更简单。可以将其他接口的流量重定向到这个接口,然后对 IFB 的接口做限速从而实现对原网口的 ingress 限速。

启用所有ifb接口:

ip link add ifbxxx type ifb
ip link set dev ifbxxx up

并将入口流量从物理接口重定向到相应的ifb接口。对于ppp2181-> ifbxxx:

tc qdisc add dev ppp2181 handle ffff: ingress
tc filter add dev ppp2181 parent ffff: protocol ip u32 match u32 0 0 action mirred egress redirect dev ifbxxx

2.6 cgroup+tc 实现对容器的限速

从上面的介绍已经清楚了 tc 的限速配置,要将容器和 tc 策略关联起来,需要首先给容器的 cgroup net 配置一个 cgroup id(classid),然后在 tc filter 将这个 cgroup id(classid)和某个 class 关联起来。

cd /sys/fs/cgroup/net_cls/docker/[containerID]
echo 0x1000b > net_cls.classid # 给容器的 cgroup net 配置一个 cgroup id(classid)


tc qdisc add dev ppp2181 root handle 1:0 htb default 1
tc class add dev ppp2181 parent 1:0 classid 1:1 htb rate 1000Mbit ceil 1000Mbit prio 0
tc class add dev ppp2181 parent 1:1 classid 1:b htb rate 4Mbit ceil 4Mbit prio 1 burst 96kbit
tc qdisc add dev ppp2181 parent 1:b handle b: sfq perturb 10
tc filter add dev ppp2181 parent 1: protocol ip handle 0xb: cgroup

3 参考资料

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐