Skip to content

实验 17:QoS 与限速 ​

OVS 提供两套限速机制,方向不同、原理不同、效果差别很大:

ingress policingQoS + Queue
方向入向(进入交换机)出向(离开交换机)
原理令牌桶,超了就丢分层令牌桶,超了就排队
配在哪Interface 表Port → QoS → Queue
能分级不能能(多个队列)
配置难度一条命令需要三张表
TCP 实测速率明显低于配置值接近配置值

最后一行是这一章最值得记住的结论,下面会用实测数据说明。

起环境 ​

bash
cd labs/learn-ovs/14-qos
./start.sh
text
   h1 ── ovs1 ── h2
   .1     br0     .2
                  iperf3 -s

start.sh 会先测一个基线:

text
[  5]  0.00-3.00 sec  3.22 GBytes  9224 Mbits/sec  receiver

9224 Mbit/s —— 这是容器之间 veth 的裸速度,后面所有限速效果都跟它对比。

方案一:ingress policing ​

最简单的限速,一条命令:

bash
N="docker exec clab-qos-ovs1"

$N ovs-vsctl set Interface eth1 ingress_policing_rate=20000
$N ovs-vsctl set Interface eth1 ingress_policing_burst=2000
字段单位含义
ingress_policing_ratekbit/s平均速率上限。20000 = 20 Mbit/s
ingress_policing_burstkbit允许的突发量。一般设成 rate 的 10%
ingress_policing_kpkts_ratekpps按包速率限(可选)

单位是 kbit/s,不是 bit/s

ingress_policing_rate=20000 是 20 Mbit/s。写成 20000000 会变成 20 Gbit/s,等于没限速 —— 这是最常见的配置错误。

注意 Queue 表的 max-rate 单位是 bit/s,两处不一样,很容易搞混。

实测:

bash
docker exec clab-qos-h1 iperf3 -c 192.168.10.2 -t 5 -f m
text
[  5]  0.00-5.00 sec  ...  11.9 Mbits/sec  receiver

配置 20 Mbit/s,实测只有 11.9 Mbit/s —— 差了四成。

为什么打不满 ​

policing 的做法是超速就丢包。TCP 一遇到丢包就认为网络拥塞,立刻把发送窗口砍半,然后慢慢爬升。爬到接近限速值又开始丢包,再砍半 —— 于是平均吞吐远低于配置值。

text
   带宽
    │      ╱╲      ╱╲      ╱╲          ← 每次撞到限速就丢包
 20 ├─ ─ ─╱─ ╲─ ─ ╱─ ╲─ ─ ╱─ ╲─ ─      ← 配置的 20 Mbit/s
    │    ╱    ╲  ╱    ╲  ╱    ╲
 12 ├ ─ ╱ ─ ─ ─╲╱ ─ ─ ─ ╲╱ ─ ─ ─╲─ ─   ← 实际平均只有 12
    │  ╱                          
    └──────────────────────────────▶ 时间

policing 适合限制不守规矩的流量(比如防 UDP 洪泛),不适合给 TCP 业务做带宽保证。

方案二:QoS + Queue ​

出向限速,用 Linux 的 HTB(分层令牌桶)。它涉及三张 OVSDB 表:

text
   Port(eth2)
     └─ qos ──▶ QoS (type=linux-htb, max-rate=100M)
                  ├─ queues:0 ──▶ Queue (max-rate=10M)
                  └─ queues:1 ──▶ Queue (max-rate=50M)

配置命令(和实验 12 的 Mirror 一样要用 @ 符号):

bash
$N ovs-vsctl \
  -- set Port eth2 qos=@newqos \
  -- --id=@q0 create Queue other-config:max-rate=10000000 \
  -- --id=@q1 create Queue other-config:max-rate=50000000 \
  -- --id=@newqos create QoS type=linux-htb \
       other-config:max-rate=100000000 \
       queues:0=@q0 queues:1=@q1

Queue 的单位是 bit/s

max-rate=10000000 是 10 Mbit/s(10 的 7 次方 bit/s)。

和 ingress_policing_rate 的 kbit/s 不一样。配之前先确认单位。

