目前飞牛官方并没有开发虚拟机的网络限速功能,但可以利用 OVS 原生 QoS 功能实现网络限速。(要求 OVS≥2.5 且支持 HTB )
查看 OVS 网络端口
# 查找虚拟机对应的 OVS 端口(如tap53eeb988-c7)
ovs-vsctl show
输出示例,可以看到 OVS 网桥(Bridge)名称、端口(Port)名称、接口(Interface )名称和 OVS 版本。在这里,虚拟机的接口是 vnet2,属于网桥 enp2s0-ovs,OVS 版本为 3.1.0。
19d20c10-43b1-4509-82d6-bcc1bf2cd08e
Bridge enp2s0-ovs
Port enp2s0
Interface enp2s0
Port enp2s0-ovs
Interface enp2s0-ovs
type: internal
Port vnet2
Interface vnet2
ovs_version: "3.1.0"
上传速率限制(基于 MAC地址)
ovs-vsctl -- set Port enp2s0 qos=@upload_qos \
-- --id=@upload_qos create QoS type=linux-htb other-config:max-rate=2500000000 queues=1=@vm_queue \
-- --id=@vm_queue create Queue other-config:min-rate=0 other-config:max-rate=20000000 other-config:burst=2000000
ovs-ofctl add-flow enp2s0-ovs "priority=500,dl_src=11:22:33:44:55:66,actions=set_queue:1,enp2s0" -O OpenFlow13
代码解释:
为网口 enp2s0 创建 QoS 策略,所有经过此端口的出口流量(egress)都会受 @upload_qos
控制,并加入限速队列 @vm_queue
(队列 1);
在网桥上创建流表,处理特定的数据包,在队列 1 中加入 MAC 地址,根据虚拟机的 MAC 地址限速,并指定流量出口方向为物理网卡。
由于飞牛系统每次重启都会导致虚拟机的网络端口(vnetX)改变,因此不建议直接在 vnet 端口上进行 QoS 的配置。
参数说明:
type=linux-htb
:使用 linux 网络流控的 HTB(分层令牌桶)算法。
- QoS 策略中的
other-config:max-rate
:指定该端口的最大速率(不可省略,若省略则会直接
- 影响飞牛系统网速,这里设置为了 2.5Gbps ,并且所有队列最大速率总和不得超过该值)
- HTB 队列中的
other-config:max-rate
:限制最大速率(此处设置 20Mbps )
- HTB 队列中的
other-config:burst
:允许突发流量(此处设置 2Mbit )
- HTB 队列中的
other-config:min-rate
:设置为 0 表示允许其他队列借用带宽,但可能影响虚拟机的最低带宽保障
dl_src
:匹配虚拟机源 MAC 地址
set_queue
:将流量定向到限速队列 1
-O OpenFlow13
:使用 OpenFlow 1.3 协议
流量控制示意图:
[虚拟机(vnetX)] --> OVS网桥(enp2s0-ovs)
|
|-- 流表引擎(基于MAC匹配队列)
|
[物理网卡(enp2s0)] --> 外部网络
下载速率限制
** **OVS 的 QoS 策略是针对出口方向(egress,针对端口而言)的,因此要限制虚拟机的下载速率,除了在虚拟机的网络端口(因为对于 vnetX 端口来说虚拟机下载的流量就相当于从端口传出的流量)设置 QoS 之外,也可以直接在物理网络端口上设置 ingress_policing_rate
(不推荐,会直接限制物理机网速)。
ovs-vsctl set interface vnet2 ingress_policing_rate=20000 ingress_policing_burst=2000
ingress_policing_rate
使用简单令牌桶算法,相比HTB队列缺乏优先级控制和带宽借用机制,缺乏流量整形能力,可能造成虚拟机的大量丢包,也不支持流表这种操作,因此,如有下载速率限制需求,建议使用下方提供的自动脚本。
持久化部署
- 创建脚本
/usr/local/bin/ovs-dual-qos.sh
:
#!/bin/bash
# 配置参数
PHY_MAC="aa:bb:cc:dd:ee:ff" # 物理端口网卡MAC(使用小写英文字母)
VM_MAC="a1:b2:c3:d4:e5:66" # 虚拟机固定MAC(使用小写英文字母)
PHY_RATE="2500000000" # 物理网口速率2.5Gbps(总带宽上限,需≥所有队列速率之和)
UPLOAD_RATE="20000000" # 上传限速20Mbps
DOWNLOAD_RATE="20000000" # 下载限速20Mbps
UPLOAD_BURST="2000000" # 突发流量2Mbits
DOWNLOAD_BURST="2000000"
# 获取接口UUID
iface_uuid=$(ovs-vsctl --bare find interface \
external_ids:attached-mac=\"$VM_MAC\" | head -1)
# 验证UUID有效性(若失败则说明虚拟机未启动)
if ! [[ $iface_uuid =~ ^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$ ]]; then
echo "错误:接口UUID提取失败"
exit 1
fi
# 获取虚拟机端口名称
VM_PORT=$(ovs-vsctl --bare get interface "$iface_uuid" name)
# 获取虚拟机端口所在网桥名称
VM_BRIDGE=$(ovs-vsctl port-to-br "$VM_PORT")
# 获取网桥所在物理端口名称
# 获取网桥下的所有端口
PORTS=$(ovs-vsctl list-ports $VM_BRIDGE)
# 遍历端口并匹配MAC地址
for port in $PORTS; do
port_mac=$(ovs-vsctl --bare get interface "$port" mac-in-use 2>/dev/null)
if [ "$port_mac" = "\"$PHY_MAC\"" ]; then
PHY_PORT="$port"
break
fi
done
# 未匹配时的报错
if [ -z "$PHY_PORT" ]; then
echo "错误:未找到MAC为 $PHY_MAC 的物理端口"
exit 2
fi
echo "网桥:$VM_BRIDGE、虚拟机端口:$VM_PORT、物理端口:$PHY_PORT"
if [ "$PHY_RATE" -lt "$((UPLOAD_RATE + DOWNLOAD_RATE))" ]; then
echo "错误:物理端口总带宽不足"
exit 3
fi
# 清除旧配置
phy_qos_uuid=$(ovs-vsctl get Port "$PHY_PORT" qos)
if [ "$phy_qos_uuid" != '[]' ]; then
ovs-vsctl -- destroy QoS "$phy_qos_uuid" -- clear Port "$PHY_PORT" qos
fi
vm_qos_uuid=$(ovs-vsctl get Port "$VM_PORT" qos)
if [ "$vm_qos_uuid" != '[]' ]; then
ovs-vsctl -- destroy QoS "$vm_qos_uuid" -- clear Port "$VM_PORT" qos
fi
ovs-ofctl del-flows "$VM_BRIDGE" "dl_src=$VM_MAC"
#################### 上传限速配置 ####################
# 在物理端口配置出口QoS
ovs-vsctl -- \
set Port $PHY_PORT qos=@upload_qos \
-- --id=@upload_qos create QoS type=linux-htb \
other-config:max-rate=$PHY_RATE \
queues=1=@upload_queue \
-- --id=@upload_queue create Queue \
other-config:max-rate=$UPLOAD_RATE \
other-config:burst=$UPLOAD_BURST
# 流表规则:匹配源MAC→限速队列(注意!流表规则只能在网桥上添加,不可在端口上添加)
ovs-ofctl add-flow "$VM_BRIDGE" \
"priority=500,dl_src=$VM_MAC,actions=set_queue:1,output:$(ovs-vsctl get Interface "$PHY_PORT" ofport)"
#################### 下载限速配置 ####################
# 在虚拟机端口配置出口QoS
ovs-vsctl -- \
set Port $VM_PORT qos=@download_qos \
-- --id=@download_qos create QoS type=linux-htb \
other-config:max-rate=$PHY_RATE \
queues=2=@download_queue \
-- --id=@download_queue create Queue \
other-config:max-rate=$DOWNLOAD_RATE \
other-config:burst=$DOWNLOAD_BURST
ovs-ofctl add-flow "$VM_BRIDGE" \
"priority=500,dl_dst=$VM_MAC,actions=set_queue:2,output:$(ovs-vsctl get Interface "$VM_PORT" ofport)"
- 创建 systemd 服务
/etc/systemd/system/ovs-qos.service
:
[Unit]
Description=OVS QoS
After=network-online.target openvswitch.service libvirtd.service
Requires=libvirtd.service
[Service]
# 等待虚拟机启动
ExecStartPre=/bin/sh -c 'while ! virsh list | grep -q "running"; do sleep 2; done'
ExecStart=/usr/local/bin/ovs-dual-qos.sh
Restart=on-failure
# 增加超时配置避免无限等待
TimeoutStartSec=120s
RestartSec=10s
[Install]
WantedBy=multi-user.target
- 授权并启用服务:
chmod +x /usr/local/bin/ovs-dual-qos.sh
systemctl enable ovs-qos
查看流控配置
# 查看端口QoS绑定状态
ovs-vsctl list port vnet2 | grep qos
# 查看QoS策略详细信息
ovs-vsctl list qos
# 查看队列参数
ovs-vsctl list queue
# 查看端口统计(含突发包计数)
ovs-appctl dpctl/show -s
# 查看队列统计
watch -n 1 "ovs-ofctl queue-stats enp2s0-ovs"
# 查看流表规则
ovs-ofctl dump-flows enp2s0-ovs
# 查看ingress限速统计(Kbps单位)
ovs-vsctl list interface vnet2 | grep policing
# 查看HTB队列状态
tc -s -d qdisc show dev enp2s0-ovs
预期输出:qos
字段应显示 UUID
,且 other_config:max-rate
值为 35000000
(回滚)恢复默认状态
# 消除 QoS 策略
ovs-vsctl clear port enp2s0 qos
ovs-vsctl destroy qos <QoS_UUID>
ovs-vsctl destroy queue <Queue_UUID>
# 消除 ingress_policing_rate 规则
ovs-vsctl set interface vnet2 ingress_policing_rate=0 \
ingress_policing_burst=0
# 消除流表
ovs-ofctl del-flows enp2s0-ovs