VLAN 感知虚拟机

Launchpad蓝图

此蓝图提出了将 VLAN 感知虚拟机整合到 OpenStack 的方法。 在本文档中,VLAN 感知虚拟机是指通过其虚拟网络接口卡 (vNIC) 发送和接收 VLAN 标记帧的虚拟机。 其主要目的是克服当前每网络一个 vNIC 模型的限制。 VLAN(或其他封装)感知虚拟机可以通过不同的封装类型和 ID 来区分许多网络的流量,而无需使用许多 vNIC。 这种方法可以扩展到更多的网络,并能够动态处理网络连接(无需热插拔 vNIC)。

问题描述

目前,虚拟机中的 VLAN 未与 Neutron 集成。 在各种用例中,允许虚拟机使用 VLAN 作为本地封装来区分每个网络进出单个虚拟接口 (VIF) 的流量将非常有用。

用例

  • 有些应用程序需要连接到许多(例如,数百个)Neutron 网络。 与每个虚拟机拥有数百个 VIF 相比,使用单个或其他少量 VIF 和 VLAN 来区分每个网络的流量更实用。

  • 云工作负载通常非常动态。 添加/删除 VLAN 可能比在虚拟机中热插拔接口更有效和/或更简单。

  • 无需从虚拟机分离 VIF,即可将虚拟机从一个网络移动到另一个网络。

  • 虚拟机可能正在运行许多容器。 每个容器可能需要连接到不同的 Neutron 网络。 为每个容器分配 VLAN(或其他封装)ID 比为每个容器需要一个 vNIC 更有效和可扩展。

  • 有一些遗留应用程序期望使用 VLAN 作为连接到多个网络的方式。 Neutron 应该提供一种将该模型暴露给虚拟机的方法,与网络实际实现方式分离。

该提案在某种程度上可能与 VLAN 透明方法 (bp/nfv-vlan-trunks) 相关,但不同之处在于它不提供一种通过链路发送不透明数据的方式,而是意识到虚拟机(或虚拟机内部的设备)提供的封装。

提议的变更

该提案保持端口资源不变。

$ neutron port-create NETWORK

新的 API 扩展如下

  1. 带有子端口操作的成员操作的一个 trunk 资源

    • 一个 trunk 资源

    • 编码在 trunk 的一个属性中的子端口,通过成员操作添加和删除

该提案是向 API 添加一个扩展,以将现有的端口转换为 trunk 和子端口。 最好将其理解为 Neutron 需要一种描述逻辑拓扑的方法,即您有一个常规 VIF 端口(父端口),并且该父端口也可能有子端口。 这些子端口用于将 VIF 端口连接到进一步的网络。 父端口和子端口都具有端口今天具有的所有属性(但是,某些子端口属性的行为必须不同,例如 binding:host_id),并且可以在任何 Neutron 网络上创建。 所有超出传统端口属性所需的信息都包含在引用传统端口的新资源中。

API 扩展的第一部分是逻辑拓扑部分。 首先,我们包含一种指示端口可能有子端口的方法。 这并非绝对必要,但为了方便起见,包含在内。 它允许 Neutron 提前拒绝端口创建,如果后端不支持子端口。

# trunk-create may refer to 0, 1 or more subport(s).
$ neutron trunk-create --port-id PORT \
                      [--subport PORT[,SEGMENTATION-TYPE,SEGMENTATION-ID]] \
                      [--subport ...]

所有引用的端口都必须存在。

请注意,如果给定的插件未实现对 bp/vlan-aware-vms 的支持,则此请求将被拒绝。

如果后端未实现该扩展,则任何 trunk 操作都将失败(状态码 404)。 如果插件实现了该扩展,则 trunk 操作可能会在端口状态与请求不兼容时返回冲突错误。

除了逻辑拓扑部分之外,我们还需要定义超visor 和 VM 之间用于将数据包定向到逻辑子端口以及确定来自 VM 的数据包的源逻辑端口的封装(分段)类型。 这里的封装是本地的,仅在 VM 和超visor 之间使用。 它与端口附加到的 Neutron 网络实现方式无关。 SEGMENTATION-TYPE 是字符串枚举,当前只有一个有效值“vlan”。 SEGMENTATION-ID 是无符号整数。 在通用情况下,SEGMENTATION-TYPE 和 SEGMENTATION-ID 在 trunk-create 和 trunk-subport-add 中都是可选的,允许 neutron 服务器在需要时选择类型和 ID。 另一方面,后端允许拒绝未指定分段详细信息的请求,并要求用户提供它们。 稍后规范可能会引入进一步的分段类型。