常用的 Queue 参数:

参数含义
max-rate速率上限(bit/s)
min-rate保证的最低速率。总和不能超过 QoS 的 max-rate
priority优先级,数字小的先发
burst突发量

用流表选队列 ​

配好队列还不够 —— 得告诉 OVS 哪些流量走哪个队列:

bash
$N ovs-ofctl add-flow br0 \
  "priority=100,ip,nw_src=192.168.10.1,actions=set_queue:1,NORMAL"

set_queue:N 决定这个包用几号队列。不设的话走 0 号队列(也叫 Default 队列)。

这是 QoS 真正强大的地方:队列的选择可以基于任意流表匹配条件 —— 源 IP、目的端口、VLAN、DSCP,什么都行。

实测 ​

先把 ingress policing 关掉,只留 HTB:

bash
$N ovs-vsctl set Interface eth1 ingress_policing_rate=0 ingress_policing_burst=0

走 queue 1(上限 50 Mbit/s):

text
[  5]  0.00-5.02 sec  29.8 MBytes  49.7 Mbits/sec  receiver

改成走 queue 0(上限 10 Mbit/s):

bash
$N ovs-ofctl mod-flows br0 \
  "priority=100,ip,nw_src=192.168.10.1,actions=set_queue:0,NORMAL"
text
[  5]  0.00-5.13 sec  6.00 MBytes  9.81 Mbits/sec  receiver

对比一下三组数据:

机制配置值实测达成率
ingress policing20 Mbit/s11.960%
HTB queue 150 Mbit/s49.799%
HTB queue 010 Mbit/s9.8198%

shaping(排队)几乎精准命中,policing(丢包)大幅低于配置值。

因为 HTB 超速时是把包排进队列慢慢发,不丢包。TCP 感知到的是延迟增加而不是丢包,不会触发拥塞退避,所以能稳定跑在限速值附近。

结论

  • 要给业务做带宽保证 → 用 QoS + Queue(shaping)
  • 要压制滥用流量 → 用 ingress policing(policing)

给 TCP 业务配 policing 然后抱怨"限速 100M 只跑到 60M",是很常见的误用。

看队列统计 ​

bash
$N ovs-appctl qos/show eth2
text
QoS: eth2 linux-htb
max-rate: 100000000

Default:
  burst: 76512
  min-rate: 76000
  max-rate: 10000000
  tx_packets: 3
  tx_bytes: 210

Queue 1:
  burst: 76512
  min-rate: 76000
  max-rate: 50000000
  tx_packets: 825
  tx_bytes: 7622925

tx_packets 直接证明 set_queue 生效了:825 个包走了 Queue 1,Default 只有 3 个(那是没被流表规则匹配到的零散流量)。

排查"限速没生效"时,先看这里 —— 如果流量都进了 Default 队列,说明 set_queue 的流表规则没命中。

min-rate:带宽保证 ​

max-rate 是上限,min-rate 是保证值:

bash
-- --id=@q0 create Queue other-config:min-rate=30000000 \
                          other-config:max-rate=100000000
-- --id=@q1 create Queue other-config:min-rate=70000000 \
                          other-config:max-rate=100000000

含义是:

  • 链路空闲时,任一队列都能跑到 100 Mbit/s
  • 链路拥塞时,queue 0 至少能拿到 30 Mbit/s,queue 1 至少 70 Mbit/s

所有队列的 min-rate 之和不能超过 QoS 的 max-rate,否则保证无法兑现,OVS 会按比例缩减。

这是"共享带宽但保底"的标准做法,比单纯设上限灵活得多。

Meter:OpenFlow 标准的限速 ​

除了上面两种 OVS 特有的机制,OpenFlow 1.3 还定义了 Meter:

bash
# 建一个 meter:超过 10000 kbit/s 就丢
$N ovs-ofctl -O OpenFlow13 add-meter br0 \
  "meter=1,kbps,band=type=drop,rate=10000"

# 流表里引用它
$N ovs-ofctl -O OpenFlow13 add-flow br0 \
  "priority=100,ip,actions=meter:1,NORMAL"

