允许路由器上使用多个外部网关

RFE: https://bugs.launchpad.net/neutron/+bug/1905295

本文档提出允许 Neutron 路由器拥有多个外部网关,以便表示路由器设计,其中路由器具有多个通往外部世界的接口,以提高容量(通常在这些接口之间进行负载均衡)和可用性(容忍路由器上行链路或路由器网关端口故障)。

问题描述

下面我们描述了一个云设计,它要求我们允许单个路由器上使用多个外部网关。但是本文档中提出的功能绝不特定于此设计,本文档仅致力于允许任何需要它的设计使用多个外部网关。

Neutron 路由器可以是主动-主动 HA 路由器。计算节点通过 MLAG 连接到 Neutron 路由器,路由器的两侧向计算节点呈现单个 IP。在 Neutron 路由器和数据中心网关之间有一步逻辑路由器(从这里开始:数据中心网关),它也是一个主动-主动 HA 路由器。Neutron 路由器和数据中心网关通过 2x2 点对点链路互连(从每一侧到每一侧)。在此设置中,Neutron 路由器具有 4 个通往外部世界的链路。

+--+   +--+
|R3|   |R4| Datacenter gateway
+--+   +--+
 | \   / |
 |  \ /  |
 |   X   |  2x2 point-to-point links
 |  / \  |
 | /   \ |
+--+   +--+
|R1|   |R2| Neutron router
+--+   +--+
 | \   / |
 |  \ /  |
 |   X   |  MLAG
 |  / \
 | /
+--+
|C1|   ...  Compute hosts
+--+

提议的变更

总体图景

external_gateway_info 结构中的信息用于创建网关端口。网关端口是路由器的腿,除了数据包转发之外,我们还执行 SNAT(如果启用)和 DNAT(用于浮动 IP 和端口转发),并影响该路由器的默认路由。鉴于多个 external_gateway_info 结构,我们可以直接创建多个网关端口。我们也可以直接安装其他外部网关的直接连接路由。

仅使用一个外部网关时 - 针对数据包转发 - 自然只有内部-内部和内部-外部方向。但是,使用多个外部网关,外部-外部方向也成为可能。我们没有外部-外部转发的用例。但是,为了简化实现,本文档建议支持该功能,即允许从一个外部网关到另一个外部网关的数据包转发。如果需要,后续文档可以提出对外部-外部数据包转发的更精细控制。

但是,不能在不产生复杂后果的情况下(例如,在多个下一跳之间进行负载均衡)将路由器的默认路由倍增。因此,为了简化起见,对于每个路由器,我们保留一个外部网关作为特殊网关。特殊网关用于设置路由器的默认路由,而其他外部网关不会影响默认路由。

可以像往常一样为每个外部网关执行 NAT 和浮动 IP 的预留,但请参阅“范围之外”部分。

首次实现针对 l3-agent 上的集中式路由器。

对于特殊的第一个网关端口,我们继续添加与之前相同的路由。

Neutron 路由器中通常的一组隐式管理的路由是

  • 每个路由器接口的每个子网的一个直接连接路由。

  • 每个网关端口的每个子网的一个直接连接路由。

  • 指向单个网关端口子网的 gateway_ip 的一个默认路由。

遵循此逻辑,当我们添加更多网关端口时,我们还为每个其他网关端口的每个子网添加一个直接连接路由。因此,目的地为这些子网的流量将直接通过各自的网关端口发送。流量也可以从外部到达网关端口。但是,大多数流量将通过默认路由发送。

超出范围

首先,此处并未针对 l3-agent 整体支持主动-主动 HA 路由器。

我们仅有意为第一个特殊网关端口(的子网的 gateway_ip)添加默认路由。我们不会为其他网关端口(的子网的 gateway_ip)添加默认路由。对其他路由的进一步管理(以实际使用其他外部网关)留给

  • 通过 extraroutes API 管理其他路由。

  • neutron-dynamic-routing 的未来改进,以便 Neutron 路由器也可以接收(而不仅仅是通告)其他路由。[1]

由于 extraroutes API 已经可用,我们相信在合并此功能后就可以实现多个外部网关的一些使用。

使用新引入的附加外部网关作为下一跳(用于租户网络或浮动 IP)通告任何路由也超出本文档的范围。我们认为通过路由协议通告此类路由显然是有意义的,但是

  • 这可以在 neutron-dynamic-routing 中的后续文档中涵盖,并且

  • 无需路由协议即可基本使用此处提出的更改。