# trunk-add-subport adds 1 or more subport(s)
$ neutron trunk-subport-add TRUNK \
                            PORT[,SEGMENTATION-TYPE,SEGMENTATION-ID] \
                           [PORT,...]

所有引用的端口都必须存在。

CLI 有以下新命令

  • trunk-create

  • trunk-delete

  • trunk-list

  • trunk-show [不应打印子端口]

  • trunk-subport-add

  • trunk-subport-delete

  • trunk-subport-list

重要的是,在创建 trunk 和可能存在的子端口之后,虚拟机仍然使用对普通旧端口的引用启动

$ nova boot --nic port-id=...

Nova 无需了解 trunk 和子端口。

示例逻辑模型

           +-----------+
           |           |
           | +-------+ |   +------+
           | | port1 +-----+ net1 |
           | +-------+ |   +------+
+-----+    |           |
|     |    | +-------+ |   +------+
| vm0 +------+ port0 +-----+ net0 |
|     |    | +-------+ |   +------+
+-----+    |           |
           | +-------+ |   +------+
           | | port2 +-----+ net2 |
           | +-------+ |   +------+
           |           |
           +-----+-----+
                 ^
                 |
 +----------------+--------------+
 |                               |
 | Ports combined into one VIF   |
 | by turning port0 into a trunk |
 | and the other ports into      |
 | subports of the trunk.        |
 |                               |
 +-------------------------------+
  • port0 的流量在 vm0 中未标记。

  • port1 的流量被封装,例如作为 vlan ID 100。

  • port2 的流量被封装,例如作为 vlan ID 200。

在上面的示例中,虚拟机连接到 3 个 Neutron 网络。 port0 是一个常规端口,处理未标记的流量。 port0 有两个子端口,每个子端口使用不同的 VLAN ID 进行 VLAN 封装。 目标子端口的数据包将在发送到 VIF 之前使用适当的 VLAN ID 进行标记。 在 VIF 上接收到的与子端口关联的 VLAN ID 标记的数据包将被视为逻辑源是该子端口,并且数据包将被发送到子端口附加到的 Neutron 网络。

子端口具有独立的、任意的 MAC 地址,就像任何其他 Neutron 端口一样。 如果应用程序要求子端口使用与其父端口相同的 MAC 地址,则端口创建 API 支持指定 MAC 地址,因此可以指定与父端口的 MAC 地址匹配。 不允许父端口和子端口在同一网络上具有相同的 MAC 地址。

不支持嵌套子端口。

约束

  • 可以添加子端口的顶级端口必须通过在 port_id 属性中创建引用该端口的 trunk 来标记。

  • 给定父端口的每个子端口在其同级端口中都应具有唯一的 (segmentation_type, segmentation_id)。 否则,将无法正确区分源和目标逻辑端口。

  • 父端口处理“未标记”流量。 父端口将接收与任何子端口不匹配的所有数据包,这与常规端口今天的行为方式没有区别。 这也意味着每个虚拟机都有一个用于未标记流量的端口。 虽然不一定需要使用它,但如果需要,可以将其附加到虚拟 Neutron 网络。 也可以在父端口上设置安全组以丢弃所有传入和传出流量。 未来的增强功能可以包括创建尚未附加到网络的 Neutron 端口,但尚不清楚这有多有价值。

  • 子端口从其父端口逻辑继承其 binding:host_id。

  • 尝试更新子端口的 binding:host_id(例如,通过使用子端口 UUID 启动 Nova VM)必须导致错误。

  • 普通用户只能将其子端口连接到其自己的父端口。 管理员可以将子端口连接到不同所有者的父端口。

  • 在 OVS 支持 QinQ 之前,基于 OVS 的 Neutron 后端无法支持将子端口附加到“vlan_transparent”网络。 这需要能够在 VLAN 标记的子端口接口上传输 VLAN 标记的流量(嵌套 VLAN),而这目前尚不可行。

存在以下删除约束

  • 删除 trunk 会自动删除其所有子端口。

  • 禁止删除被子端口引用的(子)端口。 必须先删除子端口。

  • 禁止删除被 trunk 引用的(父)端口。 必须先删除 trunk。

Nova 变更

