QoS 最小保证包速率

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

与带宽可能成为网络接口的限制因素类似,包处理能力往往是像 OVS 这样的软交换解决方案的限制因素。同时,某些应用程序不仅依赖于保证的带宽,还依赖于保证的包速率才能正常工作。OpenStack 已经通过 最小带宽 QoS 策略规则 支持带宽保证。本规范旨在添加对类似最小包速率 QoS 策略规则的支持。

为了支持新的 QoS 规则类型,Neutron 和 Nova 都需要扩展。本规范仅关注 Neutron 的影响。有关高级视图和 Nova 特定影响,请阅读 Nova 规范

问题描述

当前,有几个参数,定量和定性,定义了一个 VM 并用于选择正确的宿主机和网络后端设备来运行它。软交换解决方案(如 OVS)的包处理能力也可以是影响新工作负载放置的参数。

本规范侧重于管理运行在为 VM 提供服务的超visor 宿主机上的软交换机(如 OVS)的包处理能力。管理网络后端其他部分的包处理能力(如在 Top-Of-Rack 交换机中)不在本规范的范围内。

保证包处理能力通常涉及在两个层面执行约束。

  • 放置:在放置(调度)VM 及其端口时避免过度订阅。

  • 数据平面:在软交换机上强制执行保证。

本规范仅解决放置执行。数据平面执行可以在以后开发。当 包速率限制策略规则 功能实现时,可以通过将最小和最大包速率 QoS 规则添加到相同的 QoS 策略中(最大限制设置为等于最小保证)来应用基本的数据平面执行。

用例

我作为管理员希望定义我的 OVS 软交换机每计算节点能够处理的最大包速率,以千包每秒 (kpps) 为单位,以便避免 OVS 过载。

我作为最终用户希望定义 Neutron 端口需要为我的 VM 提供,以千包每秒 (kpps) 为单位的最小包速率,以便使用该端口的应用程序能够按预期工作。

我作为管理员希望将具有此类端口的 VM 放置在仍然可以提供 Neutron 端口请求的最小包速率的计算节点上,以便应用程序获得其请求的内容。

我作为管理员希望在服务器的 Neutron 端口请求的最小包速率保证无法在任何其他符合条件的计算节点上满足的情况下,拒绝 VM 生命周期操作,以便避免 OVS 过载并保持应用程序保证。

提议的变更

注意

有关包括 Nova、Neutron 和 Placement 之间影响和交互的高级解决方案,请参阅 Nova 规范

此功能类似于已经支持的 最小带宽 QoS 策略规则。因此,本规范描述了与已经引入的概念和实现相关的impact,同时也指出了关键区别。

该解决方案需要区分两种部署场景。

  1. 包处理功能是在计算主机 CPU 上实现的,因此从入站和出站方向处理的包由相同的 CPU 核心处理。这是在非硬件卸载的 OVS 部署中的情况。在这种情况下,OVS 代表单个包处理资源池。这可以用一个新的资源类来表示,即 NET_PACKET_RATE_KILOPACKET_PER_SEC

  2. 包处理功能是在专用硬件中实现的,其中传入和传出的包由独立的硬件资源处理。这是硬件卸载的 OVS 的情况。在这种情况下,单个 OVS 具有两个独立的资源池,一个用于传入的包,一个用于传出的包。因此,需要用两个新的资源类来表示这些资源池:NET_PACKET_RATE_EGR_KILOPACKET_PER_SECNET_PACKET_RATE_IGR_KILOPACKET_PER_SEC

OVS 包处理能力

Neutron OVS 代理需要提供配置选项,以便管理员定义每个计算节点的 OVS 的最大包处理能力。这意味着以下新的配置选项被添加到 OVS 代理配置中

