Cells (v2)

在版本 16.0.0 中添加: (Pike)

本文档描述了使用 cells v2 的部署布局,包括安全性和可扩展性的部署注意事项,以及管理员和操作员运行和维护 cells v2 的推荐实践和技巧。它侧重于 Pike 及更高版本中存在的代码,虽然它面向想要拥有多个 cells 的人,但 Nova 中 cells v2 支持的性质意味着它在某种程度上适用于所有部署。

在进一步阅读之前,Andrew Laski 在奥斯汀(Newton)峰会上发表了一个很好的概述 演示,可能值得观看。

注意

Cells v2 与早期 Nova 版本中发现的 cells 功能不同,也称为 cells v1。Cells v1 在 16.0.0 (Pike) 中已被弃用,并在 Train (20.0.0) 中完全删除。

概述

Nova 中 cells 功能的目的是允许更大的部署将其许多计算节点分片到 cells 中。所有 Nova 部署本质上都是 cells 部署,即使大多数部署可能只有一个 cell。这意味着多 cells 部署与“标准”Nova 部署不会有根本性的不同。

考虑这样一个部署。它将由以下组件组成

  • 提供外部 REST API 给用户的 nova-api-wsgi 服务。

  • 负责跟踪资源并决定实例应该在哪个计算节点上的 nova-schedulerplacement 服务。

  • 一个“API 数据库”,主要由 nova-api-wsginova-scheduler(以下称为 *API 级别服务*)用于跟踪实例的位置信息,以及正在构建但尚未调度的实例的临时位置。

  • 将 API 级别服务的长期任务卸载并隔离计算节点免受直接数据库访问的 nova-conductor 服务

  • 管理 virt 驱动程序和 hypervisor 主机的 nova-compute 服务。

  • 由 API、conductor 和 compute 服务使用的“cell 数据库”,其中包含关于实例的大部分信息。

  • 一个“cell0 数据库”,就像 cell 数据库一样,但只包含未能调度的实例。这个数据库模仿一个常规 cell,但没有计算节点,仅用作放置无法降落在真实计算节点(以及真实 cell)上的实例的位置。

  • 允许服务通过 RPC 相互通信的消息队列。

在较小的部署中,通常会有一个所有服务共享的单个消息队列和一个数据库服务器,该服务器托管 API 数据库、单个 cell 数据库以及所需的 cell0 数据库。由于我们只有一个“真实”cell,因此我们将其视为“单 cell 部署”。

在较大的部署中,我们可以选择使用多个 cells 对部署进行分片。在这种配置中,仍然只有一个全局 API 数据库,但每个 cell 都有一个 cell 数据库(实例的大部分信息都位于其中),每个数据库都包含整个部署中一部分实例,以及每个 cell 的消息队列和每个 cell 的 nova-conductor 实例。 此外,还有一个额外的 nova-conductor 实例,称为 *super conductor*,用于处理 API 级别操作。

在这些较大的部署中,Nova 的每个服务都将使用特定于 cell 的配置文件,所有配置文件至少都将指定一个消息队列端点(即 transport_url)。大多数服务还将包含数据库连接配置信息(即 database.connection),而需要访问全局路由和 placement 信息的 API 级别服务也将配置为访问 API 数据库(即 api_database.connection)。

注意

为服务配置的 transport_urldatabase.connection 定义了服务所在的 cell。

API 级别服务需要能够联系所有 cell 中的其他服务。由于它们只有一个配置的 transport_urldatabase.connection,它们会在 API 数据库中查找其他 cell 的信息,这些记录称为 *cell 映射*。

注意

API 数据库必须具有与下层服务配置的 transport_urldatabase.connection 配置选项匹配的 cell 映射记录。有关如何创建和检查这些记录的更多信息,请参阅 nova-manage Cells v2 命令

下一节将更详细地介绍单 cell 和多 cell 部署之间的区别。

服务布局

服务通常具有明确定义的通信模式,这决定了它们在部署中的布局。在小型/简单的场景中,这些规则的影响不大,因为所有服务都可以通过单个消息总线在单个 cell 数据库中相互通信。但是,随着部署的增长,可扩展性和安全性问题可能会导致服务的分离和隔离。

单 cell

这是简单(单 cell)部署的基本服务的图表,以及它们之间的关系(即通信路径)

