多区域 Trove

目前,Trove 能够将实例部署到单个区域内的多个可用区,但限制在单个 Openstack 区域内的部署。本规范概述了一个提案,允许 Trove 将实例部署到多个 Openstack 区域。

有三种不同的方法来实现 Trove 中的多区域支持。第一种方法是让每个区域拥有自己的 Trove 控制器,并实现跨区域的一致 Trove 实例视图;这将允许任何区域的用户查看其区域内的所有 trove 实例,无论哪个 Trove 控制器创建了它们。第二种方法是在所有区域拥有一个单一的 trove 控制器,由共享数据库(例如 Galera)协调;这将允许任何区域的用户查看所有区域中的所有 trove 实例。第三种方法是让每个区域的 Trove 控制器独立,但能够创建其他区域的实例;这将允许用户查看其区域中的 Trove 控制器创建的所有 Trove 实例,无论实例位于哪个区域,但他们将无法查看其他区域的 Trove 控制器在其区域创建的 Trove 实例。

本规范概述了一个提案,该提案将允许实现第二种和第三种替代方案。作者认为,第一种替代方案将比第二种和第三种方案复杂得多、难以实现且容易出错。

Launchpad 蓝图:https://blueprints.launchpad.net/trove/+spec/multi-region

问题描述

Trove 将被修改为能够使用 Openstack 的跨区域客户端访问来实施支持在其他区域创建 Trove 实例的功能。本质上,一个 Trove 控制器将能够访问多个 Openstack 区域中的 Nova 和 Cinder 服务。

多区域支持需要 Keystone 服务在区域之间联合,从而有效地允许 Trove 控制器使用单一的通用身份验证访问多个区域的服务。Openstack 目前支持此功能。

对象存储(例如 Swift)可以在区域之间联合,或者每个区域可以拥有独立的对象存储服务。在代表物理上不同的数据中心的区域中,共享对象存储的单一实现可能更可取,因为它将允许数据在区域之间高效共享,而不是在每次访问时完全传输。

在默认配置中,每个区域将托管一组独立的 Trove 控制器服务,每个区域都有自己的 Trove API、Taskmanager 和 Conductor 服务,并由每个区域的单独 Trove 数据库支持。当一个区域的 Taskmanager 需要在另一个区域分配资源(计算和块存储)时,它将使用 Nova 和 Cinder 客户端的 OS_REGION_NAME 参数来访问另一个区域中的适当的 Openstack 服务。在第二个区域分配的 Trove 实例仅对在第一个区域执行的 trove cli 命令可见。

另一种配置是可能的,即每个区域将托管一组共享通过联合数据库产品(例如 Galera 集群)实现通用 Trove 数据库的 Trove 服务。这将允许每个区域的用户查看所有 Trove 实例,无论它们托管在哪个区域,但可能会使 Trove 服务暴露于使用乐观锁的数据库上运行 Openstack 服务的常见问题。最初实施多区域支持时,不设想对这种替代方案进行测试。

如本文所述实施多区域支持需要每个区域的实例位于所有区域共享的网络上,并且实例能够访问每个区域的 Rabbit 网络。

提议的变更

支持多个区域,如本文所述,主要包括允许 Trove 服务(API、Taskmanager 和 Guestagent)在访问 Nova 和 Cinder 的客户端时提供 OS_REGION_NAME 参数。添加此支持将包含以下组件

  • 将“region”字段添加到 Trove 数据库中的 Instances 表。

  • 增强 CLI 和 REST API,以允许为每个要创建的实例(单个实例和集群)指定“区域名称”。

  • 根据需要将“区域名称”添加到 guest agent RPC 调用(prepare 调用中的 backup_info 参数)。

  • 增强每个数据存储以根据需要使用“区域名称”。

当指定在不同区域启动实例时,Trove 需要确保目标区域中存在适当的镜像。为此,trove 将联系另一个区域的 Glance 服务以检索与数据存储中指定的相同名称的镜像的元数据(在第一个区域)。将比较两个区域中镜像的校验和,以确保在每个区域安装了相同的镜像。

配置

预计不会对配置文件进行任何更改。

数据库

将在 Trove 的“instances”表中添加一个“region_name”字段。

将提供迁移脚本,以便在 Openstack 版本升级期间将“region_name”参数添加到上述列出的表中。

将向 DBInstance 类添加一个 region_name 属性,并在 SimpleInstance 类中进行阴影处理。

公共 API

将向以下 REST API 添加一个“region”参数,以指示应创建指定资源的区域。如果未指定“region”参数,则将在执行命令的区域中创建资源。