[ovs]
# Comma-separated list of <hypervisor>:<packet_rate> tuples, defining the
# minimum packet rate the OVS backend can guarantee in kilo (1000) packet
# per second. The hypervisor name is used to locate the parent of the
# resource provider tree. Only needs to be set in the rare case when the
# hypervisor name is different from the DEFAULT.host config option value as
# known by the nova-compute managing that hypervisor or if multiple
# hypervisors are served by the same OVS backend.
# The default is :0 which means no packet processing capacity is guaranteed
# on the hypervisor named according to DEFAULT.host.
# resource_provider_packet_processing_without_direction = :0
#
# Similar to the resource_provider_packet_processing_without_direction but
# used in case the OVS backend has hardware offload capabilities. In this a
# case the format is
# <hypervisor>:<egress_packet_rate>:<ingress_packet_rate> which allows
# defining packet processing capacity per traffic direction. The direction
# is meant from the VM perspective. Note that the
# resource_provider_packet_processing_without_direction and the
# resource_provider_packet_processing_with_direction
# are mutually exclusive options.
# resource_provider_packet_processing_with_direction = :0:0
#
# Key:value pairs to specify defaults used while reporting packet rate
# inventories. Possible keys with their types: allocation_ratio:float,
# max_unit:int, min_unit:int, reserved:int, step_size:int
# resource_provider_packet_processing_inventory_defaults = {
#   'allocation_ratio': 1.0, 'min_unit': 1, 'step_size': 1, 'reserved': 0}

注意

请注意,虽然带宽清单是在 [ovs]resource_provider_bandwidths 配置选项中定义的每个 OVS 网桥,但包处理能力应用于整个 OVS 实例。

注意

请注意,为了支持两种 OVS 部署场景,有两个互斥的配置选项。一个用于处理具有无方向资源清单的正常 OVS 部署,另一个用于处理具有方向感知资源清单的硬件卸载 OVS 部署。

OVS 代理 heartbeat RPC 消息的 configurations 字段已扩展,以将包处理能力配置报告给 Neutron 服务器。如果超visor 名称未从配置中省略,则将其解析为 RPC 消息中的 [DEFAULT]host 的值。

Neutron 服务器将包处理能力报告为新的 NET_PACKET_RATE_KILOPACKET_PER_SECNET_PACKET_RATE_[E|I]GR_KILOPACKET_PER_SEC 资源清单到 Placement 上的 OVS 代理资源提供商 (RP),类似于今天报告带宽资源的方式。现在 OVS 代理 RP 具有资源清单,Neutron 服务器还需要将与桥接 RP 上报告的相同 CUSTOM_VNIC_TYPE_ 特征报告到 OVS 代理 RP。这些是代理配置为支持的 vnic 类型。请注意,由于该资源未在可用的 physnet 之间拆分,因此不需要 CUSTOM_PHYSNET_ 特征用于包速率调度。

注意

关于在 OVS 网桥级别报告资源的替代方案,请参阅 Nova 规范

最小包速率 QoS 策略规则

需要一个新的 Neutron API 扩展来扩展 QoS API,以使用新的 minimum_packet_rate 规则类型。此规则具有与 minimum_bandwidth 规则类似的结构和语义

qos_apidef.SUB_RESOURCE_ATTRIBUTE_MAP = {
    'minimum_packet_rate_rules': {
        'parent': qos_apidef._PARENT,
        'parameters': {
            qos_apidef._QOS_RULE_COMMON_FIELDS,
            'min_kpps': {
                'allow_post': True,
                'allow_put': True,
                'convert_to': converters.convert_to_int,
                'is_visible': True,
                'is_filter': True,
                'is_sort_key': True,
                'validate': {
                    'type:range': [0, db_const.DB_INTEGER_MAX_VALUE]}
            },
            'direction': {
                'allow_post': True,
                'allow_put': True,
                'is_visible': True,
                'default': constants.EGRESS_DIRECTION,
                'validate': {
                    'type:values': (
                        constants.ANY_DIRECTION,
                        constants.INGRESS_DIRECTION,
                        constants.EGRESS_DIRECTION
                    )
                }
            }
        }
    }
}

