为 Nova 实时迁移提供端口绑定信息

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

Nova 实时迁移包含 3 个阶段

  • pre_live_migration - 在迁移开始前执行;迁移目标已知,但实例仍驻留在源主机上。

  • live_migration_operation - 迁移本身,包含 2 个子阶段

    • 在虚拟机在迁移目标计算主机上运行之前。

    • 虚拟机在目标计算主机上运行之后,但迁移仍在进行中(后复制迁移)。

  • post_live_migration - 在迁移完成后执行;源虚拟机不再存在。用于完成迁移。

目前,端口绑定发生在迁移期间的目标计算主机上,被认为是 post_live_migration 阶段。不幸的是,这在流程中太晚了,因为在某些情况下,Nova 在 pre_live_migration 阶段需要此信息。我们无法将端口绑定移动到 pre_live_migration 阶段,因为原始端口绑定将被删除,由于实例仍在原始主机上处于活动状态,这将导致问题。更多详细信息包含在“替代方案”部分。

问题描述

为了改进实时迁移,需要 Neutron 在 pre_live_migration 阶段允许在目标计算主机上进行端口绑定,而无需删除源计算主机上的原始端口绑定。

该方案是在源主机和目标主机上都具有端口绑定,其中只有一个端口绑定处于活动状态。在实例迁移完成后,目标端口绑定将被激活,源端口绑定将被停用,但不会删除。源计算主机上的原始端口绑定将保留,以便可能将实例移回源主机,并且仅在 post_live_migration 阶段删除。

这些问题可以分为 2 类

#1 如果目标上的端口绑定失败,则保持源实例运行

  • 端口绑定失败时实例错误状态

    目前,端口绑定在迁移完成后在 post_live_migration 阶段触发。如果端口绑定失败,则实例将陷入错误状态。

    解决方案是将端口绑定移动到 pre_live_migration 阶段,在那里可以更早地失败端口绑定。这将防止实例在迁移完成后陷入错误状态,并在 pre_live_migration 阶段失败迁移。

    将端口绑定移动到 pre_live_migration 阶段的问题是,某些驱动程序即使实例仍处于活动状态,也会关闭源计算主机上的端口绑定。为了实现更干净的迁移解决方案,需要修改 Neutron 以允许源主机和目标主机上的端口绑定处于活动/非活动状态。

#2 处理主机之间端口绑定细节的差异

  • 在运行不同 l2 代理的主机之间进行实时迁移

    另一个需要考虑的情况是迁移发生在运行不同 l2 代理的两个主机之间。这里的要求是在执行迁移之前更新 Nova 中的实例定义。对于 libvirt,Nova 将使用目标接口定义更新 domain.xml。有关更多信息,请参阅 [3][4]

    一个特殊情况是迁移发生在具有不同防火墙驱动程序的代理之间,例如从运行 ovs hybrid-fw 驱动程序的主机迁移到运行新的 ovs conntrackd 防火墙驱动程序的主机。

    只有在使用相同 VNIC 类型的源绑定和目标绑定时才允许进行此类迁移。

  • 使用 MacVTap 代理进行实时迁移,当使用不同的 physnet 映射时

    MacVTap 在某些情况下对实时迁移存在限制 [1],并且需要在开始迁移之前更新实例定义(libvirt domain.xml)。

    更新实例定义发生在 live_migration_operation 阶段,在实例在目标计算主机上运行之前。为了使此操作成功,需要两个端口绑定,一个在源主机上处于活动状态,一个在目标主机上处于非活动状态。

提议的变更

对于迁移,Nova 需要在 pre_live_migration 阶段从目标计算主机获取端口绑定信息。可以通过允许计算端口绑定到迁移源和迁移目标主机来实现。

可以通过以下步骤实现

#1 扩展端口下的现有 API 实体,允许 CRUD 绑定。

#2 更新 ML2 以支持更改。

#3 更新数据库以支持更改。

Nova 的使用

Nova 将利用扩展的 API,潜在流程如下

  • pre_live_migration:创建目标主机的非活动绑定。

  • live_migration_operation:使用从非活动绑定收集的信息修改实例定义。

  • live_migration_operation:一旦实例处于活动状态,将非活动绑定设置为活动状态,并将先前的活动绑定设置为非活动状态。

  • post_live_migration:删除源计算主机上的非活动绑定。

../../../_images/nova-live-migration-flow.png
  • 如果在实例在目标上处于活动状态后执行回滚:从 Neutron 的角度来看,如果绑定在目标主机上处于活动状态,Nova 需要将源绑定设置回活动状态

    PUT /v2.0/ports/{port_id}/bindings/{host_id}/activate
    