向后兼容性

我们建议以略微冗余的方式存储和公开外部网关,以便更容易实现向后兼容性。尽可能地(API、DB、RPC),保持路由器的当前标量外部网关(或网关端口)属性不变。但也要添加一个新的路由器属性用于复数形式:外部网关或网关端口,其中包含(不仅是其余部分,而且)所有这些对象的列表。因此,标量属性中的对象也存在于此列表中 - 最好是列表的第一个元素。

这只是一个高级方法,旨在在只需要保持向后兼容行为时,不更改任何代码。

数据库影响

当前的 DB 模式以某种冗余的方式包含路由器 - 外部网关关系。

首先,在 routerports 表中,没有约束限制属于一个路由器的类型为 network:router_gateway 的端口数量。今天,我们每个路由器最多存储一个 network:router_gateway 端口,但 DB 模式允许更多。[2]

其次,routers 表的 gw_port_id 列是一个标量。今天,它存储与 routerports 表中相同的端口 UUID。[3]

我们建议

  • 保持 SQL 模式不变,但在 routerports 表中开始存储多个 network:router_gateway 端口。

  • 也保持 routers.gw_port_id 标量,并在其中存储一个特殊的、向后兼容的外部网关(因此,它同时存在于 routerports 表和 routers.gw_port_id 中)。

  • neutron.db.models.l3.Router 类扩展为新的属性 gw_ports,该属性映射到 routerports 表中存储的所有相关 network:router_gateway 端口。

REST API 影响

引入一个新的 API 扩展,名为 multiple-external-gateways

此扩展添加一个新的路由器属性:external_gateways。它是一个 external_gateway_info 结构的列表,例如

[
  {"network_id": ...,
   "external_fixed_ips": [{"ip_address": ..., "subnet_id": ...}, ...],
   "enable_snat": ...},
  ...
]

列表中的第一个元素是特殊的

  • 它始终与原始 external_gateway_info 相同。

  • 它是设置 Neutron 路由器默认路由的网关。

列表的其余部分的顺序无关紧要且被忽略。列表中不允许重复(即,具有相同 network_id 的多个外部网关)。

更新 external_gateway_info 也会更新 external_gateways 的第一个元素,并且保持 external_gateways 的其余部分不变。将 external_gateway_info 设置为空值也会将 external_gateways 重置为空列表。

不能在 POST /v2.0/routersPUT /v2.0/routers/{router_id} 请求中设置 external_gateways 属性,而是可以通过子方法进行管理

  • PUT /v2.0/routers/{router_id}/add_external_gateways

    接受 external_gateway_info 结构的列表。将外部网关添加到已经存在一个的网络会引发错误。

  • PUT /v2.0/routers/{router_id}/update_external_gateways

    接受 external_gateway_info 结构的列表。要更新的外部网关由 PUT 请求中找到的 network_ids 标识。可以更新 external_fixed_ipsenable_snat 字段。不能更新 network_id 字段。

  • PUT /v2.0/routers/{router_id}/remove_external_gateways

    接受潜在的部分 external_gateway_info 结构的列表。仅使用 external_gateway_info 结构中的 network_id 字段。可以存在 external_fixed_ipsenable_snat 键,但其值将被忽略。

add/update/remove PUT 子方法响应整个路由器对象,就像 POST/PUT/GET /v2.0/routers 一样。

RPC 影响

sync_routers 消息已经有一个 gw_port 字段。扩展该消息以包含 gw_ports 字段,其中包含所有网关端口。升级此 RPC 消息的版本。

升级影响

sync_routers RPC 消息将具有新版本。

客户端影响

osc 和 openstacksdk 中的相关更改。

测试

  • 单元测试。

  • l3-agent 的完整堆栈测试。

  • neutron-tempest-plugin 中的 Tempest 测试。

负责人

参考资料

[1] https://review.opendev.org/c/openstack/neutron-specs/+/783791 [2] https://opendev.org/openstack/neutron/src/commit/b7c4a11158786431c262cfcc2fc4bc46ab6bacd2/neutron/db/models/l3.py#L24 [3] https://opendev.org/openstack/neutron/src/commit/b7c4a11158786431c262cfcc2fc4bc46ab6bacd2/neutron/db/models/l3.py#L54