规则的 direction,入站或出站,是从 Nova 服务器的角度考虑的。因此,1 kpps 入站保证意味着系统确保至少 1000 个包可以每秒通过给定的端口进入 VM。

为了支持两种不同的 OVS 部署场景,我们需要允许将新 QoS 策略规则的 direction 字段设置为 any,以指示此 QoS 规则在资源会计是无方向的正常 OVS 情况下有效。

在同一 QoS 策略中混合方向感知和无方向的最小包速率规则将始终导致调度期间出现 NoValidHost 错误,因为单个 OVS 实例不能同时具有方向感知和无方向的清单。因此,Neutron 服务器将使用 HTTP 400 拒绝此类规则创建。

注意

关于仅具有方向感知 QoS 规则类型的替代方案,请参阅 Nova 规范

上述定义允许使用 PUT 请求更新 min_kpps 值。如果该规则已经用于绑定的端口,则此请求将使用 HTTP 501 拒绝,类似于最小带宽规则的行为。

Neutron 通过端口的 resource_request 向管理员客户端提供必要的信息,以允许客户端保持放置资源分配的一致性,从而实现调度时间保证。Nova 将使用此信息来做到这一点。但是,Neutron 或 Nova 无法强制执行另一个可以绑定端口的客户端也正确分配这些端口的资源。这不在本规范的范围内。

这导致以下新的 API 资源和操作

  • GET /v2.0/qos/policies/{policy_id}/minimum_packet_rate_rules

    列出 QoS 策略的最小包速率规则

    响应

    {
      "minimum_packet_rate_rules": [
          {
              "id": "5f126d84-551a-4dcf-bb01-0e9c0df0c793",
              "min_kpps": 1000,
              "direction": "egress"
          }
      ]
    }
    
  • POST /v2.0/qos/policies/{policy_id}/minimum_packet_rate_rules

    创建最小包速率规则

    请求

    {
      "minimum_packet_rate_rule": {
          "min_kpps": 1000,
          "direction": "any",
      }
    }
    

    响应

    {
      "minimum_packet_rate_rule": {
          "id": "5f126d84-551a-4dcf-bb01-0e9c0df0c793",
          "min_kpps": 1000,
          "direction": "any"
      }
    }
    
  • GET /v2.0/qos/policies/{policy_id}/minimum_packet_rate_rules/{rule_id}

    显示最小包速率规则详细信息

    响应

    {
      "minimum_packet_rate_limit_rule": {
          "id": "5f126d84-551a-4dcf-bb01-0e9c0df0c793",
          "min_kpps": 1000,
          "direction": "egress"
      }
    }
    
  • PUT /v2.0/qos/policies/{policy_id}/minimum_packet_rate_rules/{rule_id}

    更新最小包速率规则

    请求

    {
      "minimum_packet_rate_rule": {
          "min_kpps": 2000,
          "direction": "any",
      }
    }
    

    响应

    {
      "minimum_packet_rate_rule": {
          "id": "5f126d84-551a-4dcf-bb01-0e9c0df0c794",
          "min_kpps": 2000,
          "direction": "any",
      }
    }
    
  • DELETE /v2.0/qos/policies/{policy_id}/minimum_packet_rate_rules/{rule_id}

    删除最小包速率规则

注意

本规范有意不建议添加旧式 /v2.0/qos/policies/{policy_id}/minimum_packet_rate_rules/{rule_id} API,因为 Neutron 团队更喜欢未来的 /v2.0/qos/alias_bandwidth_limit_rules/{rule_id} 风格的 API。旧式 API 仅为了与已经存在的 QoS 规则的向后兼容性而保留。但是,由于此新 API 没有旧式对应项,因此从资源名称中删除了“alias”前缀。

为了持久化新的 QoS 规则类型,需要一个新的 DB 表 qos_minimum_packet_rate_rules

