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