Nova 服务优雅关闭:第一部分¶
https://blueprints.launchpad.net/nova/+spec/nova-services-graceful-shutdown-part1
本文档提出 2026.1 周期内优雅关闭 积压规范 的规范 1。
Nova 服务无法优雅关闭。当服务停止时,也会停止所有正在进行的操作,这不仅会中断正在进行的操作,还可能导致实例进入不希望或无法恢复的状态。我们的目标是让服务停止处理新的请求,但在服务终止之前完成正在进行的操作。
问题描述¶
Nova 服务没有优雅关闭的方式意味着它们不会等待正在进行的操作完成。当启动关闭时,服务会等待 RPC 服务器停止,并等待消耗队列中的所有现有请求消息(RPC 调用/cast),但服务不会完成操作。
每个 Nova 计算服务都有一个运行的 worker,监听单个 RPC 服务器(topic: compute.<host>)。相同的 RPC 服务器用于新的请求以及其他计算或 conductor 服务通信的正在进行的操作。当启动关闭时,停止 RPC 服务器意味着它将停止处理新的请求,这没问题,但同时它也会停止正在进行的操作所需的通信。例如,如果正在进行实时迁移,源计算和目标计算会多次相互通信(同步和异步方式)。一旦计算服务上的 RPC 服务器停止,它就无法与另一个计算通信,导致实时迁移失败。这将导致系统和实例进入不希望或无法恢复的状态
用例¶
作为操作员,我希望能够优雅地关闭(SIGTERM)Nova 服务,这样就不会影响用户的正在进行的操作,也不会使资源保持可用状态。
作为操作员,我希望即使服务被优雅终止(SIGTERM),也能保持实例和其他资源处于可用状态。
作为操作员,我希望在 Nova 服务在 k8s pod 中运行时,能够充分利用 k8s pod 优雅关闭的优势。
作为用户,我希望在服务被优雅终止(SIGTERM)之前完成正在进行的操作。
提议的变更¶
有关详细上下文,请参阅优雅关闭 积压规范。
通过 RPC 分离新的和正在进行中的请求:¶
RPC 通信是服务完成特定操作的重要组成部分。在关闭期间,我们需要确保保持所需的 RPC 服务器/总线处于活动状态。如果停止 RPC 通信,那么与服务终止没有区别。
Nova 实现,并且此规范大量讨论了 RPC 服务器 start、stop 和 wait,因此让我们从 oslo.messaging/RPC 资源的角度简要介绍它们,以便以简单的方式理解本提案。你们中的大多数人可能已经知道这些,所以可以跳过此部分。
RPC 服务器
创建和 start()
它将在 oslo.messaging 侧创建所需的资源,例如 dispatcher、consumer、listener 和队列。
它将处理与所需的 exchange 的绑定。
stop()
它将禁用 listener 从队列中获取任何新消息的能力,但会向 dispatcher 发送已经获取的消息。
它将删除 consumer。
它不会删除消息代理侧的队列和 exchange。
它不会停止 RPC 客户端向队列发送新消息,但是它们不会被获取,因为 consumer 和 listener 已停止。
wait()
它将等待线程池完成调度所有已获取的消息。基本上,这将确保在 manager 上调用方法。
按服务分析以及所需的提议的 RPC 设计更改
以下服务与其他的 Nova 服务 RPC 服务器通信。由于它们没有自己的 RPC 服务器,因此不需要更改
Nova API
Nova metadata API
nova-novncproxy
nova-serialproxy
nova-spicehtml5proxy
Nova scheduler:不需要 RPC 更改。
请求处理:Nova scheduler 服务作为多个 worker 运行,每个 worker 都有自己的 RPC 服务器,但所有的 Nova scheduler worker 都会监听相同的 RPC topic 和队列
scheduler,采用 fanout 方式。当前,nova.service.py->stop() 调用 stop() 和 wait() 在 RPC 服务器上。一旦 RPC 服务器停止,它将停止监听任何新消息。但这不会对其他 scheduler worker 产生任何影响,它们会继续监听相同的队列并处理请求。如果任何 scheduler worker 停止,那么其他 worker 将处理请求。
响应处理:每当有 RPC 调用时,oslo.messaging 都会创建一个与唯一消息 ID 关联的另一个回复队列。此回复队列将用于将 RPC 调用响应发送给调用者。即使 RPC 服务器在此 worker 上停止,也不会影响回复队列。
我们仍然需要保持 worker 处于活动状态,直到所有响应都通过回复队列发送,为此,我们需要在 scheduler 服务中实现正在进行中的任务跟踪,但这将在步骤 2 中处理。
这样,停止 Nova scheduler worker 不会影响 scheduler 服务上的 RPC 通信。
Nova conductor:不需要 RPC 更改。
Nova conductor 二进制文件是一个无状态服务,可以生成多个 worker 线程。Nova conductor 的每个实例都有自己的 RPC 服务器,但所有的 Nova conductor 实例都会监听相同的 RPC topic 和队列
conductor。这允许 conductor 实例充当分布式 worker 池,以便停止单个 conductor 实例不会影响 conductor 实例池的 RPC 通信,允许其他可用的 worker 处理请求。每个 cell 都有自己的 conductor 池,这意味着只要任何 cell 有一个 conductor 处于活动状态,RPC 通信就可以继续正常工作,即使停止了一个或多个 conductor。请求和响应处理与 scheduler 中提到的方式相同。
注意
此规范不涵盖 conductor 单个 worker 的情况。这可能需要 conductor 的 RPC 设计,但需要进一步调查。
Nova compute:需要 RPC 设计更改
请求处理:Nova compute 在每个主机上运行一个 worker,每个主机上的 compute 都有自己的 RPC 服务器、listener 和单独的队列。它处理新的请求以及正在进行的操作所需的通信,都在同一个 RPC 服务器上。为了实现优雅关闭,我们需要分离新的请求和正在进行的操作的通信。这将通过在 compute 服务中添加一个新的 RPC 服务器来完成。
为了便于阅读,我们将为每个 RPC 服务器使用不同的术语
‘ops RPC 服务器’:这将用于新的 RPC 服务器,它将用于完成正在进行中的请求,并在关闭期间保持活动状态。
‘新请求 RPC 服务器’:这将用于当前的 RPC 服务器,它用于新的请求,将在关闭期间停止。
‘新请求 RPC 服务器’ 每 compute:此 RPC 服务器没有更改,但它将用于所有新的请求,以便我们可以在关闭期间停止它并停止 compute 上的新请求。
‘ops RPC 服务器’ 每 compute
每个 compute 将有一个新的 ‘ops RPC 服务器’,它将监听一个新的 topic
compute-ops.<host>。compute-ops名称用于因为它主要用于 compute 操作,但如果需要可以使用更好的名称。它将使用 ‘新请求 RPC 服务器’ 使用的相同的传输层/总线和 exchange。
它将创建自己的 dispatcher、listener 和队列。
两个 RPC 服务器都将绑定到相同的端点(相同的 compute manager),以便来自任一服务器的请求都由相同的 compute manager 处理。
此服务器将主要用于 compute-to-compute 操作和服务器外部事件。目的是在关闭期间保持此 RPC 服务器处于活动状态,以便可以完成正在进行的操作。
在关闭期间,nova.service 将等待 compute 告知它们是否完成了所有任务,以便它可以停止 ‘ops RPC 服务器’ 并完成服务关闭。
响应处理:无论请求来自哪个 RPC 服务器,每当有 RPC 调用时,oslo.messaging 都会创建一个与唯一消息 ID 关联的另一个回复队列。此回复队列将用于将 RPC 调用响应发送给调用者。即使 RPC 服务器在此 worker 上停止,也不会影响回复队列。
Compute 服务工作流程
SIGTERM 信号由 oslo.service 处理,它将在 nova.service 上调用 stop
nova.service 将停止 ‘新请求 RPC 服务器’,以便 compute 不再获取任何新请求。‘ops RPC 服务器’ 正在运行且处于活动状态。
nova.service 将等待 manager 发出信号,指示所有正在进行的操作都已完成。
一旦 compute 向 nova.service 发出信号,它将停止 ‘ops RPC 服务器’ 并继续服务关闭。
RPC 客户端
RPC 客户端保持为单例类,该类使用 topic
compute.<host>创建,这意味着默认情况下消息将通过 ‘新请求 RPC 服务器’ 发送。如果任何 RPC cast/call 想要通过 ‘ops RPC 服务器’ 发送消息,则需要在 client.prepare() 调用期间将
topic覆盖为compute-ops.<host>。如果 RPC 客户端检测到旧的 compute(基于 version_cap),则它将回退到将消息发送到 ‘新请求 RPC 服务器’ topic
compute.<host>。哪些 RPC cast/call 将使用 ‘ops RPC 服务器’ 将在实施期间决定,以便我们可以更好地判断哪些方法用于我们希望在关闭期间完成的操作。一个草案列表,我们可以在其中使用 ‘ops RPC 服务器’
注意
这是一个草案列表,可以在实施期间更改。
迁移
实时迁移
注意
我们将使用 ‘新请求 RPC 服务器’ 来处理 check_can_live_migrate_destination 和 check_can_live_migrate_source 方法,因为这是实时迁移请求尚未开始的初始阶段。如果在启动关闭之前发起实时迁移请求,则应拒绝迁移。
pre_live_migration()
live_migration()
prep_snapshot_based_resize_at_dest()
remove_volume_connection()
post_live_migration_at_destination()
rollback_live_migration_at_destination()
drop_move_claim_at_destination()
调整大小方法
冷迁移方法
服务器外部事件
重建实例
validate_console_port() 这是控制台已经请求时,如果正在进行端口验证请求,compute 应该在关闭之前完成它,以便用户可以获得他们请求的控制台。
基于时间的等待服务完成正在进行的操作
注意
基于时间的等待是一个临时解决方案。稍后,它将被替换为对正在进行中的任务的适当跟踪。
为了使优雅关闭不那么复杂,此规范提出了一种可配置的基于时间的等待,让服务完成它们的操作。
等待时间应小于全局优雅关闭超时。以便外部系统或 oslo.service 不会在服务等待时间结束之前关闭服务。
一些将通过此提案解决的关闭问题示例
迁移
迁移操作将使用 ‘ops RPC 服务器’。
如果迁移正在进行中,则服务关闭将不会终止迁移;而是能够等待迁移完成。
稍后,我们将使长时间运行的迁移中止,但这超出此规范的范围。
实例启动
实例启动操作将继续使用 ‘新请求 RPC 服务器’。否则,我们将无法停止新请求。
如果 compute 服务正在进行实例启动请求,则关闭将等待 compute 成功启动它们。
实例外部事件将在优雅关闭期间收到;因此,实例启动请求不会被外部事件阻止。
如果在启动关闭后到达新的实例启动请求,则它将保留在队列中,compute 在重新启动后将处理它。
任何到达 compute 的操作都将在服务关闭之前完成。
注意
根据到目前为止的测试(eventlet 模式),它不需要对 oslo.messaging 进行任何更改,但我们需要通过在原生线程模式下(使用 oslo.service 线程后端)运行 compute 来进行测试。
优雅关闭超时:¶
Nova 服务超时
我们需要在 Nova 中配置两个可配置的超时时间
整体关机超时时间
oslo.service 已经有了超时时间(graceful_shutdown_timeout),该超时时间可以为每个服务配置,并用于超时 SIGTERM 信号处理程序。
oslo.service 将基于 graceful_shutdown_timeout 终止 Nova 服务,即使 Nova 服务的优雅关机尚未完成。
其默认值为 60 秒,这对于 Nova 服务来说太短了。建议将所有 Nova 服务的默认值覆盖为 180 秒。
操作员可以为每个 Nova 服务覆盖此值。
Nova 服务完成进行中任务的超时时间
当启动关机时,每个服务需要完成其进行中的任务,这可能需要时间,并且我们必须在 oslo.service graceful_shutdown_timeout 到达之前超时。
我们需要这个超时时间,因为在完成进行中的任务后,Nova 服务需要在管理器上调用 cleanup_host(),这也需要一些时间才能完成。如果我们没有这个超时时间,并且服务完成进行中的任务花费的时间过长,那么 oslo.service graceful_shutdown_timeout 将不会让 cleanup_host() 被执行。
我们需要为每个 Nova 服务添加这个可配置的超时选项,并且它们的默认值应该低于 graceful_shutdown_timeout。
外部系统超时时间
根据 Nova 服务部署的方式,可能存在外部系统(例如,Nova 在 k8s pod 上运行)的优雅关机超时时间。这可能会影响 Nova 的优雅关机,因此我们需要清楚地记录如果存在外部系统超时时间,则 Nova 服务超时 graceful_shutdown_timeout 应该相应设置。外部系统超时时间应该高于 graceful_shutdown_timeout,否则外部系统将超时并中断 Nova 的优雅关机。
备选方案¶
RPC 重设计的另一种方案是为每个 RPC 服务器处理两个主题。这需要在 oslo.messaging 框架以及驱动程序实现中进行大量的更改。想法是允许 oslo.messaging Target 接受多个主题(将主题作为列表),并要求驱动程序为每个主题创建单独的消费者、侦听器、调度器和队列。为每个主题创建绑定到交换机的绑定。这还需要 oslo.messaging 提供一种新的方法,让 RPC 服务器取消订阅特定主题并继续侦听其他主题。我们还需要重新设计 RPC 服务器当前的 stop() 和 wait() 的工作方式。这太复杂了,几乎是重新设计了 oslo.messaging RPC 的概念。
另一种方案是跟踪并停止从 Nova api 或调度器服务发送请求,但这将无法停止所有新请求(计算到计算的任务)或让进行中的任务完成。
数据模型影响¶
无
REST API 影响¶
无
安全影响¶
无
通知影响¶
无
其他最终用户影响¶
这应该为最终用户提供积极的影响,以便关机不会停止他们正在进行的操作。
性能影响¶
对正常操作没有影响,但服务关机将花费更多时间。有一个可配置的超时时间来控制服务关机等待时间。
其他部署者影响¶
除了更长的关机过程之外,没有其他影响,但他们可以为服务关机配置适当的超时时间。
开发人员影响¶
无
升级影响¶
添加新的 RPC 服务器会影响升级。旧的计算节点将没有新的“ops RPC 服务器”侦听 RPC_TOPIC_OPS 主题,因此我们需要使用 RPC 版本控制来处理它。如果 RPC 客户端检测到旧的计算节点(基于 version_cap),它将回退到将消息发送到原始 RPC 服务器(侦听 compute.<host>);因此,在所有计算节点升级并删除 RPC version_cap 之前,新的计算节点上将无法正常工作优雅关机。
实现¶
负责人¶
主要负责人
gmaan
其他贡献者
无
功能联络人¶
无
工作项¶
在计算服务上实现“ops RPC 服务器”
在关机期间需要完成的操作(例如,计算到计算的任务和服务器外部事件)使用“ops RPC 服务器”。
由于升级影响,需要进行 RPC 版本控制。
依赖项¶
为所有 Nova 服务删除 Eventlet:我们需要确保在原生线程模式下优雅关机正常工作,因此我们需要等待所有计算服务移动到原生线程模式。这将测试 oslo.service 与线程后端。
oslo.service 线程后端需要考虑可配置的 graceful_shutdown_timeout。
测试¶
我们无法为这个编写 tempest 测试,因为 tempest 将无法停止服务。
我们可以尝试在“post-run”阶段进行一些测试(使用一些需要时间的大量实时迁移),就像 evacuate 测试一样。
将添加单元和功能测试。
文档影响¶
优雅关机工作将与超时或等待时间等其他注意事项一起记录。
参考资料¶
PoC
PTG 讨论
历史¶
发布名称 |
描述 |
|---|---|
2026.1 Gazpacho |
引入 |