op.create_table(
    'qos_minimum_packet_rate_rules',
    sa.Column('id', sa.String(36), nullable=False,
              index=True),
    sa.Column('qos_policy_id', sa.String(36),
              nullable=False, index=True),
    sa.Column('min_kpps', sa.Integer()),
    sa.Column('direction', sa.Enum(constants.ANY_DIRECTION,
                                   constants.EGRESS_DIRECTION,
                                   constants.INGRESS_DIRECTION,
                                   name="directions"),
              nullable=False,
              server_default=constants.EGRESS_DIRECTION),
    sa.PrimaryKeyConstraint('id'),
    sa.ForeignKeyConstraint(['qos_policy_id'], ['qos_policies.id'],
                            ondelete='CASCADE')
)

这意味着添加了一个新的 QosMinimumPacketRateRule DB 模型和 OVO。

请求包速率资源

今天,Neutron 端口的 resource_request 字段用于表达端口的资源需求。此信息是从附加到端口的 QoS 策略规则计算得出的。到目前为止,只有最小带宽规则被用作请求资源的来源。但是,当添加新的最小包速率规则作为额外的请求资源来源时,需要更改 resource_request 字段的结构和实际逻辑。

当前结构仅允许描述一组资源和特征。

{
    "required": [<CUSTOM_PHYSNET_ traits>, <CUSTOM_VNIC_TYPE traits>],
    "resources":
    {
        <NET_BW_[E|I]GR_KILOBIT_PER_SEC resource class name>:
        <requested bandwidth amount from the QoS policy>
    }
},

当前结构仅允许描述一组资源和特征。但是,如上所述,包处理资源清单报告在 OVS 代理 RP 上,而带宽资源报告在 OVS 网桥 RP 上。这意味着需要将这些资源作为单个 RP 中的单个资源组分开,因为 Placement 中的一个资源组始终从单个 RP 分配。

因此,建议对 resource_request 字段使用以下结构

{
    "request_groups":
    [
        {
            "id": <some unique identifier string of the group>
            "required": [<CUSTOM_VNIC_TYPE traits>],
            "resources":
            {
                NET_PACKET_RATE_[E|I]GR_KILOPACKET_PER_SEC:
                <amount requested via the QoS policy>
            }
        },
        {
            "id": <some unique identifier string of the group>
            "required": [<CUSTOM_PHYSNET_ traits>,
                         <CUSTOM_VNIC_TYPE traits>],
            "resources":
            {
                <NET_BW_[E|I]GR_KILOBIT_PER_SEC resource class name>:
                <requested bandwidth amount from the QoS policy>
            }
        },
    ]
}

列表中的每个字典代表需要从单个资源提供商满足的一组资源和特征。例如,来自带宽的网桥 RP,或来自 OVS 代理 RP 的包速率。这解决了 RP 分离的问题。但是,另一方面,端口仍然需要从相同的整体实体(例如,来自 OVS)分配资源,通过从 OVS 代理 RP 分配包处理容量,从 OVS 网桥 RP 分配带宽。换句话说,从 OVS 分配包处理请求,从 SRIOV 代理管理的 SRIOV PF 分配带宽请求是无效的。Placement 已经知道资源清单之间的结构连接,因为 OVS 网桥 RP 是 OVS 代理 RP 的子节点,而 PF RP 不是。但是,默认情况下,当请求两个资源组时,Placement 仅强制它们从 RP 树中的两个 RP 满足,但它不会强制执行这些 RP 之间的子树关系。为了表达这两个资源组应该从同一子树中的两个 RP 满足(在本例中,从 OVS 代理 RP 根的子树),Placement 需要额外的信息。Placement 支持一个 same_subtree 参数,可以表达我们需要的内容。Neutron 需要在 resource_request 字典中添加一个新的顶级键 same_subtree。例如:

{
    "request_groups":
    [
        {
            "id": "port-request-group-due-to-min-pps",
            # ...
        },
        {
            "id": "port-request-group-due-to-min-bw",
            # ...
        },
    ],
    "same_subtree":
    [
        "port-request-group-due-to-min-pps",
        "port-request-group-due-to-min-bw"
    ]
}

