核心 OVN BGP 集成

https://bugs.launchpad.net/neutron/+bug/2111276

OVN 25.03 引入了 BGP 相关的功能,提供了与当前 ovn-bgp-agent 底层暴露方法同等的能力。本规范旨在引入一种设计,使用 OVN 的功能与 Neutron 集成,以取代 ovn-bgp-agent。

问题描述

ovn-bgp-agent 是在计算和网络节点上运行的另一个进程。当创建或移动新的工作负载时,会发生大量的处理,因为该代理需要关注这些变化并在节点上根据需要重新配置。由于 OVN 充分了解其资源的本地性,我们可以将所有处理交给 OVN,仅在 Neutron 中管理底层的 BGP OVN 拓扑,并仍然使用纯 L3 脊叶拓扑进行数据平面流量。

本规范中使用的缩写词

  • BGP:边界网关协议

  • ECMP:等价多路径

  • LRP:逻辑路由器端口

  • LSP:逻辑交换端口

  • LS:逻辑交换机

  • LR:逻辑路由器

  • VRF:虚拟路由和转发

  • FRR:自由范围路由 (https://github.com/FRRouting/frr)

提议的变更

本规范建议引入一个新的 Neutron 服务插件,用于管理 OVN 中的底层 BGP 拓扑。其主要目的是确保与 BGP 相关的 OVN 资源始终正确配置,充当这些资源的协调器。此外,它还负责计算节点的扩展,因为每个计算节点都需要拥有自己的绑定资源,例如路由器和具有 localnet 端口的逻辑交换机。

无需对 API 或数据库模型进行任何更改。但是,需要修改 Neutron OVN DB 同步脚本,以不监控底层的 BGP 资源。可能这已经在 Neutron 中通过规范 [1] 计划存在,因此我们需要恢复这项工作。可以通过在 BGP 管理资源的 external_ids 列中设置一个显式标签来实现,Neutron 将不会触碰该标签。此外,我们需要确保在表示层上,所有底层的 BGP 资源都不会通过 API 暴露给用户。例如,路由器列表命令不应返回 BGP 路由器。

每个计算节点都需要运行一个 FRR 实例,该实例监控本地 VRF 并将路由通告给 BGP 对等体。安装程序的责任是配置 FRR 实例以使用正确的 BGP 参数并连接到正确的 BGP 对等体。

由于通过描述比视觉上理解拓扑更容易,以下图表显示了 OVN 中的底层 BGP 逻辑拓扑。为了获得更好的分辨率,建议在新标签页中打开该图像。

../../_images/ovn-bgp-topology.jpg

OVN BGP 逻辑拓扑(点击查看完整分辨率)

BGP 分布式逻辑路由器

引入了一个具有启用 OVN BGP 功能的新路由器,该路由器在图表中命名为“BGP 分布式路由器”,并启用了动态路由标志。该路由器通过一个虚拟连接连接到提供商逻辑交换机。此连接不用于任何流量,仅用于逻辑上连接逻辑交换机和 BGP 路由器,以便 northd 可以在 Southbound DB 的 Advertised_Route 表中为需要通告的 IP 创建条目。

该路由器还连接到一个具有 localnet 端口的 LS。此 LS 连接到提供商网桥 br-bgp,需要在每个 chassis 上配置,因为这里的流量是分布式的,并且可以在任何节点上发生。该网桥通过 Neutron 创建的 localnet 端口连接到 ls-public LS。此 LS 通常连接传统部署中的物理网络。我们需要 localnet 端口以避免 OVN 通过 geneve 隧道将流量发送到托管逻辑路由器的节点。

BGP 分布式路由器通过对等 LRP 连接到每个 chassis 的逻辑路由器,并绑定到相应的 chassis。下一节描述了每个 chassis 的 LR。由于 BGP 路由器是分布式的,我们需要选择正确的 LRP,以避免将流量转发到不同的 chassis。例如,如果来自 chassis A 的租户 LSP 有出站流量,则 BGP 分布式路由器需要将流量路由到 chassis A 上的 LRP。为此,我们将使用逻辑路由策略和 is_chassis_resident 匹配。下面显示了一个逻辑路由策略的示例

action              : reroute
bfd_sessions        : []
chain               : []
external_ids        : {}
jump_chain          : []
match               : "inport==\"lrp-bgp-main-router-to-ls-interconnect\" && is_chassis_resident(\"cr-lrp-bgp-main-router-to-bgp-router-r0-compute-0\")"
nexthop             : []
nexthops            : ["169.254.0.1"]
options             : {}
priority            : 10

在这种情况下,nexthop 是 chassis A 上的 LRP,目前必须是 IPv4,因为 OVN 当前包含一个错误,该错误阻止将 IPv6 LLAs 用作 nexthop,该错误在 [2] 中报告。该策略仅在 is_chassis_resident 中定义的 chassis 上实现,因此流量始终保持在 chassis 的本地。由于策略处于 LR 管道的后期阶段,我们需要创建一个逻辑路由器静态路由,以便通过路由阶段。因此,BGP 分布式逻辑路由器需要包含两个静态路由。一个用于路由进入提供商网络的流量,另一个未使用的路由仅用于在管道中击中重路由策略之前通过路由阶段。

第一个静态路由可能如下所示

bfd                 : []
external_ids        : {}
ip_prefix           : "192.168.111.0/24"
nexthop             : "192.168.111.30"
options             : {}
output_port         : lrp-bgp-main-router-to-ls-interconnect
policy              : []
route_table         : ""
selection_fields    : []

其中 ip_prefix 是提供商网络前缀,output_port 是连接到提供商 LS 的 LRP。nexthop 是作为网关的 Neutron 路由器端口的 LRP。

第二个静态路由未被使用,并且可以如下所示

bfd                 : []
external_ids        : {}
ip_prefix           : "0.0.0.0/0"
nexthop             : "192.168.111.30"
options             : {}
output_port         : []
policy              : []
route_table         : ""
selection_fields    : []

该路由需要匹配所有流量,并且 nexthop 不重要,因为它将由基于 chassis 本地性的重路由策略确定。带有实现的路由管道如下所示

... the other routes are here but none matches 0.0.0.0/0 ...
table=15(lr_in_ip_routing   ), priority=4    , match=(reg7 == 0 && ip4.dst == 0.0.0.0/0), action=(ip.ttl--; reg8[0..15] = 0; reg0 = 192.168.111.30; reg5 = 192.168.111.30; eth.src = 00:de:ad:10:00:00; outport = "lrp-bgp-main-router-to-ls-interconnect"; flags.loopback = 1; reg9[9] = 1; next;)
table=15(lr_in_ip_routing   ), priority=0    , match=(1), action=(drop;)
table=16(lr_in_ip_routing_ecmp), priority=150  , match=(reg8[0..15] == 0), action=(next;)
table=16(lr_in_ip_routing_ecmp), priority=0    , match=(1), action=(drop;)
table=17(lr_in_policy       ), priority=10   , match=(inport=="lrp-bgp-main-router-to-ls-interconnect" && is_chassis_resident("cr-lrp-bgp-main-router-to-bgp-router-r0-compute-0")), action=(reg0 = 169.254.0.1; reg5 = 169.254.0.2; eth.src = 00:de:ad:00:10:00; outport = "lrp-bgp-main-router-to-bgp-router-r0-compute-0"; flags.loopback = 1; reg8[0..15] = 0; reg9[9] = 1; next;)

正如我们需要到达击中重路由策略的阶段一样,我们首先需要通过 lr_in_ip_routing 阶段,并且该阶段使用静态路由实现。这意味着我们使用第一个规则匹配 0.0.0.0/0 前缀,然后使用带有其重路由操作的最后一个规则更改 output_port。如果不存在静态路由,则流量将使用包含 drop 操作的第二个规则被丢弃。

每个 chassis 的逻辑路由器

还有一个逻辑路由器被创建并绑定到每个 chassis。这些路由器用于从 BGP 对等体学习 ECMP 路由,并在提供商网桥和 BGP 分布式路由器之间转发流量。

对于计算节点通过相同的脊叶拓扑共享数据平面和控制平面流量的情况,需要在提供商网桥上维护 openflow 规则,以区分控制平面和数据平面之间的流量,从而将流量转发到主机,并将需要进入 OVN 覆盖网络的数据平面流量转发到 OVN 覆盖网络。可以使用以下 openflow 规则来实现这一点

priority=10,ip,in_port=eth0,nw_dst=<host IPs> actions=NORMAL
priority=10,ipv6,in_port=eth0,ipv6_dst=<host IPv6s> actions=NORMAL
priority=10,arp actions=NORMAL
priority=10,icmp6,icmp_type=133 actions=NORMAL
priority=10,icmp6,icmp_type=134 actions=NORMAL
priority=10,icmp6,icmp_type=135 actions=NORMAL
priority=10,icmp6,icmp_type=136 actions=NORMAL
priority=10,ipv6,in_port=eth0,ipv6_dst=fe80::/64 actions=NORMAL
priority=8,in_port=eth0 actions=mod_dl_dst:<LRP MAC>,output:<patch_port_to_ovn>

这些规则匹配目标主机上的流量并将其转发到主机。所有其他流量都转发到 OVN 覆盖网络。patch_port_to_ovn 是 ovn-controller 基于 ovn-bridge-mappings 配置创建的 patch 端口。

路由器本身需要实现来自提供商网络和来自 OVN 覆盖网络的流量的路由。对于传入的提供商网络流量,路由可以如下所示

bfd                 : []
external_ids        : {}
ip_prefix           : "192.168.111.0/24"
nexthop             : "169.254.0.2"
options             : {}
output_port         : lrp-bgp-router-r0-compute-0-to-bgp-main-router
policy              : []
route_table         : ""
selection_fields    : []

其中 ip_prefix 匹配提供商网络的子网,nexthop 设置为连接到 BGP 分布式路由器的 LRP 的地址,output_port 设置为与其对等 LRP。

来自 OVN 覆盖网络的出站流量需要使用 ECMP 路由到 BGP 网络。可以使用以下针对每个 BGP 对等体的静态路由来实现

bfd                 : []
external_ids        : {}
ip_prefix           : "0.0.0.0/0"
nexthop             : "100.64.0.1"
options             : {}
output_port         : lrp-bgp-router-r0-compute-0-to-ls-r0-compute-0-eth0
policy              : []
route_table         : ""
selection_fields    : []

bfd                 : []
external_ids        : {}
ip_prefix           : "0.0.0.0/0"
nexthop             : "100.65.0.1"
options             : {}
output_port         : lrp-bgp-router-r0-compute-0-to-ls-r0-compute-0-eth1
policy              : []
route_table         : ""
selection_fields    : []

流量流程

本节描述了从托管在 chassis 上的 LSP 发送和接收的流量。

从外部网络到具有 Floating IP 的 VM 的流量示例,位于 chassis 1

由于 ls-public LS 和 BGP 分布式路由器之间的虚拟连接,OVN 会为 Floating IP 创建一个 Advertised_Route 条目。由于关联的逻辑端口绑定到 chassis 1,OVN 会将 chassis 1 上的本地 VRF 填充为 Floating IP 的路由,并且本地 FRR 实例会将路由通告给 BGP 对等体。

Fabric 从 BGP 对等体学习 Floating IP 的路由,并由于 ECMP 路由将流量转发到 chassis 1 到 eth0 或 eth1。

流量不匹配提供商网桥上的任何更高优先级的 openflow 规则,并匹配最后一个规则。该规则将目标 MAC 更改为与 per-chassis 路由器关联的 LRP MAC 地址,并将流量转发到 OVN。流量进入 per chassis 逻辑路由器,该路由器配置了 Logical_Static_Route 以将流量转发到分布式 BGP 路由器。BGP 分布式路由器配置为将流量转发到匹配目标 IP 和提供商网络子网的 ls-inter-public 交换机,并通过 br-bgp 提供商网桥将流量传递到 ls-public 逻辑交换机。从这里,流量遵循与没有 BGP 相同的路径,并由 Neutron 路由器进行 NAT 处理。

从具有 Floating IP 的 VM 到外部网络的流量示例

出站 VM 流量由 Neutron 路由器进行 NAT 处理,流量转发到连接到 ls-public LS 的提供商网络网关。由于存在 localnet 端口,流量通过 br-bgp 网桥到达分布式 BGP 路由器,在那里它匹配人工 Logical_Router_Static_Route 以跳过管道中的 lr_in_ip_routing 阶段,并与基于 chassis 本地性的 BGP 路由器策略匹配。策略的重路由操作将选择连接到 per-chassis 路由器的正确 LRP。在这里,流量匹配每个对等体的静态路由,并通过 ECMP 转发到 BGP 网络。

测试

现有的 tempest 测试应该提供良好的回归测试。我们可以重用 ovn-bgp-agent 项目中的现有拓扑,该拓扑与模拟 BGP 路由器的 VM 对等。

实现

实现分为两个部分。第一部分创建服务插件,负责 OVN 中的 BGP 拓扑,包括配置静态路由和路由器策略。

第二部分是 OVN 代理扩展 [3],用于配置每个 chassis 的主机配置。OVN 代理本身与 BGP 服务插件是正交的,可以用任何负责节点动态配置的第三方工具替换。OVN 代理扩展负责将流量引导到 OVN,并配置每个 chassis 的主机配置,例如根据 BGP 对等体添加 ovn-bridge-mappings,并实现本地 openflow 规则以区分控制平面和数据平面之间的流量。它还监视 br-bgp 上的 patch 端口,并在 localnet 端口之间创建直接连接,以避免在网桥上进行任何 FDB 学习。下面显示了一个简单的 openflow 规则示例

priority=10,in_port=2, actions=output:3
priority=10,in_port=3, actions=output:2

其中 2 是连接到连接到 BGP 分布式路由器的逻辑交换机的 patch 端口,3 是连接到 Neutron 公共交换机的 patch 端口。

是否使用 BGP 与 Neutron 确定由启用服务插件和 OVN 代理扩展来决定。

如本规范的第一段所述,OVN 中的 BGP 支持是在 25.03 中引入的。因此,BGP 服务插件需要 OVN 25.03 或更高版本。

负责人

文档

部署指南将在描述如何启用服务插件以及需要在节点上配置的内容,例如将流量引导到 OVN 或配置通告路由到其对等体的 BGP 扬声器。将引入一个使用 FRR 配置的示例,以便操作员可以参考配置。