digraph services {
  graph [pad="0.35", ranksep="0.65", nodesep="0.55", concentrate=true];
  node [fontsize=10 fontname="Monospace"];
  edge [arrowhead="normal", arrowsize="0.8"];
  labelloc=bottom;
  labeljust=left;

  { rank=same
    api [label="nova-api"]
    apidb [label="API Database" shape="box"]
    scheduler [label="nova-scheduler"]
  }
  { rank=same
    mq [label="MQ" shape="diamond"]
    conductor [label="nova-conductor"]
  }
  { rank=same
    cell0db [label="Cell0 Database" shape="box"]
    celldb [label="Cell Database" shape="box"]
    compute [label="nova-compute"]
  }

  api -> mq -> compute
  conductor -> mq -> scheduler

  api -> apidb
  api -> cell0db
  api -> celldb

  conductor -> apidb
  conductor -> cell0db
  conductor -> celldb
}

所有服务都配置为通过相同的消息总线相互通信,并且只有一个 cell 数据库,其中包含实时实例数据。cell0 数据库存在(并且是必需的),但由于没有计算节点连接到它,这仍然是一个“单 cell”部署。

多 cell

为了将服务分片到多个 cell 中,必须发生一些事情。首先,消息总线必须沿与 cell 数据库相同的线路分割。其次,必须为 API 级别服务运行一个专用的 conductor,并访问 API 数据库和专用的消息队列。我们称之为 *super conductor* 以区分其位置和目的与每个 cell 的 conductor 节点。

digraph services2 {
  graph [pad="0.35", ranksep="0.65", nodesep="0.55", concentrate=true];
  node [fontsize=10 fontname="Monospace"];
  edge [arrowhead="normal", arrowsize="0.8"];
  labelloc=bottom;
  labeljust=left;

  subgraph api {
    api [label="nova-api"]
    scheduler [label="nova-scheduler"]
    conductor [label="super conductor"]
    { rank=same
      apimq [label="API MQ" shape="diamond"]
      apidb [label="API Database" shape="box"]
    }

    api -> apimq -> conductor
    api -> apidb
    conductor -> apimq -> scheduler
    conductor -> apidb
  }

  subgraph clustercell0 {
    label="Cell 0"
    color=green
    cell0db [label="Cell Database" shape="box"]
  }

  subgraph clustercell1 {
    label="Cell 1"
    color=blue
    mq1 [label="Cell MQ" shape="diamond"]
    cell1db [label="Cell Database" shape="box"]
    conductor1 [label="nova-conductor"]
    compute1 [label="nova-compute"]

    conductor1 -> mq1 -> compute1
    conductor1 -> cell1db

  }

  subgraph clustercell2 {
    label="Cell 2"
    color=red
    mq2 [label="Cell MQ" shape="diamond"]
    cell2db [label="Cell Database" shape="box"]
    conductor2 [label="nova-conductor"]
    compute2 [label="nova-compute"]

    conductor2 -> mq2 -> compute2
    conductor2 -> cell2db
  }

  api -> mq1 -> conductor1
  api -> mq2 -> conductor2
  api -> cell0db
  api -> cell1db
  api -> cell2db

  conductor -> cell0db
  conductor -> cell1db
  conductor -> mq1
  conductor -> cell2db
  conductor -> mq2
}

重要的是要注意,较低 cell 框中的服务只有能力回调到 placement API,但无法通过 RPC 访问任何其他 API 层服务,也没有访问 API 数据库以获取云上全局资源可见性的权限。这是有意为之,并提供了安全性和故障域隔离的好处,但也对某些原本需要这种任意到任意通信风格的事情产生影响。请查看 需要 upcall 的操作,以获取有关由于此限制可能存在的任何注意事项的最新信息。

数据库布局

如前所述,全局数据与特定于 cell 的数据之间存在分割。这些数据库模式被称为 *API* 和 *main* 数据库模式,分别。

API 数据库

API 数据库是 API 级别服务使用的数据库,例如 nova-api-wsgi,以及在多 cell 部署中,superconductor。与此数据库相关的模型和迁移可以在 nova.db.api 中找到,可以使用 nova-manage api_db 命令来管理该数据库。

Main(cell 级别)数据库

main 数据库是 cell 级别 nova-conductor 实例使用的数据库。与此数据库相关的模型和迁移可以在 nova.db.main 中找到,可以使用 nova-manage db 命令来管理该数据库。

用法

如前所述,所有部署实际上现在都是 cells v2 部署。因此,任何 Nova 部署的设置——即使那些打算只有一个 cell 的部署——都将涉及一定程度的 cells 配置。这些更改与配置相关,既在主 nova 配置文件中,也在数据库中的一些额外记录中。

所有 Nova 部署现在必须配置以下数据库:

  1. “API”数据库

  2. 一个特殊的“cell”数据库,称为“cell0”

  3. 一个(或最终更多)“cell”数据库

