Catalog Index Service

https://blueprints.launchpad.net/glance/+spec/catalog-index-service

本规范旨在提高 Glance API 服务的性能,并大幅提升搜索能力。

它将通过将用户搜索查询从现有的 API 服务器卸载来提高性能。此外,我们正在 Horizon 上进行多项改进,其中包括改进图像、快照、工件的详细信息和搜索。理想的用户体验极大地依赖于丰富、动态、近乎实时的分面和聚合搜索能力,以及强大的查询语言。

最初,这将视为“实验性 API”。API 的设计目标尽可能接近最终版本,但我们保留完全放弃或更改使其不向后兼容的权利。

问题描述

Glance 拥有所有图像的元数据,并提供它们的列表。如果您想根据条件搜索图像,API 的功能有限且不够灵活。当前搜索仅限于对某些硬编码属性进行联合(“AND”操作)搜索。不支持交集(“OR”操作)。

搜索不包括搜索描述,并且对基于属性的搜索的支持有限。使用传统的关系数据库对描述进行全文搜索通常很慢。将全文索引添加到数据库可能会降低数据库的整体性能(影响插入)。

随着 Juno 版本中元数据定义的添加,新的搜索 API 应该允许用户使用标签和属性类型定义来指定搜索条件,以及使用临时字符串搜索。理想情况下,他们应该能够以近乎实时的响应速度自动完成可能的属性和属性值。

工件定义将支持基于工件类型存储动态属性。但是,使用传统的 RDBMS 无法进行适当的索引和搜索。最终会导致对给定查询不适用的表进行全表扫描。此外,编写和理解查询引擎也很难理解和维护,尤其是在原始作者离职后。

在关系数据库中实现具有高性能搜索约束的新属性非常困难。

搜索引擎需要易于定制,以便可以动态更改收集的数据,并且可以轻松修改索引和搜索方式,而无需迁移源数据。(例如,一种将命名空间分组在一起的新对象类型,称为“类别”)

典型的搜索界面还应提供自动完成和搜索建议等功能,并具有近乎实时的性能。这在传统的数据库中是不可能的。

随着时间的推移,向搜索查询添加更多属性应该易于扩展和维护,而不会使搜索逻辑的复杂性呈指数级增长。

用户应该能够轻松地将搜索查询跨资源(图像、元数据、工件)组合起来。

搜索引擎不应给主要服务的正常功能带来额外负载,并且应该能够通过在单独的服务器实例上分配负载来轻松容纳更多用户。例如,来自 Horizon 的搜索查询不应影响 Nova。

提议的变更

我们建议为 Glance 创建一个新的目录搜索服务,该服务将提高 Glance API 服务的性能,并大幅提升搜索能力。以下子部分详细介绍了这些概念。

框图

https://wiki.openstack.org/w/images/7/74/Index-service-block-diagram.png