id 字段是一个字符串,假定对于 neutron 部署中的每个端口的每个组都是唯一的。为了实现这一点,将通过 UUID5 方法组合 port_id 和为该组做出贡献的 QoS 规则的 UUID 来为每个组生成一个新的 UUID。这样,我们就可以获得组的唯一且确定性 ID,因此我们不需要在 Neutron 中持久化组 ID。

我们讨论并拒绝了另外两个替代方案

  • 现在忽略相同的子树问题。QoS 配置 已经要求管理员创建一个设置,其中不同的机制驱动程序通过 vnic_type_prohibit_list 配置选项支持不相交的 vnic_type 集。端口始终请求特定的 vnic_type,并且支持的 vnic_type 是不相交的,因此端口的资源请求始终通过 CUSTOM_VNIC_TYPE_ 特征选择特定的网络后端。仅将包速率资源添加到 OVS 后端,因此如果包含包速率和带宽资源的端口的资源请求的调度成功,则我们知道包速率资源是由 OVS 代理 RP 满足的。因此,vnic_type 与 OVS 后端匹配。带宽请求也由具有相同 vnic_type 的后端满足,因此也由 OVS 后端满足。如果我们选择此方向,则需要仔细记录上述假设,以便未来的开发人员可以检查由于新功能添加而使任何陈述无效的情况。

  • 假设 Nova 中的所有单个端口的资源组都必须从同一子树满足。然后 resource_request 中不需要 same_subtree 键,但 Nova 会隐式地假设它存在并相应地生成 Placement 请求。

所选的替代方案更具未来证明性。当 IP 分配池处理转换为使用资源请求时,该资源将来自共享资源提供商,因此 Nova 无法隐式假设整个 resource_request 的 same_subtree。

请注意,当前的 Neutron API 没有定义字段的确切结构,它只是一个字典,因此从 Neutron 的角度来看,不需要新的 API 扩展来更改结构。但是 Nova 需要知道 Neutron 将使用哪种格式。我们有替代方案

  • 一个新的 Neutron API 扩展:它可以发出 API 变更的信号。Nova 已经习惯于检查扩展列表,以查看 Neutron 中是否启用了某些功能。

  • resource_request 字典中添加一个新的顶级 resource_request_version 字段可以发出当前和未来的版本。Nova 需要检查该字段是否存在于 resource_request 中,并有条件地解析字典的其余部分。

  • 让 Nova 基于结构进行猜测。新的顶级 request_groups 键可以用于 Nova 以检测新的格式。

API 扩展是选择的替代方案,因为它感觉像是 Neutron 中发出 API 变更的标准方式。

端口绑定

目前 Nova 发送端口的 resource_request 所满足的资源提供程序的 UUID,位于 binding:profileallocation 键中。端口绑定逻辑使用此信息将端口绑定到与端口资源分配自相同的物理设备或 OVS 桥接。这是必要的,因为允许存在多个这样的设备,从 Neutron 的角度来看,它们是等效的,即它们连接到相同的 physnet 并支持相同的 vnic_type。当端口具有两组资源请求(一组用于带宽,另一组用于包速率)时,资源分配将从多个 RP 满足。为了支持这一点,我们需要更改 allocation 键的结构。由于 resource_request 中的每组资源现在都有一个唯一的标识符,Nova 可以在 binding:profileallocaton 键中发送一个 <group.id>: <RP.uuid> 的映射,以便 Neutron 获知哪个 RP 提供了哪一组资源。这意味着 allocation 键中具有以下结构

{
    <uniq id of the group requesting min_pps>:
        <OVS agent RP UUID>,
    <uniq id of the group requesting min_bw>:
        <OVS bridge RP UUID or SRIOV PF RP UUID>,
}

只有存在于 resource_request 字段中的 id 中的 group id 键才存在于此字典中。