有关 Nova 实现的更多详细信息,请参阅相关的 Nova Blueprint [3] 及其 Spec [4]。Neutron 不会决定 Nova 实时迁移实现的 capabilities,并将支持任何一种路径,无论是回滚还是不回滚。

端口的绑定 API 扩展

列出绑定

GET /v2.0/ports/{port_id}/bindings

{
    "bindings": [
        {
            "host_id": "source-host_id",
            "vif_type": "ovs",
            "vif_details": {
                "port_filter": true,
                "ovs_hybrid_plug": true
                },
            "profile": {},
            "vnic_type": "normal",
            "status": "active"
        },
        {
            "host_id": "target-host_id",
            "vif_type": "bridge",
            "vif_details": {
                "port_filter": true,
                },
            "profile": {},
            "vnic_type": "normal",
            "status": "inactive"
        },
    ]
}
响应参数

参数

风格

类型

描述

bindings

plain

xsd:list

一个 binding 对象列表

更多参数请参见 显示绑定

列出绑定的重要关键特性

  • 计算绑定将当前列出,对不受支持的绑定的请求将返回“NotImplemented”,直到引入该 capability 为止。

  • 所有绑定都将被列出,并且当返回许多绑定时将使用分页。

显示绑定

GET /v2.0/ports/{port_id}/bindings/{host_id}

响应参数

参数

风格

类型

描述

binding

plain

xsd:dict

一个 binding 对象

host_id

plain

xsd:string

分配端口的主机名。

vif_type

plain

xsd:string

在此端口绑定期间确定的 VIF 类型

vif_details

plain

xsd:dict

包含此特定绑定的其他详细信息的字典。详细信息由机制驱动程序设置。

vnic_type

plain

xsd:string

此端口绑定的 VNIC 类型。

profile

plain

xsd:dict

包含 vif 配置文件。

status

plain

xsd:String

绑定的状态 状态用法

{
    "binding": {
         "host_id": "target-host_id",
         "vif_type": "target-vif-type",
         "vif_details": {
             "port_filter": true,
             },
         "vnic_type": 'NORMAL',
         "profile": {},
         "status": "active"
     }
}

显示绑定的重要关键特性

  • DVR 端口在此资源中显示,将显示分布式端口的实际 vif_type,如它们存储在 DistributedPortBindings 中,即 ovs。

创建绑定

POST /v2.0/ports/{port_id}/bindings

请求参数

参数

风格

类型

描述

binding

plain

xsd:dict

一个 binding 对象

host_id(必需)

plain

xsd:string

分配端口的主机名。

vnic_type(可选,默认 = ‘normal’)

plain

xsd:string

此端口绑定的 VNIC 类型。

profile(可选)

plain

xsd:dict

包含 vif 配置文件。

{
    "binding": {
        "host_id": "target-host_id"
    }
}

响应参数

参见 列出绑定

{
    "binding": {
        "host_id": "target-host_id",
        "vif_type": "ovs",
        "vif_details": {
            "port_filter": true,
            "ovs_hybrid_plug": true
            },
         "vnic_type": 'NORMAL',
         "profile": {},
         "status": "active"
    }
}

如果绑定失败,应返回新的 4xx 或 5xx 返回代码。这与今天不同,失败的绑定返回 2xx 响应代码,并且 vif_type 设置为“binding_failed”。

更新/创建绑定的重要关键特性

  • 默认情况下,创建端口绑定时状态将为活动状态。如果创建绑定,不存在,并且已经存在活动绑定,则绑定将默认为非活动状态,需要操作员激活新的绑定。

  • 如果正在添加的绑定已经存在,将返回 4xx。

  • 计算端口一次只能有一个活动绑定。这不是 Neutron 的强制执行,而是 PortBinding 周围操作的结果。此功能扩展了具有多个绑定的 capability,但仅允许计算端口有一个活动绑定。

  • 此时,创建绑定将限制为计算端口。

  • 现有 API 未受影响,将返回 host_id:{host_id} 作为当前活动绑定。

  • 激活非活动计算绑定将停用当前活动绑定。

更新绑定

PUT /v2.0/ports/{port_id}/bindings/{host_id}

请求参数

参数

风格

类型

描述

binding

plain

xsd:dict

一个 binding 对象

所有创建参数都适用于更新。参见 创建绑定

{
    "binding": {
        "vnic_type": 'NORMAL',
        "profile": {}
    }
}