因此,一个小的 Nova 部署将拥有一个 API 数据库、一个 cell0,以及我们在这里称之为“cell1”的数据库。高级跟踪信息保存在 API 数据库中。从未调度的实例被降级到 cell0 数据库,这实际上是一个失败实例的墓地。所有成功/正在运行的实例都存储在“cell1”中。

注意

由于 Nova 服务使用配置文件和一些数据库记录,因此使用不完整的配置启动或重新启动这些服务可能会导致不正确的部署。仅在完成以下步骤后才重新启动服务。

注意

以下示例显示了设置命令的完整扩展命令行用法。这样可以更轻松地可视化每个命令使用的各种 URL。但是,您应该能够将所有这些内容放入配置文件中,并且 nova-manage 将使用这些值。如有必要,您可以创建单独的配置文件并将其作为 nova-manage --config-file foo.conf 传递,以在不指定命令行中的内容的情况下控制行为。

配置新部署

如果您是第一次安装 Nova 并且数据库中还没有计算主机,那么有必要配置 cell0 和至少一个额外的“真实”cell。首先,确保您的 API 数据库模式已使用 nova-manage api_db sync 命令填充。确保此数据库的连接信息存储在 nova.conf 文件中使用 api_database.connection 配置选项

[api_database]
connection = mysql+pymysql://root:secretmysql@dbserver/nova_api?charset=utf8

由于可能有多个“cell”数据库(事实上每个人都会有 cell0 和 cell1 至少),这些数据库的连接信息存储在 API 数据库中。因此,API 数据库必须存在,并且必须提供有关如何连接到它的信息,然后才能继续执行以下步骤,以便 nova-manage 能够找到您的其他数据库。

接下来,我们将使用 nova-manage cell_v2 map_cell0 创建 cell0 数据库所需的记录。例如

$ nova-manage cell_v2 map_cell0 \
    --database_connection mysql+pymysql://root:secretmysql@dbserver/nova_cell0?charset=utf8

注意

如果您没有指定 --database_connection,则命令将使用配置文件中的 database.connection 值并修改数据库名称以具有 _cell0 后缀

警告

如果您的数据库位于不同的主机上,则应指定 --database_connection 或确保正在使用的 nova.conf 具有指向与 cell0 数据库相同用户/密码/主机的 database.connection 值。如果 cell0 映射创建不正确,可以使用 nova-manage cell_v2 delete_cell 命令将其删除,然后再使用正确的数据库连接值重新运行 nova-manage cell_v2 map_cell0

然后,我们将使用 nova-manage db sync 将数据库模式应用于此新数据库。例如

$ nova-manage db sync \
    --database_connection mysql+pymysql://root:secretmysql@dbserver/nova_cell0?charset=utf8

由于没有主机位于 cell0 中,因此不需要对其进行进一步设置。请注意,所有部署都只有一个 cell0,因为它很特殊,因此完成此步骤后,您无需再次执行此步骤,即使您添加了更多常规 cell。

现在,我们必须创建另一个 cell,这将是我们的第一个“常规”cell,其中包含实际的计算主机,并且实例可以实际调度到其中。首先,我们使用 nova-manage cell_v2 create_cell 创建 cell 记录。例如

$ nova-manage cell_v2 create_cell \
    --name cell1 \
    --database_connection  mysql+pymysql://root:secretmysql@127.0.0.1/nova?charset=utf8 \
    --transport-url rabbit://stackrabbit:secretrabbit@mqserver:5672/

注意

如果您没有指定数据库和传输 URL,则 nova-manage 将使用配置文件中的 transport_urldatabase.connection 值。

注意

为创建的新 cell 指定名称是一个好主意,以便以后可以使用 nova-manage cell_v2 list_cells 命令轻松查找 cell UUID。

注意

如果传递了 --verbose,则 nova-manage cell_v2 create_cell 命令将打印新创建的 cell 的 UUID,如果您需要针对特定 cell 运行诸如 nova-manage cell_v2 discover_hosts 之类的命令,这将很有用。

此时,API 数据库现在可以找到 cell 数据库,并且进一步的命令将尝试在其内部查找。如果这是一个全新的数据库(例如,如果您正在添加 cell,或者如果这是一个新部署),那么您需要使用 nova-manage db sync 在其上初始化模式。

现在我们有一个 cell,但其中没有主机,这意味着调度程序将永远无法实际将实例放置在那里。下一步是扫描数据库以查找计算节点记录并将它们添加到我们刚刚创建的 cell 中。为此,您必须启动一个计算节点,使其将其自身注册为正在运行的服务。您可以使用 openstack compute service list 命令识别它

$ openstack compute service list --service nova-compute

一旦完成,您可以使用 nova-manage cell_v2 discover_hosts 命令扫描并将其添加到单元中