该服务将基于 Elasticsearch。Elasticsearch 是一个基于 Lucene 的搜索引擎。它提供了一个分布式、可扩展、近乎实时的、分面的、多租户功能的全文搜索引擎,具有 RESTful Web 接口和无模式 JSON 文档。Elasticsearch 以 Apache 许可证的条款开发和发布为开源软件。Elasticsearch 的知名用户包括 Wikimedia、StumbleUpon、Mozilla、Quora、Foursquare、Etsy、SoundCloud、GitHub、FDA、CERN 和 Stack Exchange。(来源:http://en.wikipedia.org/wiki/Elasticsearch

elastic-recheck 项目也使用 Elasticsearch(和 kibana)来对 OpenStack gate 失败进行分类和跟踪。(来源:http://status.openstack.org/elastic-recheck

索引

索引

这将作为所有搜索请求的缓存。它将由 Elasticsearch 提供支持。

索引加载器

索引加载器定义了索引的数据映射,并从源加载数据。它们在服务初始化期间调用,并在稍后需要时按需调用以索引所有内容。它们确保所有适当的 RBAC 信息都包含在索引中,以便促进对搜索结果的适当授权。

索引加载器将尽可能地保持本机 API 格式,并尽可能直接地传递,以便将数据操作和维护降至最低。

Glance 图像数据的示例映射如下

{
  'dynamic': True,
  'properties': {
      'id': {'type': 'string'},
      'name': {'type': 'string'},
      'description': {'type': 'string'},
      'tags': {'type': 'string'},
      'disk_format': {'type': 'string'},
      'container_format': {'type': 'string'},
      'size': {'type': 'long'},
      'virtual_size': {'type': 'long'},
      'status': {'type': 'string'},
      'visibility': {'type': 'string'},
      'checksum': {'type': 'string'},
      'min_disk': {'type': 'long'},
      'min_ram': {'type': 'long'},
      'owner': {'type': 'string'},
      'protected': {'type': 'boolean'},
  },
}

索引更新

初始化索引后,需要不断更新它以使其与数据源保持同步。更新客户端将侦听来自数据源的通知,以重新索引特定资源的(例如,图像或工件)数据。

对于 glance,它将侦听消息主题上的通知,例如 (image.create, image.update 等),并重新索引受影响的图像元数据。(更多信息请参见 https://docs.openstack.org/developer/glance/notifications.html

索引管理 API

允许对索引中的数据进行 CRUD 管理,包括加载、更新和删除。仅允许管理员用户进行索引。

默认 policy.json 将为

{
  "catalog_index": "role:admin",
  "catalog_search": ""
}

搜索

搜索 API 允许用户执行搜索查询并获取与查询匹配的搜索结果。查询可以通过参数提供简单的查询字符串,也可以通过请求体提供。

注意

搜索查询不会被解析,而是“按原样”传递给 elastic search 引擎,除了添加过滤器之外。来自搜索引擎的响应可以根据插件实现的文件类型进行过滤。

所有搜索 API 都可以应用于索引中的多个类型,以及跨多个索引,并支持多索引语法。

这将允许搜索短语补全以及搜索建议(例如处理拼写错误)

搜索将具有两个级别的 RBAC。

1. 使用 policy.json 文件的 API 级别策略检查。这将允许对简单的拒绝/允许 API 使用进行粗粒度的 RBAC 支持。

2. RBAC 查询过滤器。这些将与索引加载器一起定义。当收到请求时,所请求的资源类型将映射到 RBAC 查询过滤器。

RBAC 查询过滤器将向发送到 elastic search 服务的请求添加任何适当的过滤器,以便仅返回用户有权查看的特定结果。

例如,图像索引加载器将包括索引所有者信息和可见性信息。RBAC 过滤器将检查传入的请求并添加过滤器,以便结果不包括来自用户请求项目以外的其他项目的非共享/非公共图像。

受保护的属性字段将从配置文件中读取,并将作为“源过滤”字段添加到 elasticsearch 查询中,这将根据用户的授权保留/删除搜索输出中的受保护字段。

备选方案

可以通过在 Glance 数据库上编写 SQL 查询来搜索数据,但有几个因素使其不是理想的解决方案

  • 实时跨多个表进行连接将使响应时间非常慢

  • 使用传统的关系数据库对描述进行全文搜索通常很慢。将全文索引添加到数据库可能会降低数据库的整体性能

  • 可以使用 metadefs 动态添加属性类型,并且在关系数据库中无法进行适当的索引

  • 搜索查询将在 Glance 核心功能使用的相同数据库上运行,并无意中影响它们的响应时间。

  • 随着时间的推移,向搜索查询添加更多属性应该易于扩展和维护,而不会使搜索逻辑的复杂性呈指数级增长

用户应该能够将搜索查询跨资源(图像、工件等)组合起来,并且搜索引擎不应与任何特定模块紧密集成。

另一种选择是让客户端加载整个数据集并在客户端内搜索。这意味着每次用户加载页面时,每个用户都会获得所有数据,并且必须将其与服务器端数据保持同步。这会增加核心 OpenStack 服务提供数据的负载和负担,并且速度较慢,因为客户端必须跨网络加载整个数据集。此外,客户端必须重新创建搜索建议和具有 AND/OR 逻辑的复杂查询等逻辑。

值得注意的是,这些选项都不包括返回结果的搜索请求评分以及可配置阈值(Elasticsearch 提供的功能)。

数据模型影响

索引的数据将存储在 Glance SQL 数据库之外,因此我们预计 Glance 中不会进行任何数据模型更改。

REST API 影响

常见响应代码

  • 创建成功:201 Created

  • 修改成功:200 OK

  • 删除成功:204 No Content

  • 失败:400 Bad Request,包含详细信息。

  • 禁止:403 Forbidden

  • 未找到:404 Not found,例如,如果未找到特定实体

  • 方法不允许:405 Not allowed,例如,尝试在列表资源上删除

  • 未实现:501 Not Implemented,例如,HEAD 未实现

这是一个实验性 API

API 版本

搜索图像支持 GET 和 POST。Elasticsearch 支持带有查询参数的 GET,但它是查询 DSL 的有限子集。GET 在这里使用请求体来实现所有可用的查询选项

请参阅以下 URI 以获取查询 DSL http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl.html

搜索图像(GET)

GET /v2/search

示例请求体

{
  "index": ["glance"],
  "type": ["image"],
  "query": {
      "query_string": {
          "query": "cirros"
      }
  }
}

示例响应体

{
  "took": 5,
  "timed_out": false,
  "_shards": {
      "total": 10,
      "successful": 10,
      "failed": 0
  },
  "hits": {
      "total": 3,
      "max_score": 0.40409642,
      "hits": [
          {
              "_index": "search",
              "_type": "image",
              "_id": "75fbdd4c-3e5b-4552-8950-9bb5262babcd",
              "_score": 0.40409642,
              "_source": {
                  "status": "active",
                  "virtual_size": null,
                  "name": "cirros-0.3.2-x86_64-uec-ramdisk",
                  "property": [],
                  "container_format": "ari",
                  "min_ram": 0,
                  "disk_format": "ari",
                  "properties": [],
                  "owner": "f72690e85b2a4ff095f50b7fad99429a",
                  "protected": false,
                  "checksum": "68085af2609d03e51c7662395b5b6e4b",
                  "min_disk": 0,
                  "is_public": true,
                  "size": 3723817,
                  "id": "75fbdd4c-3e5b-4552-8950-9bb5262babcd",
                  "description": ""
              }
          },
          {
              "_index": "search",
              "_type": "image",
              "_id": "95467ea8-dd34-4bdd-8a6a-f52e47ee9bce",
              "_score": 0.23091224,
              "_source": {
                  "status": "active",
                  "virtual_size": null,
                  "name": "cirros-0.3.2-x86_64-uec",
                  "property": [
                      "kernel_id_d00ea383-a1fa-48d3-b56c-880093730b53",
                      "ramdisk_id_75fbdd4c-3e5b-4552-8950-9bb5262babcd",
                      "hypervisor_type_uml",
                      "hw_watchdog_action_poweroff"
                  ],
                  "container_format": "ami",
                  "min_ram": 0,
                  "disk_format": "ami",
                  "properties": [
                     {
                          "name": "kernel_id",
                          "value": "d00ea383-a1fa-48d3-b56c-880093730b53"
                      },
                      {
                          "name": "ramdisk_id",
                          "value": "75fbdd4c-3e5b-4552-8950-9bb5262babcd"
                      },
                      {
                          "name": "hypervisor_type",
                          "value": "uml"
                      },
                      {
                          "name": "hw_watchdog_action",
                          "value": "poweroff"
                      }
                  ],
                  "owner": "f72690e85b2a4ff095f50b7fad99429a",
                  "protected": false,
                  "checksum": "4eada48c2843d2a262c814ddc92ecf2c",
                  "min_disk": 0,
                  "is_public": true,
                  "size": 25165824,
                  "id": "95467ea8-dd34-4bdd-8a6a-f52e47ee9bce",
                  "description": ""
              }
          },
          {
              "_index": "search",
              "_type": "image",
              "_id": "d00ea383-a1fa-48d3-b56c-880093730b53",
              "_score": 0.067124054,
              "_source": {
                  "status": "active",
                  "virtual_size": null,
                  "name": "cirros-0.3.2-x86_64-uec-kernel",
                  "property": [],
                  "container_format": "aki",
                  "min_ram": 0,
                  "disk_format": "aki",
                  "properties": [],
                  "owner": "f72690e85b2a4ff095f50b7fad99429a",
                  "protected": false,
                  "checksum": "836c69cbcd1dc4f225daedbab6edc7c7",
                  "min_disk": 0,
                  "is_public": true,
                  "size": 4969360,
                  "id": "d00ea383-a1fa-48d3-b56c-880093730b53",
                  "description": ""
              }
          }
      ]
  }
}

搜索图像(POST)

POST /v2/search

请参阅以下 URI 以获取查询 DSL http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl.html

示例请求体

{
  "index": ["glance"],
  "type": ["image"],
  "query": {
      "query_string": {
          "query": "cirros"
      }
  }
}

示例响应体

{
  "took": 5,
  "timed_out": false,
  "_shards": {
      "total": 10,
      "successful": 10,
      "failed": 0
  },
  "hits": {
      "total": 3,
      "max_score": 0.40409642,
      "hits": [
          {
              "_index": "search",
              "_type": "image",
              "_id": "75fbdd4c-3e5b-4552-8950-9bb5262babcd",
              "_score": 0.40409642,
              "_source": {
                  "status": "active",
                  "virtual_size": null,
                  "name": "cirros-0.3.2-x86_64-uec-ramdisk",
                  "property": [],
                  "container_format": "ari",
                  "min_ram": 0,
                  "disk_format": "ari",
                  "properties": [],
                  "owner": "f72690e85b2a4ff095f50b7fad99429a",
                  "protected": false,
                  "checksum": "68085af2609d03e51c7662395b5b6e4b",
                  "min_disk": 0,
                  "is_public": true,
                  "size": 3723817,
                  "id": "75fbdd4c-3e5b-4552-8950-9bb5262babcd",
                  "description": ""
              }
          },
          {
              "_index": "search",
              "_type": "image",
              "_id": "95467ea8-dd34-4bdd-8a6a-f52e47ee9bce",
              "_score": 0.23091224,
              "_source": {
                  "status": "active",
                  "virtual_size": null,
                  "name": "cirros-0.3.2-x86_64-uec",
                  "property": [
                      "kernel_id_d00ea383-a1fa-48d3-b56c-880093730b53",
                      "ramdisk_id_75fbdd4c-3e5b-4552-8950-9bb5262babcd",
                      "hypervisor_type_uml",
                      "hw_watchdog_action_poweroff"
                  ],
                  "container_format": "ami",
                  "min_ram": 0,
                  "disk_format": "ami",
                  "properties": [
                     {
                          "name": "kernel_id",
                          "value": "d00ea383-a1fa-48d3-b56c-880093730b53"
                      },
                      {
                          "name": "ramdisk_id",
                          "value": "75fbdd4c-3e5b-4552-8950-9bb5262babcd"
                      },
                      {
                          "name": "hypervisor_type",
                          "value": "uml"
                      },
                      {
                          "name": "hw_watchdog_action",
                          "value": "poweroff"
                      }
                  ],
                  "owner": "f72690e85b2a4ff095f50b7fad99429a",
                  "protected": false,
                  "checksum": "4eada48c2843d2a262c814ddc92ecf2c",
                  "min_disk": 0,
                  "is_public": true,
                  "size": 25165824,
                  "id": "95467ea8-dd34-4bdd-8a6a-f52e47ee9bce",
                  "description": ""
              }
          },
          {
              "_index": "search",
              "_type": "image",
              "_id": "d00ea383-a1fa-48d3-b56c-880093730b53",
              "_score": 0.067124054,
              "_source": {
                  "status": "active",
                  "virtual_size": null,
                  "name": "cirros-0.3.2-x86_64-uec-kernel",
                  "property": [],
                  "container_format": "aki",
                  "min_ram": 0,
                  "disk_format": "aki",
                  "properties": [],
                  "owner": "f72690e85b2a4ff095f50b7fad99429a",
                  "protected": false,
                  "checksum": "836c69cbcd1dc4f225daedbab6edc7c7",
                  "min_disk": 0,
                  "is_public": true,
                  "size": 4969360,
                  "id": "d00ea383-a1fa-48d3-b56c-880093730b53",
                  "description": ""
              }
          }
      ]
  }
}

索引图像:索引、创建、更新和删除数据

POST /v2/index

仅允许管理员用户进行索引。支持的操作是索引、创建、更新和删除

示例请求体

{
  "default_index": "search",
  "default_type": "image",
  "actions": [
      {
          "action": "create",
          "index": "search",
          "type": "image",
          "id": "d00ea383-a1fa-48d3-b56c-880093730b54",
          "data": {
              "status": "active",
              "virtual_size": null,
              "name": "cirros-0.3.3-x86_64-uec-kernel",
              "property": [],
              "container_format": "aki",
              "min_ram": 0,
              "disk_format": "aki",
              "properties": [],
              "owner": "f72690e85b2a4ff095f50b7fad99429a",
              "protected": false,
              "checksum": "836c69cbcd1dc4f225daedbab6edc7c7",
              "min_disk": 0,
              "is_public": false,
              "size": 4969360,
              "id": "d00ea383-a1fa-48d3-b56c-880093730b54",
              "description": ""
          }
      },
      {
          "action": "update",
          "index": "search",
          "type": "image",
          "id": "75fbdd4c-3e5b-4552-8950-9bb5262babcd",
          "data": {
              "name": "cirros x86",
              "status": "inactive"
          }
      },
      {
          "action": "delete",
          "index": "search",
          "type": "image",
          "id": "95467ea8-dd34-4bdd-8a6a-f52e47ee9bce"
      }
  ]
}

安全影响

对现有的 Glance API 没有影响。搜索查询将应用过滤器以返回用户有权查看的数据。请参阅描述。

通知影响

对现有的通知没有影响。将仅消耗通知。需要将 metadef 通知添加到 Glance 服务。

其他最终用户影响

根据需要更新 python-glanceclient

性能影响

对现有 API 或代码没有更改。Glance DB 中的数据将在初始化期间读取一次,以将其索引到搜索引擎中。

本规范旨在提高 Glance API 服务的性能,并大幅提升搜索能力。它将通过将用户搜索查询从现有的 API 服务器卸载来提高性能。

其他部署者影响

Glance Catalog Index 服务将作为具有自己的端口和端点的独立服务安装。

最初,这将视为“实验性 API”。API 的设计目标尽可能接近最终版本,但我们保留完全放弃或更改使其不向后兼容的权利。

glance-manage 将具有用于索引图像、metadef 和工件数据的新的命令

部署将针对单个区域服务。将来,如果需要,可以提供跨所有区域搜索的“聚合搜索”,从而可以搜索所有区域。

开发人员影响

这些是新的 API,不会影响任何现有的 API。

实现

负责人

主要负责人

lakshmi-sampath, kamil-rykowski,

其他贡献者

wayne-okuma, travis-tripp

评审人员

核心评审人

nikhil-komawar zhiyan

其他审核员

icordasc

工作项

  • 在 Glance 环境中安装 Elasticsearch(单节点)

  • 在 Elasticsearch 中索引字典数据
    • 编写一个工具,将所有元数据对象(命名空间对象、属性)从数据库索引到 elasticsearch

    • 编写一个工具,将所有图像从数据库索引到 elasticsearch

    • 合并 Glance 中使用的属性(可选)

    • 侦听来自 Glance 的通知/事件,以持续索引新的/旧的数据(可选)

  • 创建 Glance 搜索 API - 后端 Elasticsearch 的接口
    • 进行策略检查

    • 根据用户令牌过滤请求

  • 搜索图像
    • 列出所有给定搜索查询字符串的结果

  • 创建 Glance 索引 API
    • 策略检查

  • 与 Openstack/Infra 讨论
    • Elasticsearch 的测试环境

  • Devstack 集成单节点 elastic search。

  • Metadef 通知
    • 生成并侦听 metadef 通知

  • 调用工具(加载器)

  • 文档更新

  • 更新 glance 客户端

  • 更新 glance manage

依赖项

  • 依赖于 elasticsearch 作为搜索引擎

测试

将为所有可能的代码添加单元测试,目标是尽可能地隔离功能。

将在可能的情况下添加 Tempest 测试。

文档影响

需要为新服务和用法提供文档。

所有文档更改都将将其标记为“实验性 API”

参考资料