RPC 和 VersionedObject 兼容性

https://blueprints.launchpad.net/cinder/+spec/rpc-object-compatibility

以下提案旨在启用 Cinder 的滚动升级。更具体地说,它允许 Cinder 服务(cinder-api、cinder-scheduler、cinder-backup 和 cinder-volume)逐个更新,同时仍然保持运行状态(一次只关闭一个服务进行升级,而不是全部)。它遵循 Nova 中的滚动升级功能,使用 oslo_versionedobjects,但不同之处在于没有间接 API。相反,RPC 版本和对象将被固定到向后兼容的版本,并在所有服务升级后切换到最新版本。

问题描述

如今,当发布新里程碑时,将 OpenStack 云中的所有服务升级到最新版本可能需要相当长的时间。升级涉及:将数据库同步到最新模式,以及更新每个分布式 Cinder 服务。此过程可能会使云的部分服务停机几个小时,甚至更长时间。运营商理想情况下希望在升级到下一个版本时减少云的停机时间。

用例

作为运营商,我希望单独升级每个服务到最新版本,但不要使我的整个云在此过程中停机。

提议的变更

升级像 Cinder 这样的分布式服务存在一些问题:1. 通过 RPC 发送的内容可能会发生变化。 2. RPC 接口本身可能会发生变化,例如,新的关键字参数。

为了解决这些问题,可以使用 oslo_versionedobject 使内容向后兼容,并将 RPC 接口固定到特定版本,直到 Cinder 中的所有组件都升级为止。

实现方式如下:1. 服务启动时,它会注册它了解的 RPC 和对象版本,即最小版本和最新版本。

2. 在升级时,管理员逐个升级每个 Cinder 服务。当服务启动时,它知道存在较新版本,但继续以数据库中指示的当前/固定版本运行。数据库是服务应运行哪个版本的真实来源。

Cinder 服务的升级顺序没有特定要求。数据库跟踪服务应使用的 RPC 和对象版本。该服务将继续使用特定版本,直到管理员发出“一切正常”的信号(如下所示)。

3. 在其他 Cinder 服务正在升级时,该服务必须不断检查数据库以获取当前和可用的 RPC 和对象版本。由于不断访问数据库会严重影响性能,因此可以通过缓存跟踪版本的表的副本来最小化它。该副本将在几秒钟内有效(例如,5 秒),然后刷新。这至少减少了每次 RPC 调用对数据库的访问次数。

服务内部使用一个标志来跟踪它是否应以向后兼容模式运行(例如,BACKWARD_COMPAT_MODE),即应使 RPC 和对象版本向后兼容。如果当前版本低于可用版本,则将 BACKWARD_COMPAT_MODE 设置为 true。否则,将其设置为 false。在每个服务的 rpcapi.py 中都有逻辑来设置给定 RPC 版本的正确关键字参数。在通过 RPC 发送的对象中也有逻辑来设置给定目标版本的正确属性。更具体地说,应通过调用它自己的 obj_make_compatible() 使每个对象向后兼容,从而将对象转换为给定的目标版本。

4. 一旦所有 Cinder 服务都升级,管理员将执行“cinder-manage version upgrade”以将当前/固定版本切换到最新的可用版本。此命令基本上会更新 Cinder 数据库中的 service_versions 表。由于当前版本等于可用版本,BACKWARD_COMPAT_MODE 设置为 false,并且每个 RPC 调用不再需要检查数据库。

通过使用 BACKWARD_COMPAT_MODE 标志,每个 Cinder 服务只需要重启一次(即,一次用于升级)。一旦关闭该标志,每个 Cinder 服务就可以动态切换到使用新的 RPC 和对象版本。

一旦 Cinder 内部的大部分组件都切换到使用 oslo_versionedobject,并且添加了 RPC 兼容性层(此功能),就应该支持滚动升级(希望在 Liberty 版本及以后)。滚动升级应限制为两个旧版本。例如,版本 N 将与版本 L(Liberty)和 M 向后兼容,版本 O 将与版本 M 和 N 向后兼容。对于过旧的主机/服务,它应该在启动时引发异常,并且不允许启动。

备选方案

升级过程可以保持不变,其中必须同时关闭并升级所有组件。一些云运营商,例如 Rackspace,已经将其升级过程自动化到几分钟。但是,其他云运营商需要此功能。

数据模型影响

将创建一个名为 service_versions 的新表来跟踪 RPC 和对象版本。模式如下

service_versions = Table(
    'service_versions', meta,
    Column('created_at', DateTime(timezone=False)),
    Column('updated_at', DateTime(timezone=False)),
    Column('deleted_at', DateTime(timezone=False)),
    Column('deleted', Boolean(create_constraint=True, name=None)),
    Column('id', Integer, primary_key=True, nullable=False),
    Column('service_id', String(length=255)),
    Column('rpc_current_version', String(length=36)),
    Column('rpc_available_version', String(length=36)),
    Column('object_current_version', String(length=36)),
    Column('object_available_version', String(length=36)),
    mysql_engine='InnoDB'
)

service_id 是服务名称 + 主机。 *_current_version 是服务当前运行和固定的版本。 *_available_version 是服务了解的并且(如果较新)可以升级到的版本。

REST API 影响

安全影响

通知影响

其他最终用户影响

将引入一个新的命令,即“cinder-manage version upgrade”,以将 Cinder 服务切换到使用最新的 RPC 和对象版本。

性能影响

在升级期间,在发送 RPC 调用之前,它必须检查是否需要向后兼容。如果是,则需要调整 RPC 接口和对象以使其向后兼容。由于需要额外的数据库调用来查找当前和可用的 RPC 和对象版本,因此会产生性能成本。由于在每次 RPC 调用之前访问数据库会严重影响性能,因此可以通过缓存跟踪版本的表的副本来最小化它。该副本将在几秒钟内有效(例如,5 秒),然后刷新。这至少减少了每次 RPC 调用对数据库的访问次数。升级完成后,所有服务都升级到最新版本,则不再需要检查数据库。

其他部署者影响

开发人员影响

从合并此功能开始,Cinder-api、cinder-scheduler、cinder-volume 或 cinder-backup 中的任何对 RPC 接口的新更改都必须添加到向后兼容层中。此外,对对象(例如,卷、快照等)的任何新更改都必须在对象的 obj_make_compatible() 中使其向后兼容。

实现

负责人

主要负责人

thang-pham

其他贡献者

DuncanT(想出此功能的人)

工作项

  • 创建 service_versions 表以跟踪 RPC 和对象版本。

  • 在启动时注册每个 Cinder 服务的 RPC 和对象版本。

  • 在每个 Cinder 组件的 rpcapi.py 中创建 RPC 兼容性层,以在通过 RPC 发送之前调整对象和 RPC 接口。

  • 创建一个“cinder-manage version upgrade”CLI 以将每个 Cinder 服务切换到使用最新版本。

依赖项

  • 需要合并 oslo_versionobjects 用于卷、备份、服务、一致性组、配额,以便可以使对象向后兼容。

测试

理想情况下,在 tempest(例如,grenade)中应该有一个滚动升级测试,以测试不同版本之间的基本 RPC 和对象固定。但是,这种测试仅适用于版本 M 及以后,因为大多数 oslo_versionedobject 和 RPC 兼容性层都不在以前的版本中。

文档影响

应该记录运营商可以单独升级 Cinder 组件,而无需关闭整个云。在流程结束时,必须执行“cinder-manage version upgrade”才能将服务切换到使用最新的 RPC 和对象版本。

参考资料