$ nova-manage cell_v2 discover_hosts

此命令将连接到您已创建单元的所有数据库(如上所述),查找已在该处注册的主机,并将这些主机映射到 API 数据库中,以便调度程序将其视为实例的可用目标。每当您向单元添加更多计算主机时,都需要重新运行此命令以从顶级映射它们,以便可以利用它们。您还可以配置一个定期任务,通过将 scheduler.discover_hosts_in_cells_interval 设置为秒数的时间间隔,让 Nova 自动发现新主机。此定期任务由 nova-scheduler 服务运行,因此您必须确保在所有 nova-scheduler 主机上对其进行配置。

注意

将来,每当您添加新的计算主机时,如果未配置使用 scheduler.discover_hosts_in_cells_interval 自动主机发现,则需要在启动它们后运行 nova-manage cell_v2 discover_hosts 命令以将其映射到单元。

将新单元添加到现有部署

您可以使用与创建第一个单元相同的步骤将其他单元添加到您的部署中。我们可以使用 nova-manage cell_v2 create_cell 创建一个新的单元记录。例如

$ nova-manage cell_v2 create_cell \
    --name cell2 \
    --database_connection  mysql+pymysql://root:secretmysql@127.0.0.1/nova?charset=utf8 \
    --transport-url rabbit://stackrabbit:secretrabbit@mqserver:5672/

注意

如果您没有指定数据库和传输 URL,则 nova-manage 将使用配置文件中的 transport_urldatabase.connection 值。

注意

为创建的新 cell 指定名称是一个好主意,以便以后可以使用 nova-manage cell_v2 list_cells 命令轻松查找 cell UUID。

注意

如果传递了 --verbose,则 nova-manage cell_v2 create_cell 命令将打印新创建的 cell 的 UUID,如果您需要针对特定 cell 运行诸如 nova-manage cell_v2 discover_hosts 之类的命令,这将很有用。

您可以为要添加到部署中的每个单元重复此步骤。您的现有单元数据库将被重用 - 这只是告知顶级 API 数据库有关您的现有单元数据库的信息。

创建新的单元后,使用 nova-manage cell_v2 discover_hosts 将计算主机映射到单元。只有在您没有启用使用 scheduler.discover_hosts_in_cells_interval 选项的自动发现时,才需要这样做。例如

$ nova-manage cell_v2 discover_hosts

注意

此命令将在每个单元数据库中搜索计算主机并将它们映射到相应的单元。对于较大的部署,这可能会很慢。您可能希望指定 --cell_uuid 选项,这将限制搜索到特定的单元。如果您要指定 --cell_uuid,可以使用 nova-manage cell_v2 list_cells 命令查找单元 UUID。

最后,运行 nova-manage cell_v2 map_instances 命令将现有实例映射到新的单元。例如

$ nova-manage cell_v2 map_instances

注意

此命令将在每个单元数据库中搜索实例并将它们映射到正确的单元。对于较大的部署,这可能会很慢。您可能希望指定 --cell_uuid 选项,这将限制搜索到特定的单元。如果您要指定 --cell_uuid,可以使用 nova-manage cell_v2 list_cells 命令查找单元 UUID。

注意

如果希望限制单个运行中映射的实例数量,可以指定 --max-count 选项。如果未指定 --max-count,将映射所有实例。该命令的重复运行将从上次运行结束的地方开始,因此无需增加 --max-count 以完成。退出代码 0 表示已映射所有实例。退出代码 1 表示仍有剩余实例需要映射。

单元映射中的模板 URL