响应参数

参见 显示绑定

{
    "binding": {
        "host_id": "target-host_id",
        "vif_type": "ovs",
        "vif_details": {
            "port_filter": true,
            "ovs_hybrid_plug": true
            },
        "vnic_type": 'NORMAL',
        "profile": {"foo": "bar"},
        "status": "active"
    }
}

绑定失败时,应返回 4xx 或 5xx 返回代码。

激活非活动绑定

PUT /v2.0/ports/{port_id}/bindings/{host_id}/activate

响应参数

参见 显示绑定

{
    "binding": {
        "host_id": "target-host_id",
        "vif_type": "ovs",
        "vif_details": {
            "port_filter": true,
            "ovs_hybrid_plug": true
            },
        "vnic_type": 'NORMAL',
        "profile": {"foo":"bar"},
        "status": "active"
    }
}

激活绑定的重要关键特性

  • 激活处于非活动状态的计算绑定将停用现有的活动绑定,因为计算端口一次只能有一个绑定处于活动状态。

  • 操作将限制为计算端口。

  • 尝试激活现有的活动绑定将返回 4xx。

  • 激活绑定失败时返回 5xx。

删除绑定

DELETE /v2.0/ports/{port_id}/bindings/{host_id}

此操作不接受请求体,也不返回响应体。

删除绑定的重要关键特性

  • 可以删除活动/非活动绑定。

  • 删除活动计算端口绑定,其中存在非活动绑定,不会激活绑定。操作员需要显式激活绑定。

现有 API 与新 API 的重叠

现有 API 的所有功能都包含在新 API 中。本节描述了重叠。

现有 API 与新 API 的重叠

现有 API

新的 API

显示具有活动绑定的端口

n/a

创建端口:直接使用活动绑定

n/a

更新端口:添加 host_id(添加活动绑定)

添加绑定

创建端口:没有绑定

n/a

更新端口:更改 host_id(重新触发另一个主机的端口绑定)

更新绑定

更新端口:将 host_id 设置为‘’(删除活动绑定)

删除活动绑定

删除端口:删除所有绑定的端口

n/a

现有 API 的影响

对现有 API 的轻微调整

  • 显示端口将仍然像今天一样显示绑定。对于计算端口,它只会显示活动绑定。

  • 创建端口将创建未绑定的绑定,状态为活动状态。

  • 使用 host_id 更新端口将仍然重新触发主机的端口绑定。update_port() 的区别将仅对活动绑定采取行动。

  • 通过现有端口绑定扩展触发绑定时,端口绑定失败时 vif_type 设置为“binding_failed”,并且仍然使用 http 代码 2xx。

API 可见性

普通用户不应能够触发任何创建/更新/删除/激活操作。这应该只能通过一些特殊的服务用户或管理员角色来完成。

子资源扩展

Neutron ports 将使用子资源 bindings 扩展,具有成员名称 port 以保留 portbindings 和 ports 扩展。新的子资源扩展将是 portbindings_extended.py,并且具有父资源 ports

以下方法将添加到新创建的服务插件 bindings_plugin.py

  • get_port_bindings()

  • get_port_binding()

  • create_port_binding()

  • update_port_binding()

  • delete_port_binding()

  • update_port_binding_activate()

ML2 更改

现有方法 create_port()update_port() 需要更新以支持仅对状态为 active 的绑定采取行动。此外,将在 neutron-lib 中引入一个新的状态 inactive 用于 PortBinding。

状态用法

PortBinding 中的状态列将存储一个额外的状态“inactive”,其中当前状态为“active”和“down”。Neutron-lib 只需要添加 PORT_BINDING_STATUS_ACTIVE 和 PORT_BINDING_STATUS_INACTIVE。

from neutron_lib import constants as const

const.PORT_BINDING_STATUS_ACTIVE
const.PORT_BINDING_STATUS_INACTIVE

创建/更新/删除端口

为了支持新的子资源,将在端口下引入新的方法,但当前的 create_port()update_port()delete_port() 将被修改为仅对 PortBinding 表中的 active 绑定起作用。

目前,create_port() 在 PortBinding 中添加一个未绑定的空绑定,为了支持此规范,将进行以下更改

  • 在 PortBinding 表中创建一个状态为 active 的未绑定绑定。

此外,update_port() 将针对 active 状态进行调整,具体更改如下

  • 更新将仅更改 PortBinding 表中 active 绑定的绑定信息。

最后,delete_port() 将针对 active 状态进行调整,具体更改如下

  • 删除将仅对 PortBinding 表中的 active 绑定起作用。