Neutron OVS 代理以一种可能需要在每个顶级父端口创建一个新的 OVS 网桥的方式使用 OVS。 这需要对 Nova 中现有的 OVS 和 vhost-user VIF 类型进行增强,以允许 Neutron 告诉 Nova 要为端口使用的网桥名称。 当前,Nova 仅支持 Nova 配置文件提供的单个网桥名称。

备选方案

有关替代方案,请参阅规范的历史记录,包括

数据模型影响

Neutron 端口:无变更。

Neutron DB 表:trunks

  • id: integer primary key

  • uuid

  • name

  • tenant_id

  • port_id: foreign key to ports.id

Neutron DB 表:subports

  • id

  • port_id: foreign key to ports.id

  • trunk_id: foreign key to trunks.id

  • segmentation_type

  • segmentation_id

Neutron DB 约束

  • (segmentation_type, segmentation_id, trunk_id) 必须在 subports 表中是唯一的

  • trunks.port_id 引用的端口不得被 subports.port_id 引用,反之亦然。

REST API 影响

trunk 资源
  • id

  • name

  • tenant_id

  • port_id

POST /v2.0/trunks
     ...
PUT /v2.0/trunks/TRUNK-ID/add_subports
PUT /v2.0/trunks/TRUNK-ID/delete_subports
     {'subports':
         [{'port_id': PORT_ID,
           'segmentation_type': SEGMENTATION_TYPE,
           'segmentation_id': SEGMENTATION_ID},
          ...
         ]}}
GET /v2.0/trunks/TRUNK-ID/subports

安全影响

只有管理员才能为不同的租户连接子端口。

通知影响

其他最终用户影响

租户应注意,OpenStack 不会采取任何措施来使虚拟机能够处理标记的流量,而只是提供标记的数据包。 用户完全负责正确设置虚拟机。

性能影响

现有功能的性能不受影响。 普通端口的数据路径未更改。

在某些情况下,此更改可能会提高性能。 如果没有此更改,将虚拟机连接到许多 Neutron 网络需要每个网络一个 VIF。 借助此更改,您可以使用很少的开销将虚拟机连接到 1000 个 Neutron 网络,而无需将 1000 个虚拟接口附加到虚拟机之前。 权衡是,一些额外的负担转移到虚拟机中,以处理单个接口上的分段。

IPv6 影响

无。 父端口和子端口都具有 Neutron 端口今天具有的所有属性,包括 IPv6 地址(如果需要)。

其他部署者影响

开发人员影响

  • 将使用新的 neutron 资源

  • 需要修改 Neutron 插件以支持此模型

  • 需要开发新的测试。

社区影响

实现

负责人

  • Kevin Benton

  • Peter V. Saveliev

  • Russell Bryant

  • Bence Romsics

工作项

  • API 扩展和 DB 模式更新

  • API+DB 更改的单元测试

  • 用于创建端口拓扑的 Tempest 测试

  • 用于进行功能验证的 Tempest 场景测试

  • Neutron 插件支持。 * networking-ovn (OVN 已经支持此模型) * ml2+ovs

  • 将子端口信息暴露给虚拟机,通过 Nova 元数据服务(可能需要新的规范)

依赖项

OVS VIF 类型:将桥名称从 neutron 传递到 nova

测试

Tempest 和功能测试将被创建。

全栈和入树 API 测试

  • 创建父端口

  • 创建子端口

  • 绑定父端口

  • 删除子端口

  • 删除父端口

功能测试

要实现的测试

  • 使用一个父端口且没有子端口启动 VM。 验证连接性。

  • 使用一个父端口和多个子端口启动 VM。 验证与每个逻辑端口的连接性。

  • 使用多个父端口和子端口启动 VM,并验证连接性。

  • 向正在运行的 VM 添加子端口,该 VM 具有父端口。

  • 从正在运行的 VM 删除子端口,该 VM 具有父端口。

  • 删除具有父端口和子端口的 VM。

API 测试

要实现的测试

  • 检查子端口只能连接到父端口。

  • 检查无效的 segmentation_type 是否被拒绝

  • 检查无效的 segmentation_id 是否被拒绝

文档影响

应将父端口和子端口的使用记录为使用单个 VIF 在 VM 上创建逻辑多端口拓扑的一种方式。

应提供用例的可能场景,并提供 CLI 示例。

用户文档

更新网络 API 参考。 更新管理员指南。

开发人员文档

父端口资源描述、子端口行为、API 参考。

参考资料