从 18.0.0 (Rocky) 版本开始,单元映射中 --database_connection--transport-url 的 URL 中提供的 URL 可以包含变量,这些变量将在每次从数据库加载时进行评估,并且其值将从主机配置文件中的相应基本选项中获取。将解析基本 URL,并将以下元素可以替换到单元映射 URL 中(使用 rabbit://bob:s3kret@myhost:123/nova?sync=true#extra

单元映射 URL 变量

变量

含义

示例 URL 的一部分

scheme

:// 之前的这部分

rabbit

username

凭据中的用户名部分

bob

password

凭据中的密码部分

s3kret

hostname

主机名或地址

myhost

port

端口号(必须指定)

123

路径

URL 的“路径”部分(不带前导斜杠)

nova

查询

完整的查询字符串参数(不带前导问号)

sync=true

fragment

第一个井号后的所有内容

extra

变量以花括号提供,例如 {username}。简单的模板 rabbit://{username}:{password}@otherhost/{path} 在使用上面的示例时会生成完整的 URL rabbit://bob:s3kret@otherhost/nova

注意

在 SIGHUP 期间不会从配置文件重新加载 database.connectiontransport_url 值,这意味着如果更改了变量,则需要完全重启服务才能注意到单元映射记录中的更改。

注意

transport_url 选项可以包含 URL 的“netloc”部分(即 userA:passwordA@hostA:portA,userB:passwordB@hostB:portB)的扩展语法。在这种情况下,将尊重形式为 username1username2 等的替换,并可以在模板 URL 中使用。

对这些 URL 进行模板化可能有助于为每个服务主机提供其用于数据库等服务的自己的凭据。如果没有模板化,所有主机将使用相同的 URL(以及因此相同的凭据)来访问数据库和消息队列等服务。通过使用模板 URL,该模板导致凭据从主机本地配置文件获取,每个主机将使用这些连接的不同值。

假设您有两个服务主机,它们通常配置为将 cell0 数据库作为其主要连接,它们的(缩写)配置如下所示

[database]
connection = mysql+pymysql://service1:foo@myapidbhost/nova_cell0

[database]
connection = mysql+pymysql://service2:bar@myapidbhost/nova_cell0

如果没有单元映射模板 URL,它们仍然将使用相同的凭据(如映射中存储)来连接到单元数据库。但是,请考虑如下模板 URL

mysql+pymysql://{username}:{password}@mycell1dbhost/nova

mysql+pymysql://{username}:{password}@mycell2dbhost/nova

使用第一个服务和 cell1 映射,将实际用于连接到该数据库的计算 URL 为

mysql+pymysql://service1:foo@mycell1dbhost/nova

设计

在引入 cells v2 之前,当请求到达 Nova API 以获取特定实例时,实例信息是从数据库中获取的。该信息包含计算节点的主机名,实例当前位于该主机上。如果请求需要对实例采取行动(通常会这样做),则使用主机名来计算队列的名称,并写入一条消息,该消息最终会到达正确的计算节点。

cells v2 功能的核心是将此主机名查找分为两部分,从而产生三个信息而不是一个。基本上,与其仅仅查找实例所在的计算节点的名称,我们还开始获取数据库和队列连接信息。因此,当要求对实例 $foo 采取行动时,我们现在

  1. 查找该实例的(数据库、队列、主机名)三元组

  2. 连接到该数据库并获取实例记录

  3. 连接到队列并将消息发送到正确的hostname队列

上述内容与以前的组织方式有两点不同。首先,现在我们需要在知道实例所在位置之前进行两个数据库查找。其次,我们需要按需连接到适当的数据库和队列。这些更改都对性能有影响,但可以通过使用诸如实例映射信息的内存缓存以及数据库和队列系统的连接池等方法来减轻这些影响。单元的数量将始终小于实例的数量。

该功能还具有可用性影响,因为跨多个单元查询的实例列表可能会因单元中的数据库故障而导致部分结果。这些问题可以在 处理单元故障 中缓解。单元中的数据库故障将导致比部分列表结果更大的问题,因此预计会快速解决,并且 cells v2 将通过在响应中指示数据可能不完整来处理它。

与 cells v1 的比较

在引入 cells v2 之前,nova 具有一个非常相似的功能,也称为单元或称为 cells v1 以进行区分。cells v2 旨在解决许多被认为的 cells v1 功能的缺点。cells v2 功能相对于以前的 cells v1 功能的优势包括

  • nova 中数据库和队列的本机分片作为一等功能。所有代码路径都将通过查找过程,因此我们不会像当前单元那样遇到相同的特征奇偶校验问题。

  • 无需在顶部复制所有单元数据库的高级复制。API 需要一个自己的数据库来处理诸如实例索引之类的事务,但它不需要在顶级复制所有数据。

  • 它明确区分了全局和本地数据元素。flavor 和 keypair 等内容是需要仅在顶级存在的明确的全局概念。提供这种分离允许计算节点变得更加无状态,并与诸如删除/更改的全局数据隔离。

  • 现有的非单元用户将突然能够从其现有部署中生成一个新的“单元”,而无需更改其架构。只需将有关新数据库和队列系统的信息添加到新的索引中,即可让他们使用这些资源。

  • 现有的单元用户需要填写单元映射索引,关闭现有的单元同步服务,并最终清理其顶级数据库。但是,由于高级组织结构没有实质性的不同,因此他们无需重新架构其系统以迁移到 cells v2。

  • 将新的主机集作为新的“单元”添加允许将其插入到部署中并进行测试,然后再允许将构建计划安排到它们。

注意事项

注意

自在 16.0.0 (Pike) 版本中引入 cells v2 以来,已经解决了许多这些注意事项。这些在下面指出。

跨单元移动操作

在 21.0.0 (Ussuri) 版本中引入了跨单元冷迁移和调整大小的支持。这在 跨单元调整大小 中有记录。在此版本之前,无法从一个单元中的主机迁移或调整大小到另一个单元中的实例。

目前无法从一个单元中的主机迁移、撤离或卸架实例到另一个单元中的主机。

列出实例的性能

在 17.0.0 (Queens) 版本之前,跨多个单元边界对实例列表操作进行排序和分页可能无法正确进行。此外,跨多个单元的排序列表操作的性能比单个单元慢得多。这在 高效的多单元实例列表和排序 规范中得到了解决。

通知

在具有多个消息队列的多单元环境中,操作员可能希望配置到统一队列的单独连接以进行通知。这可以在所有节点上的配置文件中完成。有关更多详细信息,请参阅 oslo.messaging 配置文档

Nova Metadata API 服务

从 19.0.0 (Stein) 版本开始,可以使用配置选项 api.local_metadata_per_cell 以全局方式或每个单元的方式运行 nova metadata API 服务

全局

如果您有跨单元的网络,则可能需要全局运行 Nova metadata API。以全局方式运行时,应将其配置为具有对 api_database.connection 信息的 API 级别服务。在这种情况下,不应将 nova metadata API 服务作为独立服务(使用 nova-metadata-wsgi 服务)运行。

本地每个单元

在多单元部署中,为每个单元运行 Nova 元数据 API 可以提高性能和数据隔离性。如果您的网络沿着单元边界进行分段,那么您可以为每个单元运行 Nova 元数据 API 服务。如果您选择按单元运行,还应配置每个 neutron-metadata-agent 服务指向相应的 nova-metadata-wsgi。在这种情况下,nova 元数据 API 服务 必须 作为独立服务运行,使用 nova-metadata-wsgi 服务。

控制台代理

从 18.0.0 (Rocky) 版本开始,控制台代理必须按单元运行,因为控制台令牌授权存储在单元数据库中。这意味着每个控制台代理服务器必须访问 database.connection 信息,该信息用于包含其代理控制台访问权限的实例的单元数据库。此功能是 convert-consoles-to-objects 规范的一部分。

需要回调的操作

如果您部署多个单元并使用上述描述的超导体,计算节点和基于单元的导体将无法与调度器通信,因为它们未连接到相同的 MQ。这是为了隔离而设计的,但目前没有流程可以在没有这种连接的情况下实现某些功能。因此,任何需要所谓的“回调”的操作都将无法正常工作。这会影响以下内容

  1. 启动和调整大小期间的实例重新调度(第 1 部分)

    注意

    这已在 Queens 版本 中得到解决。

  2. 从计算节点到调度器的实例亲和性报告

  3. 服务器创建和撤离期间的延迟反亲和性检查

  4. 从单元查询主机聚合

    注意

    这已在 Rocky 版本 中得到解决。

  5. 附加卷和 [cinder] cross_az_attach = False

  6. 启动和调整大小期间的实例重新调度(第 2 部分)

    注意

    这已在 Ussuri 版本 中得到解决。

第一种情况很简单:如果您启动一个实例,它会被调度到计算节点,如果失败,通常会被重新调度到另一个节点。这需要调度器的干预,因此在 Pike 的多单元布局中将无法正常工作。如果您不依赖重新调度来弥补瞬态的计算节点故障,那么这不会影响您。为了确保您不要进行徒劳的重新调度尝试,您应该在 nova.conf 中将 scheduler.max_attempts 设置为 1

后两种情况相关。总结来说,Nova 具有一些用于确保实例之间的亲和性/反亲和性得以保留的机制,这些机制在 Pike 的多单元布局中无法正常工作。如果您不使用亲和性操作,那么这不会影响您。为了确保您不要进行徒劳的亲和性检查尝试,您应该在 nova.conf 中将 workarounds.disable_group_policy_check_upcall 设置为 True,并将 filter_scheduler.track_instance_changes 设置为 False

第四种情况以前仅在执行使用已删除的 XenAPI 驱动程序的实时迁移且未指定 --block-migrate 时才是一个问题。驱动程序会尝试根据源主机和目标主机是否在同一聚合中来确定是否应执行块迁移。由于聚合数据已迁移到 API 数据库,单元导体将无法访问聚合信息并会失败。

第五种情况是一个问题,因为当卷附加到 nova-compute 服务中的实例时,并且 nova.conf 中的 [cinder]/cross_az_attach=False 时,我们会尝试查找实例所在的可用区,其中包括获取 instance.host 所在的任何主机聚合。由于聚合位于 API 数据库中,并且单元导体无法访问该信息,因此这将失败。将来,此检查可以移动到 nova-api 服务,以便在到达单元之前检查实例和卷之间的可用区,但对于 从卷启动 而言,nova-compute 服务本身会创建卷,并且必须告诉 Cinder 在哪个可用区创建卷。从长远来看,从卷启动期间的卷创建应移动到顶级超导体,这将消除此 AZ 回调检查问题。

第六种情况在 bug 1781286 中有详细说明,并且与第一个问题类似。问题是,服务器在没有指定可用区的情况下创建,其 AZ 将在重新调度期间根据选择的备用主机进行计算。确定备用主机的 AZ 需要对 API DB 进行“回调”。

处理单元故障

有关 nova-api 如何处理单元故障的说明,请参阅 Compute API 指南的 处理停用单元 部分。以下是有效容忍单元故障情况的一些建议做法和注意事项。

配置注意事项

由于通过超时确定单元是否可访问,因此建议根据您的要求为以下设置提供合适的值。

  1. database.max_retries 默认为 10,这意味着每次单元不可访问时,nova 会在将单元声明为“停用”单元之前重试 10 次。

  2. database.retry_interval 为 10 秒,oslo_messaging_rabbit.rabbit_retry_interval 默认为 1 秒,这意味着每次单元不可访问时,它会每 10 秒或 1 秒重试,具体取决于它是数据库问题还是消息队列问题。

  3. Nova 还具有一个名为 CELL_TIMEOUT 的超时值,该值硬编码为 60 秒,nova-api 在返回“停用”单元的局部结果之前会等待的总时间。

上述设置的值会影响 nova 确定单元不可访问以及采取必要措施(例如返回局部结果)所需的时间。

操作员还可以根据 api.list_records_by_skipping_down_cells 配置选项来控制某些操作的结果,例如列出服务器和服务。如果此选项为 true,则将跳过不可访问单元的结果;如果为 false,则在无法计算局部结构的情况下,请求将只是以 API 错误失败。

停用单元

在修复基础设施中的临时故障期间,受影响的单元可以被停用,以便将其从调度候选单元中移除。要启用或停用单元,请使用 nova-manage cell_v2 update_cell --cell_uuid <cell_uuid> --disable。有关命令用法的详细信息,请参阅 Cells v2 命令 man 页。

已知问题

  1. 服务和性能:如果在 nova 服务启动期间单元停用,则服务可能会因为无法连接到某些计算和初始化可能需要的所有单元数据库而挂起。这种情况的一个示例是,如果 upgrade_levels.compute 设置为 auto,那么如果至少有一个不可访问的单元,nova-api 服务将在启动时挂起。这是因为它需要连接到所有单元以收集有关每个计算服务版本的的信息,以确定要使用的计算版本上限。当前的解决方法是将 upgrade_levels.compute 固定到特定版本,例如“rocky”,并在这种情况下启动服务。有关更多详细信息,请参阅 bug 1815697

  1. 配额计数:另一个已知问题是当前计算配额使用情况的方法,我们需要查询每个单元数据库以获取已使用的资源并对其进行汇总,这使得它对临时的单元故障敏感。在单元不可用时,我们无法计算存储在该单元数据库中的资源使用情况,并且行为就像有更多的配额可用一样。也就是说,如果租户已经使用了他们的所有配额,并且其中一部分位于单元 A 中,而单元 A 暂时离线,那么该租户突然能够分配比其限制更多的资源(如果单元 A 返回,租户将分配比其允许的配额更多的资源)。

    注意

    从 Train (20.0.0) 版本开始,可以配置从 placement 服务和 API 数据库计算配额使用情况,以使配额使用情况计算在多单元环境中对停用或性能不佳的单元具有弹性。有关更多详细信息,请参阅 配额文档

常见问题解答

  • 如何确定哪些主机绑定到哪个单元?

    有几种方法可以做到这一点。

    1. 运行 nova-manage cell_v2 discover_hosts --verbose

      这不会生成报告,但如果您正在确定主机是否在单元中,您可以运行此命令,它将报告尚未映射到单元的任何主机并对其进行映射。此命令是幂等的。

    2. 运行 nova-manage cell_v2 list_hosts

      这将列出所有单元中的主机。如果您只想列出特定单元中的主机,可以使用 --cell_uuid 选项。

  • 我更新了 database_connection 和/或 transport_url 在单元中使用 nova-manage cell_v2 update_cell 命令,但 API 仍然尝试使用旧设置。

    单元映射在 nova-api-wsgi 服务 worker 中缓存,因此您需要重新启动 worker 进程以重建缓存。请注意,还有另一个全局缓存与请求上下文相关联,它用于 nova-conductor 和 nova-scheduler 服务,因此如果您在这些服务中遇到相同的问题,可能需要执行相同的操作。从 16.0.0 (Pike) 版本开始,缓存上没有计时器或使用 SIGHUP 刷新缓存的钩子。

  • 我从 Newton 升级到 Ocata,我可以列出实例,但当我尝试获取特定实例的详细信息时,会收到 HTTP 404 (NotFound) 错误。

    需要将实例映射到单元,以便 API 知道实例位于哪个单元。在升级时,nova-manage cell_v2 simple_cell_setup 命令会自动将实例映射到由现有的 nova 数据库支持的单个单元。如果您已经升级并且没有使用 nova-manage cell_v2 simple_cell_setup 命令,则可以使用 --cell_uuid 选项运行 nova-manage cell_v2 map_instances 命令,以将给定单元中的所有实例映射到该单元。有关命令用法的详细信息,请参阅 Cells v2 命令 man 页。

  • 我可以创建一个单元,但禁用其调度功能吗?

    是的。可以通过使用 --disabled 选项运行 nova-manage cell_v2 create_cell 命令来创建预先禁用的单元,使其不会成为新 VM 调度的候选单元。

  • 如何禁用单元,以便新的服务器创建请求不会发送到它,同时我执行维护?

    可以使用 nova-manage cell_v2 update_cell 命令,并使用 --disable 选项来禁用现有单元,并在维护期结束后,可以使用此命令并使用 --enable 选项重新启用该单元。

  • 我使用 nova-manage cell_v2 update_cell 停用(或启用)单元,或者我使用 nova-manage cell_v2 create_cell 命令创建了一个新的(预先禁用的)单元(映射),但调度器仍然使用旧设置。

    单元映射缓存在调度器 worker 中,因此您需要重启调度器进程来刷新缓存,或者向调度器发送 SIGHUP 信号,它将自动刷新单元缓存,并且更改将生效。

  • 为什么 cells v2 没有实现单元 REST API?为什么 API 中没有单元的 CRUD 操作?

    cells v1 的一个部署挑战是 API 和控制服务需要在部署新单元之前启动。这对于从不关机的大型公共云来说不是问题,但对于进行离线升级和/或可能因断电等原因完全离线的较小云来说,这不是一个合理的条件。cells v1 的初始 devstack 和 gate 测试因需要设计一个解决方案来使服务部分上线以部署其余部分而延迟,并且这仍然是其他部署工具的一个差距。另外,考虑一下 FFU 的情况,控制平面需要在多版本升级窗口期间关闭,而此时需要修改单元记录。如果通过 API 进行这些更改,这将变得更加困难,因为 API 在此过程中必须保持关闭状态。

    此外,长期目标是将单元配置(即 cell_mappings 以及相关的 URL 和凭据)移入配置,并摆脱在数据库中存储和配置这些内容的需求。显然,API 中的 CRUD 接口将阻止我们进行此操作。

  • 为什么单元没有作为 API 中列出服务、实例和其他资源的分组机制公开?

    在 cells v2 的设计早期,我们设定了一个目标,即不要让单元概念从 API 中泄露,甚至对于操作员也是如此。聚合是 nova 支持分组主机的方式,原因有很多,聚合可以跨越单元,并且如果需要,也可以与单元对齐。如果我们支持单元作为另一个分组机制,我们可能最终需要为它们实现许多与聚合相同的功能,例如调度器功能、元数据和其他搜索/过滤操作。由于聚合是 Nova 支持分组的方式,我们期望操作员在需要从 API 将单元视为主机组时使用聚合,并将实际单元作为纯粹的架构细节。

    在 API 中按单元过滤实例的需求可以通过添加通用的按聚合过滤器来解决,这将允许列出包含在任何聚合中的实例,包括如果需要匹配单元边界的聚合。

  • 为什么 GET /serversGET /servers/detailGET /servers/{server_id}GET /os-services 的 API 响应在某些时候缺少某些单元的信息?为什么当我运行 openstack server listopenstack server show 时,我看到这些单元中服务器的状态显示为“UNKNOWN”?

    从 microversion 2.69 开始,GET /serversGET /servers/detailGET /servers/{server_id}GET /os-services 的 API 响应在单元关闭时可能包含缺失的键。有关部分构造的更多信息,请参阅计算 API 指南的 处理关闭的单元 部分。

    有关管理注意事项,请参阅 处理单元故障

参考

多年来,在各种 OpenStack 和 OpenInfra Summit 上已经进行了大量的 cells v2 相关演示。这些为该功能的历史和开发以及来自实际用户的详细信息提供了很好的参考。