使用 Cinder 的新 Attach/Detach API¶
https://blueprints.launchpad.net/nova/+spec/cinder-new-attach-apis
使 Nova 使用 Cinder 的新 attach/detach API。
问题描述¶
在尝试实现 Cinder 多重挂载并尝试使所有驱动程序都能正常工作的实时迁移时,已经清楚 Cinder 和 Nova 的交互不够清晰,这导致了两个项目之间交互的错误和问题,以及在尝试演进这种交互时出现的问题。
让我们创建一个 Nova 和 Cinder 之间的新干净接口。
您可以在这里查看有关新 Cinder API 的详细信息:https://specs.openstack.org/openstack/cinder-specs/specs/ocata/add-new-attach-apis.html
用例¶
主要的 API 操作包括
将卷附加到实例,包括在生成实例期间,以及调用 os-brick 将卷后端(可选)连接到 hypervisor。连接是可选的,因为当主机到卷后端存在共享连接时,后端可能已经附加了。
从实例分离卷,包括(可选)调用 os-brick 将卷从 hypervisor 主机断开连接。
实时迁移实例,涉及在启动实时迁移之前在目标主机上设置卷连接,然后在实时迁移完成后删除源主机连接。如果回滚,则删除目标主机连接。
迁移和调整大小从这个新的视角来看,与实时迁移非常相似。
撤离,我们知道旧主机不再运行,我们需要将卷附加到新主机。
搁置,我们希望卷在逻辑上附加到实例,但我们也需要在实例卸载时将其从主机分离。
附加/分离附加到搁置实例的卷
使用交换卷在两个不同的 Cinder 后端之间迁移卷。
特别是,请注意
卷附加特定于主机 uuid、实例 uuid 和卷 uuid
当卷标记为 multi_attach=True 时,您可以对同一卷进行多次附加,到不同的实例(在同一主机或不同主机上)。
对于相同的实例 uuid 和卷 uuid,即使 multi_attach=False,您也可以在两个不同的主机上拥有连接。这通常用于移动虚拟机时。
主机上的卷连接可以与其他连接到相同卷后端的卷共享,具体取决于所选驱动程序。因此,需要小心删除该连接,不要错误地添加两个连接,也不要过早地删除正在使用的连接。Cinder 需要向 Nova 提供额外的信息,特别是对于每个附加项,如果连接是共享的,如果是,则该连接当前与谁共享。
提议的变更¶
Cinder 现在具有两种不同的 attach/detach API 流。我们需要一种在不影响现有实例的情况下从旧 API 切换到新 API 的方法。
首先,我们需要确定何时使用新 API 是安全的。我们需要配置 Cinder v3 API,并且该端点应该具有微版本 v3.27。此外,我们应该仅在所有 nova-compute 节点都升级后才使用新 API。我们可以通过查找与我们添加对新 Cinder API 的支持相关的最低服务版本来检测这一点。请注意,这意味着我们可能需要增加服务版本,以便我们可以显式检测对新 Cinder API 的支持。
如果允许使用新 API,我们可以将其用于所有新的附加项。在添加新附加项时,我们
(api) 调用 attachment_create,不带 connector,在 API 调用返回之前。BDM 记录使用 attachment_id 更新。请注意,如果卷不是 multi_attach=True,它只会允许将一个 instance_uuid 与每个卷关联。虽然长期目标是启用 multi_attach,但此规范不会附加到任何具有 multi_attach=True 的卷。虽然我们仍然可以对该卷进行单个附加,但由于依赖 cinder 来限制卷的附加数量,为了安全起见,在 Nova 中完全实现该支持之前,我们不应允许任何附加项,如果 multi_attach=True。
(compute) 获取 connector 信息并使用它来调用 attachment_update。API 现在返回所有需要提供给 os-brick 以附加卷后端的信息,以及如何将 VM 附加到该连接到卷后端的连接方式。
(compute) 在实际连接到卷之前,我们需要等待卷准备就绪并完全配置。如果等待卷准备就绪超时,我们在这里失败并删除附加项。如果这是实例的首次启动,这将使实例进入 ERROR 状态。如果卷准备就绪,我们可以继续进行附加过程。
(compute) 使用 os-brick 连接到卷后端。如果有任何错误,请尝试调用 os-brick disconnect(以双重检查是否已完全清理),然后删除 Cinder 中的附加项。如果在回滚过程中出现任何问题,请将实例置于 ERROR 状态。
(compute) 现在后端已连接,并且卷已准备就绪,我们可以以通常的方式将后端连接附加到 VM。
对于分离
(compute) 如果 BDM 中设置了 attachment_id,我们使用新的分离流程,否则我们回退到旧的分离流程。新的流程是…
(api) 正常的检查以查看请求是否有效
(compute) 从 VM 分离卷,如果失败则在此处停止请求
(compute) 调用 os-brick 从卷后端断开连接
(compute) 如果成功,则调用 attachment_remove。如果出现错误,我们添加一个实例故障并将实例置于错误状态。
如上所述,我们可以使用 BDM 中 attachment_id 的存在来确定附加项是使用新流程还是旧流程进行的。从长远来看,我们希望将所有现有附加项迁移到新的样式附加项,但这留给以后的规范。
实时迁移¶
在实时迁移期间,我们首先通过确保卷已附加到源主机和目标主机来启动该过程。当卷为 multi_attach=False 时,并且我们即将开始实时迁移 VM1 时,您会遇到如下情况
+-------------------+ +-------------------+
| | | |
| +------------+ | | +--------------+ |
| |VM1 (active)| | | |VM1 (inactive)| |
| +---+--------+ | | +--+-----------+ |
| | | | | |
| | Host 1 | | | Host 2 |
+-------------------+ +-------------------+
| |
+-----------+----------+
|
|
+---------------------------+
| | |
| +---------+---------+ |
| | VolA | |
| +-------------------+ |
| |
| Cinder Backend 1 |
| |
+---------------------------+
请注意,在 cinder 中,我们将为这个 multi_attach=False 卷最终得到两个附加项
附加项 1:VolA,VM1,主机 1
附加项 2:VolA,VM1,主机 2
从逻辑上讲,我们对同一个非 multi-attach 卷有两个附加项。这两个附加项都与 vm1 相关,但对于实时迁移期间的源主机和目标主机,都有一个附加项。请注意,这两个附加项都与相同的实例 uuid 相关联,这就是为什么即使 multi_attach=False,也允许这两个附加项的原因。
如果实时迁移成功,我们将删除附加项 1(即源主机附加项,主机 1),并只剩下附加项 2(即目标主机附加项,主机 2)。如果在源主机上 os-brick 断开连接时出现任何故障,我们将实例置于 ERROR 状态,并且不在 Cinder 中删除附加项。我们这样做是为了向操作员发出信号,表明需要手动修复某些内容。我们还将迁移置于错误状态,就像即使回滚干净也会发生的那样。
如果实时迁移由于中止或类似原因而失败,我们将执行上述操作的相反操作。我们尝试在主机 2 上 os-brick 断开连接。如果成功,我们删除附加项 2,否则将实例置于 ERROR 状态。如果回滚成功,我们将回到一个附加项,但在这种情况下,它是附加项 1。
因此,对于在 BDM 中具有 attachment_id 的卷,我们遵循 Cinder 的这种新的 API 调用流程
(destination) 获取 connector,并创建新的附加项
(destination) 附加卷后端
(source) 启动实时迁移
如果实时迁移成功
(source) 调用 os-brick 断开连接
(source) 如果成功,则删除附加项,否则将实例置于 ERROR 状态
如果实时迁移由于中止或类似原因而回滚
(destination) 调用 os-brick 断开连接
(destination) 如果成功,则删除附加项,否则将实例置于 ERROR 状态
迁移¶
与实时迁移类似,在迁移开始时,我们对源节点和目标节点都有附加项。在调用确认调整大小时,我们在源主机上执行分离,在目标主机上执行 revert resize 和分离。
撤离¶
当您调用撤离时,并且卷在 BDM 中具有 attachment_id,我们遵循此新的流程
(source) 源主机上什么也不发生,假设管理员已经围住了主机,并通过调用 force host down 确认了这一点。
(destination) 为任何附加卷创建第二个附加项,该附加项针对此 instance_uuid。
(destination) 遵循通常的卷附加流程
(destination) 现在删除旧的附加项,以确保 Cinder 清理与该连接相关的任何资源。这类似于我们今天调用 terminate_connection 的方式,除了我们必须在创建新的附加项后调用此项,以确保在整个撤离过程中始终将卷保留给该实例。
(operator) 如果源主机永远不会启动,则检测到已被撤离的实例(使用在调用撤离时创建的迁移记录)。这可能会导致 os-brick 未清理某些内容,但这相当安全,并且我们所处的状况与今天没有比糟糕。
搁置和取消搁置¶
当附加到实例的卷在 BDM 中具有 attachment_id 时,我们遵循对 Cinder API 的这种新的调用流程。请注意,一个实例获取搁置时,可以同时具有旧流程和新流程的卷附加到该实例。
在从旧主机卸载时,我们首先添加一个新的附加项(不设置 connector),然后以通常的方式执行旧附加项的分离。这确保了卷仍然附加到实例,但已安全地从我们正在卸载的主机分离。如果该分离失败,则实例应移动到 ERROR 状态。
同样,在取消搁置时,我们使用 connector 更新现有的附加项,然后继续使用通常的附加卷流程。
交换卷¶
对于交换卷,我们有一个主机、一个实例、一个设备路径,但有多个卷。
在本节中,我们讨论如果正在交换的卷在 BDM 中存在 attachment_id,因此我们遵循新的流程会发生什么。
首先,是 cinder 调用我们的 API 时的流程,其次是用户调用我们的 API 时的流程。这里涵盖了两种流程
调用 Nova 交换卷 API 以交换 uuid-old 与 uuid-new
新卷可能由用户在 cinder 中创建,并且用户可能发出了 Nova API 调用。
或者,用户可能调用了 Cinder 的 migrate volume API。这意味着 cinder 创建了新卷,并代表用户调用 Nova API。
(api) 为卷 uuid-new 创建新的附加项,如果无法创建该附加项则使 API 调用失败
(compute) 使用 connector 更新 cinder 附加项 uuid-new
(compute) os-brick 连接新卷。如果出现错误,我们像在附加期间失败一样处理此错误,并删除新卷的附加项
(compute) Nova 将卷 uuid-old 的内容复制到卷 uuid-new,在 libvirt 中,这是通过 rebase 操作完成的
(compute) 一旦复制完成,我们就从实例分离 uuid-old
(compute) 更新 BDM,以便 attachment_id 现在指向与 uuid-new 关联的附加项
(compute) 一旦旧卷分离,我们就执行 os-brick 断开连接
(compute) 如果成功,我们调用 cinder 的 migrate_volume_completion,参数为 (uuid-new, uuid-old)。如果断开连接失败,我们将实例置于 ERROR 状态。
(compute) 使用 migrate_volume_completion 返回的内容更新 BDM 中的新 volume-uuid。请注意,如果 cinder 调用了交换,它将删除旧卷,但重命名新卷以具有与旧卷相同的 uuid。如果有人调用 Nova,我们会得到 uuid-new,并更新 BDM 以反映更改。
因此,成功后,我们创建了对新卷的新附加项,并删除了对旧卷的附加项。
请注意,如果卷是 multi-attach,则交换操作将失败并且不允许。无论是由 Cinder 还是 Nova 启动,情况都将如此。随着时间的推移,我们可能会迁移到使用 attachment_id 而不是卷 id 的 Cinder 的 migrate_volume_completion API。此规范没有研究支持 multi-attach 需要什么,但这个问题似乎值得在此处注意。
备选方案¶
我们可能会在修复错误方面陷入“打地鼠”的困境。
我们应该以几种方式构建 API 交互。关键的替代方案之一是在 API 中添加大量的状态机复杂性,以便共享连接相关的锁定由 Cinder 在 API 层处理。虽然这使客户端更复杂,但似乎让 Nova 和其他客户端执行上述锁定更简单。
Nova 可以查找 attachment uuid 而不是将其存储在 BDM 中,在主机 uuid 未设置的期间,似乎更安全地存储 attachment uuid 以避免围绕哪个附加项与每个 BDM 关联的任何可能混淆。
在实时迁移期间,我们可以将额外的 attachment_id 存储在迁移数据中,而不是作为 BDM 的一部分。
我们可以继续将 connection_info 保存在 BDM 中,以便在分离卷时使用。虽然这似乎可能有助于避免 Nova 未收到通知的 connection_info 更改引起的问题,但这实际上是一种过早的优化。我们应该与 Cinder 和 os-brick 合作,以正确修复任何此类交互问题,从而帮助所有使用 Cinder 的系统。
数据模型影响¶
当使用新的 API 流程时,我们不再需要存储 connection_info,因为我们不需要将其传回给 Cinder。相反,我们只存储每个主机附加到的卷的 attachment_id,并且每当我们需要 connection_info 时,我们都从 Cinder 获取它。
当填充 attachment_id 时,我们使用新的流程来执行所有附加或分离操作。如果没有填充,我们使用旧流程。
REST API 影响¶
Nova 的 REST API 无变更。
安全影响¶
Nova 不再需要存储卷连接信息,但现在可以随时从 Cinder API 获取。
通知影响¶
无。
其他最终用户影响¶
无。
性能影响¶
不应影响性能。这里的重点是所有驱动程序的稳定性。Nova 和 Cinder 之间的 API 调用可能会略多,但预计不会显著影响性能。
其他部署者影响¶
要使用这种更稳定的 API 交互以及将依赖于此工作的新功能,必须将 Cinder 升级到支持新 API 的版本。
预计在完成此工作后的两个发布周期内,我们将停止支持旧版本的 Cinder。
开发人员影响¶
Nova 和 Cinder 的交互应该更容易理解。
实现¶
负责人¶
- 主要负责人
Matt Riedemann
- 其他贡献者
Lee Yarwood John Griffith
工作项¶
为了在本周期取得进展,我们需要将这项工作分解为小的补丁。总体策略是,我们最后实现新的连接方式,并且所有其他操作都依赖于 BDM 中包含 attachment_id,在连接代码合并之前,这不会成立。
使用 Cinder v3 API
检测是否包含新的 BDM 支持的微版本
分离新的 BDM/卷连接
重启 / 重建(使用 attachment_id 从 cinder 获取连接信息)
实时迁移
迁移
撤离
搁置和恢复
交换卷
连接(这意味着我们现在公开了所有以前的功能)
请注意,在支持多连接之前还有更多步骤,但这些内容留待未来的规范说明。
将旧的 BDM 迁移到新的 BDM 流程
添加对共享后端连接的显式支持
依赖项¶
依赖于 Cinder 的工作来添加新的 API。这在 Ocata 中已完成。
测试¶
我们需要对旧的和新的 Cinder 交互进行功能测试。这可能需要一个 grenade 作业,在升级之前将一个卷连接到实例,以便在升级后可以断开连接。
文档影响¶
我们需要添加关于更新的 Nova 和 Cinder 交互的良好开发者文档。
参考资料¶
历史¶
发布名称 |
描述 |
|---|---|
Pike |
引入 |