1. ovn trunk port
vm1 bound to worker1
vm2 bound to worker2
child1 (VLAN 30) inside vm1
child2 (VLAN 50) inside vm2
ovn nb 以及 sb db存储的网络资源
[root@central vagrant]# ovn-nbctl show
switch db4e7781-370c-4439-becd-35803c0e3f12 (network1)
port vm1
addresses: ["40:44:00:00:00:01 192.168.0.11"]
port vm2
addresses: ["40:44:00:00:00:02 192.168.0.12"]
switch 40ac144b-a32a-4202-bce2-3329f8f3e98f (network2)
port child1
parent: vm1
tag: 30 # vlan
addresses: ["40:44:00:00:00:03 192.168.1.13"]
port child2
parent: vm2
tag: 50 # vlan
addresses: ["40:44:00:00:00:04 192.168.1.14"]
[root@central vagrant]# ovn-sbctl show
Chassis worker2
hostname: worker2
Encap geneve
ip: "192.168.50.101"
[root@central vagrant]# ovn-sbctl show
Chassis worker2
hostname: worker2
Encap geneve
ip: "192.168.50.101"
options: {csum="true"}
Port_Binding child2
Port_Binding vm2
Chassis worker1
hostname: worker1
Encap geneve
ip: "192.168.50.100"
options: {csum="true"}
Port_Binding child1
Port_Binding vm1
虚拟机内部的vlan配置
[root@worker1 vagrant]# ip netns exec vm1 ip -d link show
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0 addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
2: child1@vm1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 40:44:00:00:00:03 brd ff:ff:ff:ff:ff:ff promiscuity 0
vlan protocol 802.1Q id 30 <REORDER_HDR> addrgenmode eui64
# vlan 30
numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
24: vm1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/ether 40:44:00:00:00:01 brd ff:ff:ff:ff:ff:ff promiscuity 2
openvswitch addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
[root@worker2 vagrant]# ip netns exec vm2 ip -d link show
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0 addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
2: child2@vm2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 40:44:00:00:00:04 brd ff:ff:ff:ff:ff:ff promiscuity 0
vlan protocol 802.1Q id 50 <REORDER_HDR> addrgenmode eui64
# vlan 50
numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
15: vm2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/ether 40:44:00:00:00:02 brd ff:ff:ff:ff:ff:ff promiscuity 2
openvswitch addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
2. 优化neutron 内存占用
RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存)
可以看到在优化之前,neutron-server实际使用物理内存高达73.5Gib。
经过定位分析发现,ovn sb db存在将近上百万调MAC_Binding 记录(也就是arp相关的ip和mac的对应记录)。 MAC_Binding 是由ovn-controller维护和传播的。一旦学习到一个新的MAC地址,ovn就会为每一个连接到外部的软路由添加一个MAC_Binding。
在这个场景中,连接到外部网络的软路由只有16个,并且有观察到近1M条记录。 这个不进会导致内存问题,也会对ovn sbbound server造成大量的流量和压力,ovn-controller会向ovn sbbound发起大量的事务请求,并且还要响应回复。
为什么neutron要关注MAC_Binding, 为了复用fip。但是内网ip和mac都在不断的发生切换。为了删除过期的MAC_Binding 记录,在neutron中维护了MAC_Binding 的全量copy,因为copy中的MAC_Binding没有过期机制,所以最终肯定会触发OOM。
为了解决该问题,ovn提供了一种机制用于在某个时间点清理过期的MAC_Binding 记录。 比如在绑定和取消绑定fip的时候。
为了让neutron触发清理,特别提供了一个ovsdb-client 工具。
该机制合入后,RSS从75GB 收敛到 7.5GB。
3. 定位ovn的扩展性问题
ovn 支持fip(dnat_and_snat) 在hypervisor本地直接进行南北向路由决策。而不必将流量转发到一个中心式的网络节点。
当出去的流量到达ovn网桥时,会对源ip进行snat,经过br-int到br-external 的patch port 经专用公网网卡直接转发出去。
该特性叫做DVR,路由阶段不需要转发到网关节点,直接经本地计算节点出去,没有下一跳,overlay 流量,以及分布式路由处理。
主要优势就是,只要伸缩计算节点即可,唯一的成本是公网ip。
但是在fip的使用场景中,我们留意到 fip相关的 逻辑流表非常巨大。这会导致客户端(ovn-controller)以及服务端ovsdb-server cpu和内存消耗非常高。
我想继续弄清楚流表是如何分发的,以及什么材质导致该泄漏的主要原因。我只是简单计算了一下每一个阶段的流表数量并对他们进行了排序。如下的数据中心展示了93%的逻辑流表都处于两个阶段中。
$ head -n 6 logical_flows_distribution_sorted.txt
lr_out_egr_loop: 423414 62.24%
lr_in_ip_routing: 212199 31.19%
lr_in_ip_input: 10831 1.59%
ls_out_acl: 4831 0.71%
ls_in_port_sec_ip: 3471 0.51%
ls_in_l2_lkup: 2360 0.34%
以下是对应的工具
# ovn-sbctl list Logical_Flow > logical_flows.txt
# Retrieve all the stages in the current pipeline
$ grep ^external_ids logical_flows.txt | sed 's/.*stage-name=//' | tr -d '}' | sort | uniq
# Count how many flows on each stage
$ while read stage; do echo $stage: $(grep $stage logical_flows.txt -c); done < stage_names.txt > logical_flows_distribution.txt
$ sort -k 2 -g -r logical_flows_distribution.txt > logical_flows_distribution_sorted.txt
下一步就是弄清楚这两张表的内容
(lr_out_egr_loop & lr_in_ip_routing)
_uuid : e1cc600a-fb9c-4968-a124-b0f78ed8139f
actions : "next;"
external_ids : {source="ovn-northd.c:8958", stage-name=lr_out_egr_loop}
logical_datapath : 9cd315f4-1033-4f71-a26e-045a379aebe8
match : "ip4.src == 172.24.4.10 && ip4.dst == 172.24.4.209"
pipeline : egress
priority : 200
table_id : 2
hash : 0
_uuid : c8d8400a-590e-4b7e-b433-7a1491d31488
actions : "inport = outport; outport = \"\"; flags = 0; flags.loopback = 1; reg9[1] = 1; next(pipeline=ingress, table=0); "
external_ids : {source="ovn-northd.c:8950", stage-name=lr_out_egr_loop}
logical_datapath : 9cd315f4-1033-4f71-a26e-045a379aebe8
match : "is_chassis_resident(\"vm1\") && ip4.src == 172.24.4.218 && ip4.dst == 172.24.4.220"
pipeline : egress
priority : 300
table_id : 2
hash : 0
_uuid : 0777b005-0ff0-40cb-8532-f7e2261dae06
actions : "outport = \"router1-public\"; eth.src = 40:44:00:00:00:06; eth.dst = 40:44:00:00:00:07; reg0 = ip4.dst; reg1 = 172.24.4.218; reg9[2] = 1; reg9[0] = 0; ne
xt;"
external_ids : {source="ovn-northd.c:6945", stage-name=lr_in_ip_routing}
logical_datapath : 9cd315f4-1033-4f71-a26e-045a379aebe8
match : "inport == \"router1-net1\" && ip4.src == 192.168.0.11 && ip4.dst == 172.24.4.226"
pipeline : ingress
priority : 400
table_id : 9
hash : 0
可以看出来,这些流表用于处理fip之间的包。
基本上是,对于每一个可能的fip对都存在这些流表,以便为了这些fip间的流量不会流经过geneve隧道。
然而fip到fip的流量流经两个ovn 端口,这个场景并不是最常用的用例。这些流表就先存在着以便保证dvr,并且不经过overlay 网络。
目前该问题已解决,200fip情况的逻辑流表的梳理已经从127K下降为2.7k, 并且能够保证线性增长。
4. 关于geneve的封装
http://dani.foroselectronica.es/wp-content/uploads/2019/02/icmp-geneve-decoding
- Logical Datapath (switch/router) identifier (24 bits) - Geneve VNI
- Ingress and Egress port identifiers - Option with class 0x0102 and type 0x80 with 32 bits of data:
关于vni id 实际上是逻辑交换机和逻辑路由器的标识符。
进出 端口标识符
# 查看ls lr的vni id
[root@pc-node-1 master]# k ko sbctl list Datapath_Binding
_uuid : af422fb1-f006-40a0-a347-aebfb6d686e3
external_ids : {logical-switch="9fc30bf4-a27f-4e43-900c-bc346d09e4b6", name=external204}
load_balancers : []
tunnel_key : 4
_uuid : 65d9b655-bbd7-45a5-b382-430668803c36
external_ids : {logical-switch="76f17a8b-6458-4edc-b886-89497c1e3347", name=vpc1-subnet1}
load_balancers : []
tunnel_key : 6
_uuid : c81d45b5-a362-4ce7-bc3a-f6fc81cb45bf
external_ids : {logical-switch="f451ee57-2ccd-47e5-956b-cbb78c7e69a0", name=join}
load_balancers : []
tunnel_key : 2
_uuid : b154d04e-4a14-4a9d-b171-b952de1eea7a
external_ids : {logical-switch="015926e7-4e12-464c-859b-ad309ec4904d", name=ovn-default}
load_balancers : []
tunnel_key : 3
_uuid : a6cf5a4f-3ddb-4d2f-a293-235aa1d8d553
external_ids : {logical-router="e3e5e1ea-5193-4c80-9b91-3046f229bcfb", name=vpc1}
load_balancers : []
tunnel_key : 5
_uuid : f234ef33-0a32-46d2-a065-2caa1f4cec59
external_ids : {logical-router="dcd93f0a-0706-4e6d-9e8b-17ee3de2e17b", name=ovn-cluster}
load_balancers : []
tunnel_key : 1