不需要为 list 和 show API 等不创建新资源的 API 添加额外的区域参数。对于这些 API,将通过 –os-region-name 参数指定区域。

当 Taskmanager 被要求在与其自身不同的区域创建实例时,它需要确保目标区域中存在合适的镜像来创建实例(因为无法告诉 RegionB 中的 Nova 使用来自 RegionA 的镜像)。要在 RegionB 中创建实例,RegionA 中的 Taskmanager 将按以下步骤进行:

  1. 从 RegionA 中的适当 datastore_version 中检索适当的镜像名称

  2. 从 RegionA 中的 Glance 检索校验和

  3. 确保 RegionB 中的 trove 具有相同名称的 datastore_version,并且该 datastore_version 指定相同名称的镜像

  4. 从 RegionB 中的 Glance 检索镜像的校验和

  5. 确保两个区域中的镜像具有相同的校验和

  6. 遵循类似于上述的过程,以确保两个区域中存在具有相似属性的类似名称的风味。

  7. 要求 RegionB 中的 Nova 使用适当的镜像名称和风味创建实例

实例创建

请求

POST v1/<tenant_id>/instances
{
    "instance": {
        "volume": {
            "type": null,
            "size": 1
        },
        "flavorRef": 11,
        "name": "m",
        "replica_count": 1,
        "replica_of": "0d5e5bcc-5c60-4703-b4b3-17f32e0abe72",
        "region": "RegionA"
    }
}

响应

{
    "instance": {
        "created": "2016-03-08T16:13:30",
        "datastore": {
            "type": "mysql",
            "version": "5.6"
        },
        "flavor": {
            "id": "11",
            "links": [
                {
                    "href": "https://<ip>:8779/v1.0/adbe7218e9f54369a0898f36d9c7a66d/flavors/11",
                    "rel": "self"
                },
                {
                    "href": "https://<ip>:8779/flavors/11",
                    "rel": "bookmark"
                }
            ]
        },
        "id": "0d5e5bcc-5c60-4703-b4b3-17f32e0abe64",
        "links": [
            {
                "href": "https://<ip>:8779/v1.0/adbe7218e9f54369a0898f36d9c7a66d/instances/0d5e5bcc-5c60-4703-b4b3-17f32e0abe64",
                "rel": "self"
            },
            {
                "href": "https://<ip>:8779/instances/0d5e5bcc-5c60-4703-b4b3-17f32e0abe64",
                "rel": "bookmark"
            }
        ],
        "name": "m",
        "status": "BUILD",
        "updated": "2016-03-08T16:13:30",
        "volume": {
            "size": 1
        },
        "region": "RegionA"
    }
}

集群创建

请求

POST /v1.0/<tenant_id>/clusters
{
  "cluster": {
    "name": "products",
    "datastore": {
      "type": "percona",
      "version": "5.5"
    },
    "instances": [
      {
        "flavorRef": "2",
        "volume": {
          "size": 100
        },
        "region": "RegionA",
      },
      {
        "flavorRef": "2",
        "volume": {
          "size": 100
        },
        "region": "RegionA",
      },
      {
        "flavorRef": "2",
        "volume": {
          "size": 100
        },
        "region": "RegionB",
      }
    ],
  }
}

响应

{
  "cluster": {
    "id": "dfbbd9ca-b5e1-4028-adb7-f78643e17998",
    "task": {
      "id": 2,
      "name": "BUILDING",
      "description": "Building the initial cluster."
    },
    "name": "products",
    "created": "2014-04-25T20:19:23",
    "updated": "2014-04-25T20:19:23",
    "links": [{...}],
    "datastore": {
      "type": "percona",
      "version": "5.5"
    },
    "region": "RegionA",
    "instances": [
      {
        "id": "416b0b16-ba55-4302-bbd3-ff566032e1c1",
        "status": "BUILD",
        "flavor": {
          "id": "2",
          "links": [{...}]
        },
        "volume": {
          "size": 100
        },
        "region": "RegionA",
      },
      {
        "id": "965ef811-7c1d-47fc-89f2-a89dfdd23ef2",
        "status": "BUILD",
        "flavor": {
          "id": "2",
          "links": [{...}]
        },
        "volume": {
          "size": 100
        },
        "region": "RegionA",
      },
      {
        "id": "3642f41c-e8ad-4164-a089-3891bf7f2d2b",
        "status": "BUILD",
        "flavor": {
          "id": "2",
          "links": [{...}]
        },
        "volume": {
          "size": 100
        },
        "region": "RegionB",
      }
    ],
  }
}

cluster-grow

请求