数据模型变更

PortBinding 表将扩展主键到 column host,允许基于 port_idhost 选择绑定。

此外,将在扩展中引入一个 status 列,其中状态 activedowninactive 将作为值。

在线升级,Blueprint [9],需要将 host 添加到 primary_keys,以及为 Port Binding OVO 添加一个新字段 status。如果 Blueprint [10] 中的 push-notifications 首先被合并,并且 PortBinding 对象存在于 RPC 线上,则应增加对象版本。为 status 字段定义默认值将不需要在线数据迁移。

机制驱动程序变更

需要一个新的机制驱动程序方法来确定是否支持绑定事物的新方式。必须针对所有绑定级别进行验证,并允许回退到以前的方法。默认情况下,新方法将返回 unsupported。

除了 l2 代理(ovs、lb、sr-iov、macvtap)的树内机制驱动程序之外,还需要考虑以下驱动程序

  • l2pop(但是,有想法消除 l2pop)

  • ironic

  • 第三方机制驱动程序

激活 RPC 端口更新/删除

现有的 port_updateport_delete RPC 消息将被调整为在代理使用 get_devices_details_list_and_failed_devices 获取设备信息时,无论绑定状态如何,都发送特定于主机的绑定信息。这将允许发生以下附加管道:

  • 激活将导致 port_update,它将传递主机相关的绑定信息,并指示从 inactive 到 active 的过渡,在 get_devices_details_list_and_failed_devices 响应中。这将允许发送 GARP,更新拓扑到状态更改。

  • 激活将导致向源主机发送 port_delete rpc 调用,删除源 VIF。由于 Nova 无法发出删除端口,因为端口仍然存在于另一台主机上,因此需要完成此操作。绑定将保持在 inactive 状态,其中已填充绑定信息,但从代理的角度来看,端口将不存在。此外,从 active 到 inactive 的过渡将在 rpc 调用中指示,影响 update_device_list 不更新端口状态。

../../../_images/rpc-portbinding-flow.png

在 Blueprint [10] 下为端口实现 push-notifications 的情况下,get_devices_details_list_and_failed_devices 将不会针对过渡状态进行调整。相反,绑定过渡状态将作为端口对象的一部分发送到代理。其余操作相同。

其他变更

  • Neutron/Openstack Python Client 支持。

  • Neutron-Lib 支持新的常量 PORT_STATUS_INACTIVE,请参阅 激活非活动绑定

命令行客户端影响

需要在 OSC 中支持端口绑定。将添加以下内容

$ openstack port binding list {ARGS} <port>

$ openstack port binding show {ARGS} <port> <host>

$ openstack port binding create {ARGS} <port>

$ openstack port binding update {ARGS} <port> <host>

$ openstack port binding delete {ARGS} <port> <host>

$ openstack port binding activate {ARGS} <port> <host>

安全影响

无。

通知影响

无。

其他最终用户影响

无。

性能影响

不应有性能影响。

IPv6 影响

无。

其他部署者影响

无。

开发人员影响

对 Nova 实时迁移的影响,并直接支持他们的努力。

社区影响

是的。此更改已在 ML 上讨论,在 Neutron 会议(尤其是 ML2)、中期会议和设计峰会上讨论过。

备选方案

另一种选择是使用端口下的当前资源来促进此更改以进行实时迁移。问题是,需要扩展当前的字典结构以适应“bindings”键。这可能会引起一些混淆,因为用户已经收到“bindings:profile”和各种其他值。

实现

负责人

主要负责人:* Jakub Libosvar

请在此处添加您的姓名,如果您想参与贡献,请参加 ML2 子团队会议

依赖项

无。

测试

Tempest 测试

添加场景测试,test_bindings.py,以遍历创建源迁移实例、在辅助主机上创建非活动绑定、创建辅助目标迁移实例、激活非活动绑定、停用源迁移活动绑定,然后验证连接是否仍然有效。

功能测试

将向 ml2/test_plugin.py 添加额外的功能测试,以扩展当前的端口绑定测试。这将适应在添加非活动绑定时进行状态检查。

API 测试

  • 绑定资源(CRUD)

  • 绑定资源(CRUD)以及在当前端口扩展下的活动绑定验证。

文档影响

是的。

用户文档

无。

开发人员文档

  • devrefml2_port_bindings.rst 中创建有关 active/inactive 绑定操作的详细说明。这应详细说明对 ml2 的更改以及扩展的 ports 资源。

参考资料