# 看统计
$N ovs-ofctl -O OpenFlow13 meter-stats br0

Meter 的优势是标准化,控制器可以跨厂商下发。但它的 band 类型只有 drop 和 dscp_remark,本质上还是 policing,同样有 TCP 打不满的问题。

需要 shaping 就用 QoS + Queue。

清理 ​

bash
$N ovs-vsctl clear Port eth2 qos
$N ovs-vsctl set Interface eth1 ingress_policing_rate=0 ingress_policing_burst=0

QoS 和 Queue 记录不会自动删干净

clear Port eth2 qos 只解除引用。QoS 和 Queue 是强引用表,记录会留下来变成孤儿:

bash
$N ovs-vsctl list QoS      # 还在
$N ovs-vsctl list Queue    # 还在

彻底清理要用:

bash
$N ovs-vsctl -- --all destroy QoS -- --all destroy Queue

长期反复配 QoS 的机器上,孤儿记录会越积越多。

自动检查 ​

bash
./verify.sh
text
  [通过] eth1 配了 ingress policing
  [通过] eth2 挂了 QoS
  [通过] 存在 set_queue 的流表规则
  [通过] 限速后仍然连通

== 实测限速效果(h1 → h2,入向限 20 Mbit/s)==
  实测接收速率: 12.2 Mbit/s
  [通过] 速率被限制在 30 Mbit/s 以下

自测清单 ​

  1. ingress policing 和 QoS+Queue 分别作用在哪个方向?
  2. ingress_policing_rate 和 Queue.max-rate 的单位分别是什么?
  3. 为什么 policing 下 TCP 跑不满配置的速率?
  4. 配好队列之后还需要做什么,流量才会进指定队列?
  5. 不设 set_queue 的流量走哪个队列?
  6. max-rate 和 min-rate 有什么区别?
  7. 排查"限速没生效"该先看什么?

常见问题 ​

限速完全没效果 ​

按顺序查:

bash
# 1. 单位对吗(kbit/s vs bit/s)
ovs-vsctl get Interface eth1 ingress_policing_rate

# 2. QoS 挂到 Port 上了吗
ovs-vsctl get Port eth2 qos

# 3. 流量进了哪个队列
ovs-appctl qos/show eth2

第 3 步最有用:如果 tx_packets 全在 Default 队列,说明 set_queue 的流表规则没命中。

限速生效了但达不到配置值 ​

用的是 policing。TCP 场景下这是预期行为,换成 QoS + Queue。

限速方向搞反了 ​

ingress_policing_* 限的是进入交换机的流量,配在流量源头那个接口上。 QoS 限的是离开交换机的流量,配在流量出口那个接口上。

想限制 h1 的上传:配 eth1 的 ingress policing,或者配 eth2 的 QoS。

报错 constraint violation ​

min-rate 之和超过了 QoS 的 max-rate。调小 min-rate 或调大总的 max-rate。

list QoS 里有一堆用不到的记录 ​

孤儿记录。清理:

bash
ovs-vsctl -- --all destroy QoS -- --all destroy Queue

注意这会删掉所有 QoS 配置,生产环境上先确认没有正在用的。

小结 ​

  • 两套机制方向相反:ingress_policing 管入向,QoS + Queue 管出向
  • 单位不同:policing 是 kbit/s,Queue 的 max-rate 是 bit/s
  • policing 靠丢包,TCP 会退避,实测明显低于配置值(实测 20M 只跑到 12M)
  • shaping 靠排队,实测接近配置值(50M 跑到 49.7M,10M 跑到 9.81M)
  • 给业务做带宽保证用 shaping,压制滥用流量用 policing
  • QoS 涉及 Port → QoS → Queue 三张表,用 @ 符号一个事务建好
  • 配好队列后还要用 set_queue:N 把流量指过去,否则全走 Default
  • ovs-appctl qos/show 的 tx_packets 是排查的第一站
  • min-rate 是保证值,总和不能超过 QoS 的 max-rate
  • clear Port qos 会留下孤儿记录,用 --all destroy 彻底清理

下一章:实验 18:外部控制器。