POST /v1.0/<tenant_id>/clusters/<cluster-id>/action
{
    "grow": [
      {
        "name": "redis-clstr-member-5",
        "instance_type": "master",
        "flavorRef": "2",
        "volume": {
          "size": 2
        },
      },
      {
        "name": "redis-clstr-member-6",
        "instance_type": "slave",
        "related_to": "redis-clstr-member-5",
        "flavorRef": "2",
        "volume": {
          "size": 2
        },
        "region": "RegionB",
      }
    ]
}

响应

{
  "cluster": {
    "id": "edaac9ca-b5e1-4028-adb7-fa7653e11224",
    "task": {
      "id": 2,
      "name": "BUILDING",
      "description": "Building the initial cluster."
    },
    "name": "redis-clstr",
    "created": "2015-01-29T20:19:23",
    "updated": "2015-01-29T20:19:23",
    "links": [{...}],
    "datastore": {
      "type": "redis",
      "version": "3.0"
    },
    "ip": [],
    "region": "RegionA",
    "instances": [
      {
        "id": "416b0b16-ba55-4302-bbd3-ff566032e1c1",
        "name": "redis-clstr-member-5",
        "instance_type": "master",
        "status": "BUILD",
        "ip": [],
        "links": [{...}],
        "flavor": {
          "id": "2",
          "links": [{...}]
        },
        "volume": {
          "size": 2
        }
        "region": "RegionA",
      },
      {
        "id": "965ef811-7c1d-47fc-89f2-a89dfdd23ef2",
        "name": "redis-clstr-member-6",
        "instance_type": "slave",
        "related_to": "redis-clstr-member-5",
        "status": "BUILD",
        "ip": [],
        "links": [{...}],
        "flavor": {
          "id": "2",
          "links": [{...}]
        },
        "volume": {
          "size": 2
        }
        "region": "RegionB",
      },
    ]
  }
}

公共 API 安全

此更改不应产生安全影响。

Python API

将向 Instances.create()、Clusters.create() 和 Clusters.grow() 调用中添加“region”参数。

CLI (python-troveclient)

将向“trove create”CLI 命令添加一个“--region”选项,该选项对应于 Instances.create() Python API 中的“region”参数。

将向“trove cluster-create”和“trove cluster-grow”CLI 命令的“--instance”选项添加一个“region”选项,该选项对应于 Clusters.create() 和 Clusters.grow() Python API。

内部 API

将向适当的 Taskmanager 调用添加“region”参数,以支持单个实例和集群创建的实例创建。

Trove Instance 类已经具有一个 nova_client 属性,该属性为每个客户机实例创建一个唯一的客户端连接。该调用将增强为指定实例存在的区域的名称;remote.py 中的 create_nova_client() 方法将增强为可选地接受区域名称参数。

Guest Agent

guest agent 的唯一更改应该是支持使用存储在不同区域的数据初始化数据库。这将在 prepare 过程中发生,是为了支持从不同区域的主备份创建副本。

guest.prepare() 调用已经接受一个名为 backup_info 的结构,其中包含有关要用于初始化数据库的备份的详细信息。此更改将向 backup_info 结构添加一个成员“region”,该成员将是包含备份的区域的名称。该区域名称将传递给 Swift 客户端,以告诉 Swift 备份创建在哪个区域;但是,预计 Swift 通常配置为在区域之间共享,因此能够优化所有区域的对象访问。

当 RegionA 中的 taskmanager 在 RegionB 中创建实例时,它会将 guestagent.conf 传递给新实例。RegionB 中的新实例将使用 conf 文件中的 rabbit 配置参数来确定如何连接到 RegionA 中的 rabbit broker。现有 guest agent 无需进行任何更改即可支持此功能。

备选方案

如引言所述,此处建议的设计的替代方案是让 trove 控制器执行自己的同步,从而使每个控制器可以查看每个 Trove 实例。这将需要通过某种形式的二阶段提交或某种最终一致性机制与每个区域的 Trove 控制器协调所有操作。实施起来将非常复杂,并且提供的优势与共享数据库实施相比很少。

Dashboard 影响 (UX)

用户应该能够选择创建新实例或集群的区域。

显示实例或集群属性的面板应该增强为显示区域名称。

实现

负责人

主要负责人

6-morgan

里程碑

完成目标里程碑

例如,Liberty-1

工作项

已实施,代码等待规范批准。

升级影响

预计不会因此更改而产生升级影响。

依赖项

没有依赖项。

测试

由于在 devstack 中创建多个区域的困难,将不会为此功能开发 int-tests。

文档影响

需要对 Trove CLI 命令的新参数进行文档记录。

参考资料

附录

无。