升级¶
Nova 旨在以最少的停机时间提供升级。
首先,是数据平面。升级 Nova 时,虚拟机不应出现停机时间。Nova 从早期就开始支持这一点。
其次,我们希望在升级 Nova 控制平面时没有停机时间。本文档试图描述我们如何实现这一点。
一旦我们介绍了与升级相关的关键概念,我们将介绍实现 Nova 无停机升级所需的流程。
最小停机时间升级流程¶
规划您的升级¶
阅读并确保您理解下一个版本的发行说明。
您应该确保已完成上一次升级的所有必需步骤,例如数据迁移。
备份您的数据库。Nova 不支持降级数据库。因此,如果升级失败,从备份恢复数据库是唯一的选择。
在升级期间,请注意 nova-conductor 会有额外的负载。您可能会发现需要添加额外的 nova-conductor worker 来处理额外的升级相关负载。
滚动升级过程¶
为了减少停机时间,计算服务可以以滚动方式升级。这意味着一次升级少量服务。这将导致一种情况,即旧 (N) 和新 (N+1) nova-compute 服务在一段时间内同时存在(甚至 N 与升级后的 N+2 nova-compute 服务共存,见下文)。请注意,这里没有超visor的升级,只是升级 nova 服务。如果减少停机时间不是问题(或希望降低复杂性),可以同时关闭并重新启动所有服务。
重要提示
截至 OpenStack 2023.1 (Antelope),Nova 支持在同一部署中 N 和 N-2 (Yoga) nova-compute 或 nova-conductor 服务共存。当检测到比支持范围旧的 nova-compute 服务时,nova-conductor 服务将无法启动。这因版本而异,支持范围将在发行说明中解释。 同样,在 具有多个 cell 的部署 中,如果部署中的任何其他 conductor 服务比 N-2 版本旧,则超级 conductor 服务或任何 per-cell conductor 服务都不会启动。
早于 2023.1 的版本仅支持 nova-compute 和 nova-conductor 服务之间单个版本差异的滚动升级。
维护窗口之前
从控制器节点开始该过程。安装 Nova 的下一个版本的代码,无论是在 venv 中还是在单独的控制平面节点中,包括所有 python 依赖项。
使用新安装的 nova 代码,运行 DB 同步。首先运行
nova-manage api_db sync,然后nova-manage db sync。nova-manage db sync应该针对所有 cell 数据库运行,包括cell0。如果需要,可以使用--config-file参数指向给定的 cell 的正确nova.conf文件。这些模式更改操作应该对性能产生最小或没有影响,并且不应导致任何操作失败。
此时,数据库中可能存在新的列和表。这些 DB 模式更改以一种允许 N 和 N+1 版本都对同一模式执行操作的方式完成。
维护窗口期间
几个 nova 服务依赖于外部 placement 服务处于最新级别。因此,您必须先升级 placement,然后再升级任何 nova 服务。有关升级 placement 服务的更多详细信息,请参阅 placement 升级说明。
为了最大程度的安全(没有失败的 API 操作),请优雅地关闭所有服务(即 SIG_TERM),除了 nova-compute。
在用新代码重新启动服务之前,使用
nova-status upgrade check执行特定于版本的就绪检查。有关状态检查的更多详细信息,请参阅 nova-status upgrade check。使用 nova.conf 中的
[upgrade_levels]compute=auto,在新代码上启动所有服务。首先启动 nova-conductor,最后启动 nova-api,这是最安全的。请注意,您可以使用静态别名名称代替auto,例如[upgrade_levels]compute=<release_name>。 另外请注意,如果计算服务没有与控制服务同步升级,则只需要此步骤。如果需要,可以优雅地关闭 nova-compute 服务(即 SIG_TERM),分批进行,然后使用
[upgrade_levels]compute=auto启动新版本的代码。如果使用这种分批方法,只有少数计算节点会存在任何延迟的 API 操作,并且为了确保在此期间有足够的容量来服务任何启动请求,请确保有足够的容量在线。
维护窗口之后
一旦所有服务都在运行新代码,使用
nova service-list在 DB 中双重检查,确保没有旧的孤立服务记录。现在所有服务都已升级,我们需要发送 SIG_HUP 信号,以便所有服务清除任何缓存的服务版本数据。当新服务启动时,它会自动检测应该使用 compute RPC 协议的哪个版本,并且可以决定是否可以安全地执行任何在线数据迁移。请注意,如果您为 upgrade_level 使用了静态值,例如
[upgrade_levels]compute=<release_name>,则必须更新 nova.conf 以删除该配置值并完全重新启动服务。现在所有服务都已升级并发出信号,该系统能够使用最新版本的 RPC 协议并可以访问新版本中的所有功能。
一旦所有服务都在运行最新版本的代码,并且所有服务都知道它们都已升级,就可以安全地将数据库中的数据转换为其新格式。虽然其中一些工作是在系统读取需要更新的数据库行时按需完成的,但我们必须在下一次升级之前将所有数据转换为当前版本。 此外,某些数据可能不会自动转换,因此执行数据迁移对于避免由于兼容性例程而导致的性能下降是必要的。
此过程可能会对数据库施加显著的额外写入负载。使用以下命令完成所有在线数据迁移:
nova-manage db online_data_migrations --max-count <number>。请注意,您可以使用--max-count参数来减少此操作将对数据库施加的负载,从而允许您运行一小部分迁移,直到完成所有工作。您应该使用的块大小取决于您的基础设施以及您可以对数据库施加多少额外负载。为了减少负载,请在块之间使用较小的批次和延迟。为了减少完成时间,请运行较大的批次。每次运行该命令时,都会显示已完成和剩余记录的摘要。如果使用--max-count选项,即使某些迁移产生错误,也应在返回退出状态 1 时重新运行该命令(这表示进行了一些迁移,可能还有更多工作要做)。如果已完成所有可能的迁移并且仍然产生错误,将返回退出状态 2。在这种情况下,应调查并解决错误的原因。只有当该命令返回退出状态 0 时,才能认为迁移已成功完成。此时,您还必须确保更新配置,以停止使用任何已弃用的功能或选项,并执行任何必要的工作以过渡到替代功能。所有已弃用的选项至少支持一个周期,但在执行下一次升级之前应将其删除。
当前数据库升级类型¶
目前 Nova 有两种正在使用的数据库升级类型。
模式迁移
数据迁移
Nova 不支持数据库降级。
模式迁移¶
模式迁移定义在 nova/db/main/migrations/versions 和 nova/db/api/migrations/versions 中。它们是转换我们的数据库结构的例程,应该可以添加到正在运行的系统上,然后再升级服务代码。
有关开发您自己的模式迁移作为功能或错误修复的一部分,请参阅 数据库迁移。
注意
应假定 API 数据库迁移在 main/cell 数据库的迁移之前运行。这是因为前者包含有关如何查找和连接到后者的信息。某些在多个 cell 上运行的管理命令将尝试列出并迭代 cell 映射记录,这需要一个正常工作的 API 数据库模式。
数据迁移¶
在线数据迁移发生在两个地方
内联迁移,这些迁移作为正常运行时活动的一部分发生,数据以旧格式读取并以新格式写入
后台在线迁移,使用
nova-manage执行,以完成由于正常运行时活动而不会偶然发生的转换。
在线数据迁移的一个示例是 Nova 对象版本 1.18 中完成的 flavor 迁移。这包括 flavor 存储从一个数据库位置到另一个数据库位置的临时迁移。
有关开发您自己的模式迁移作为功能或错误修复的一部分,请参阅 数据库迁移。
迁移策略¶
为了方便升级,遵循以下模式和数据迁移指南:
累积模式迁移 - 通常,几乎所有的模式迁移都应该是累积的。简单地说,它们应该只创建元素,例如列、索引和表。
减去模式迁移 - 要在 N 版本周期内删除元素,例如列或表
该元素必须被弃用并保留以实现向后兼容性。(这允许从 N 到 N+1 的平稳升级。)
对象层中的数据迁移必须将数据从旧版本的模式完全迁移到新版本。
然后在 N+2 的开始时可以删除该列。
所有模式迁移都应该是幂等的。例如,迁移应该在尝试添加它之前检查模式中是否存在元素。此逻辑来自在线迁移的自动生成工作流。
约束 - 在添加外键或唯一键约束时,模式迁移代码需要处理数据可能存在的问题,然后再应用约束。(示例:唯一约束必须在应用所述约束之前清理重复记录。)
数据迁移 - 如上所述,数据迁移将以对象层中的自定义代码的形式进行,该代码处理在旧部分和新部分之间移动数据。此外,对于每种类型的数据迁移,都应存在 nova-manage 选项,允许操作员手动请求迁移行。
概念¶
在阅读升级过程部分之前,您需要了解的关键概念如下:
- RPC 版本固定
通过仔细的 RPC 版本控制,较新的节点能够与较旧的 nova-compute 节点通信。在升级控制平面节点时,我们可以将它们固定在 compute RPC API 的旧版本,直到所有计算节点都能够升级为止。 https://wiki.openstack.org/wiki/RpcMajorVersionUpdates
注意
使用多个 cell v2 cell 进行滚动升级的程序尚未确定。
- 在线配置重新加载
在升级期间,我们将新服务固定在旧的 RPC 版本。当所有服务都更新为使用较新代码时,我们需要取消固定它们,以便能够使用任何新功能。为了避免必须重新启动服务,使用当前的 SIGHUP 信号处理或其他方式,理想情况下,我们需要一种更新当前正在运行的进程以使用最新配置的方法。
- 优雅的服务关闭
许多 nova 服务都是侦听 AMQP 队列上消息的 python 进程,包括 nova-compute。当向该进程发送 SIGTERM 时,该进程停止从其队列接收新工作,完成任何未完成的工作,然后终止。在此过程中,消息可能会留在队列中,以便 python 进程启动时使用。这为我们提供了一种使用较旧代码关闭服务,并使用较新代码启动服务,而影响最小的方法。如果这是一个可以有多个 worker 的服务,例如 nova-conductor,通常可以在旧 worker 优雅关闭之前添加新的 worker。对于 nova-compute 等单例服务,某些操作可能会在重新启动期间延迟,但理想情况下不应因重新启动而导致任何操作失败。
注意
虽然这对于 RabbitMQ RPC 后端是正确的,但我们需要确认其他 RPC 后端会发生什么情况。
- API 负载均衡器排空
在升级 API 节点时,您可以使您的负载均衡器仅将新连接发送到较新的 API 节点,从而实现 API 节点的无缝更新。
- 扩展/收缩 DB 迁移
现代数据库能够在您仍然写入数据库时进行许多模式更改。更进一步,我们可以通过首先添加新结构来完成所有 DB 更改,扩展。然后,您可以缓慢地将所有数据移动到新的位置和格式。完成此操作后,您可以删除不再需要的模式位,即收缩。这发生在停止使用特定模式后多个周期,并且可以在不影响运行时代码的情况下进行模式迁移。
- 使用对象进行在线数据迁移
自 Kilo 以来,我们已将所有数据迁移都移至 DB 对象代码中。当尝试在数据库中将数据从旧格式迁移到新格式时,这在对象代码中读取或保存旧格式的事物时完成。对于未更新的记录,您需要运行一个后台进程将这些记录转换为较新格式。必须在收缩数据库模式之前完成此过程。
- DB 剪除已删除的行
当前,资源在主数据库中被软删除,因此用户能够跟踪在生产环境中创建和销毁的实例。但是,大多数人都有数据保留策略,例如 30 天或 90 天,在此之后他们希望删除这些条目。不删除这些条目会影响 DB 性能,因为索引会变得非常大,并且数据迁移需要更长时间,因为有更多数据需要迁移。
- nova-conductor 对象回溯
RPC 固定确保新服务可以与旧服务的方法签名通信。但是,许多参数都是对象,这些对象可能对旧服务来说太新了,因此您可以将对象回溯到 nova-conductor,以便将其降级到旧服务可以理解的版本。
测试¶
我们使用“手榴弹”作业来测试升级。当前的测试仅涵盖现有的升级过程,其中旧计算可以与新控制平面一起运行,但控制平面在 DB 迁移期间已关闭。