或者,我们可以暂时忽略这个问题。目前只有 OVS 支持包速率库存,并且包速率库存是整个 OVS 实例的全局库存。单个 binding:host_id 始终映射到单个 OVS 实例,因此 Neutron 始终可以假设最小包速率资源是从请求绑定到端口的计算节点所属的 OVS 代理资源提供程序分配的。因此,端口绑定逻辑不需要包速率资源提供程序的 UUID。因此,可以将 allocation 键保留为仅传递带宽资源提供程序的 UUID(如果有)。

绑定端口上的 QoS 策略更改

Neutron 支持更改绑定端口上的 QoS 策略,即使这意味着由于由 minimum_bandwidth QoS 规则指示的资源请求更改而需要资源分配更改。此实现需要扩展到处理不仅 minimum_bandwidth 规则,还处理 minimum_packet_rate 规则的更改。

当前的兼容性矩阵是

当前场景

场景

结果

将 port.qos_policy_id 从 None 更新为具有最小带宽规则的有效 QoS 策略

已接受,但资源分配不会进行调整,因为这需要完整的调度。如果 VM 稍后调度到另一个主机(即迁移、调整大小、撤离),则该调度将考虑新的资源请求。

将 port.qos_policy_id 从没有最小带宽规则的 QoS 策略更新为具有最小带宽规则的 QoS 策略

已接受,但资源分配不会进行调整。请参见上述说明。

将 port.qos_policy_id 从具有最小带宽规则的 QoS 策略更新为具有最小带宽规则的 QoS 策略,但 min_kbps 值或方向不同。

受支持。如果当前资源提供程序上的带宽分配可以使用新的 QoS 值和方向进行更新,则接受。

将 port.qos_policy_id 从具有最小带宽规则的 QoS 策略更新为没有最小带宽规则的 QoS 策略。

受支持。Placement 中为此端口的带宽资源分配将被删除。

在添加 minimum packet rate 规则后,Neutron 将有两个需要 Placement 资源分配的 QoS 策略规则。这增加了更多需要处理的情况。上述场景仍然适用于最小带宽,并且可以类似地应用于具有单个最小包速率规则的 QoS 策略。更复杂的情况如下所述

新场景

场景

结果

旧的 QoS 策略同时具有最小带宽和包速率规则,新的策略也同时具有这两种规则,但 min_kbps 和/或 min_kpps 值不同。

受支持。如果当前资源提供程序上的带宽分配可以使用新的 QoS 值进行更新,则接受。更改最小带宽规则的方向或方向感知最小包速率规则的方向也受支持。但是,从非方向性最小包速率规则更改为方向性包速率规则,反之亦然,不受支持并被拒绝。

旧的 QoS 策略同时具有最小带宽和包速率规则,新的策略规则较少。要么只有最小带宽,要么只有最小包速率,要么两者都没有。

受支持。与删除的规则相关的分配将在 Placement 中删除。

新的 QoS 策略与旧策略相比,增加了最小带宽或包速率规则或两者都有。

已接受,但资源分配不会进行调整,因为这需要完整的调度。如果 VM 稍后调度到另一个主机(即迁移、调整大小、撤离),则该调度将考虑新规则的资源请求。

升级

需要数据库模式升级以添加新的 qos_minimum_packet_rate_rules 表。

Neutron 服务器 - OVS 代理通信中的更改意味着,在滚动升级期间,升级的 OVS 代理可能会在心跳中发送新的包处理能力相关键,而旧代理不会发送它。因此,Neutron 服务器需要将此新键视为可选的。

对新的 minimum_packet_rate QoS 策略规则以及 resource_requestallocation 字段的更改需要一个新的 API 扩展。我们需要支持升级场景,其中 Neutron 已经升级到 Yoga,但 Nova 仍然在 Xena 版本上。但是,这不需要 Neutron 中的任何额外逻辑,因为 Nova 已经在 Xena 中支持此 API 扩展。

测试

  • 单元测试。

  • 功能测试:代理-服务器交互。

  • Tempest 场景测试:端到端功能测试。

文档

参考资料