Luminous 中的新增功能:RGW 元数据搜索

2017年9月7日 yehuda

RGW 元数据搜索是 Ceph Luminous 中新增的一项功能。它能够与 Elasticsearch 集成,从而提供一个搜索 API,用于基于对象元数据查询对象存储。

新的区域类型

在 RGW 多站点系统中,一个区域是一组 radosgw 守护进程,为相同的数据提供服务,并由 Ceph 中的相同一组 RADOS 存储池提供支持。放置在同一区域组中的多个区域会镜像彼此的数据。在大多数情况下,每个集群有一个区域,并且在不同的数据中心或地理位置有多个 Ceph 集群联合起来。

作为此新的多站点架构的一部分,我们引入了一种创建新的层级或区域类型的方法。新的同步模块现在也可以将数据的副本——或元数据——发送到不同的数据层级。Enter Elasticsearch,现在可以用来索引区域组中所有对象的元数据。然后,一个区域组可以包含一些存储所有对象副本(并通过 radosgw 提供服务)的区域,以及一些索引对象元数据到 Elasticsearch 的区域。

例如,我们可以创建一个包含三个区域的区域组:区域 A、区域 B 和区域 M。区域 A 和区域 B 是数据区域。用户将在那里创建存储桶,上传对象等。区域 M 将是一个元数据搜索区域。每当数据写入区域 AB 时,elasticsearch 同步模块会将元数据的副本推送到 Elasticsearch。

在设计此功能时,我们面临的主要问题是,我们是否应该让 RGW 参与搜索查询,还是应该让用户来处理(可能通过直接访问 Elasticsearch)。我们得出结论,从用户体验的角度来看,作为用户和 Elasticsearch 之间的代理,并自行管理查询会更好。这使我们能够提供更一致的体验,并解决身份验证和授权问题。最终用户无法访问 Elasticsearch,并且我们确保发送到 Elasticsearch 的查询仅请求用户有权读取的数据。

以下是新的 API 和新的可配置项的摘要,以及一个实际的配置示例。

新的 RESTful API

为了使用和控制元数据搜索,RGW 中添加了新的 REST API

查询元数据

请求需要发送到位于 Elasticsearch 层级区域上的 RGW。

输入

GET /[]?query=

请求参数

  • max-keys:要返回的最大条目数
  • marker:结果分页标记

查询表达式的形式为

[(][)][<and|or> ...]

其中op 可以是以下任何一种:

<, <=, ==, >=, >

例如

GET /?query=name==foo

将返回用户有权读取的所有索引键,并且名称为“foo”。

输出将是一个类似于 S3 列出存储桶响应的 XML 格式的键列表。

配置自定义元数据字段

定义应该在指定的存储桶下索引哪些自定义元数据条目,以及键的类型。如果配置了显式的自定义元数据索引,则需要这样做,以便 RGW 将索引指定的自定义元数据值。否则,在索引的元数据键的类型不是字符串的情况下需要这样做。

请注意,此请求需要发送到元数据主区域。

输入

PUT /?mdsearch

HTTP 标头

X-Amz-Meta-Search: <key [; type]> [, ...]

其中 key 是 x-amz-meta-,type 是以下之一:string、integer、date。

删除自定义元数据配置

删除自定义元数据存储桶配置。此请求应发送到元数据主区域。

输入

DELETE /?mdsearch

获取自定义元数据配置

检索自定义元数据存储桶配置。

输入

GET /?mdsearch

Elasticsearch 层级区域可配置项

现在定义了以下可配置项

  • endpoint:指定访问 Elasticsearch 服务器的端点
  • num_shards(整数):Elasticsearch 在数据同步初始化时将配置的分片数。请注意,这无法在初始化后更改。这里的任何更改都需要重建 Elasticsearch 索引并重新初始化数据同步过程。
  • num_replicas(整数):Elasticsearch 在数据同步初始化时将配置的副本数。
  • explicit_custom_meta(true | false):指定是否索引所有用户自定义元数据,或者用户是否需要在存储桶级别配置应该索引哪些自定义元数据条目。默认值为 false。
  • index_buckets_list(逗号分隔的字符串列表):如果为空,将索引所有存储桶。否则,将仅索引此处指定的存储桶。可以提供存储桶前缀(例如,foo*)或存储桶后缀(例如,*bar)。
  • approved_owners_list(逗号分隔的字符串列表):如果为空,将索引所有所有者的存储桶(受其他限制),否则,将仅索引指定所有者拥有的存储桶。也可以提供后缀和前缀。
  • override_index_path(字符串):如果非空,此字符串将用作 elasticsearch 索引路径。否则,索引路径将在同步初始化时确定并生成。

配置示例

这是一个简单的配置,其中我们创建一个新的领域,一个区域组,以及该区域组中的两个区域:一个数据区域和一个元数据搜索区域。这两个区域都将在同一个 Ceph 集群上运行。

命名

为了便于说明,我们使用以下区域信息

realmgold zonegroupus data zoneus-east-1 metadata search zoneus-east-es

先决条件

  • Ceph Luminous 集群
  • 配置了 Elasticsearch。我们假设它与 radosgw 在同一台机器上运行,并侦听默认端口 9200

系统密钥

与常规多站点配置类似,我们需要定义用于跨 radosgw 通信的系统密钥

$ SYSTEM_ACCESS_KEY=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 20 | head -n 1) $ SYSTEM_SECRET_KEY=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 40 | head -n 1) $ RGW_HOST=

创建领域

$ radosgw-admin realm create --rgw-realm=gold --default

删除默认区域

仅当生成了默认区域时才需要这样做(例如,因为 radosgw 已经启动)。

$ radosgw-admin zonegroup delete --rgw-zonegroup=default

创建区域组

该区域组将包含数据区域和 Elasticsearch 区域,它们将共享相同的数据集。

$ radosgw-admin zonegroup create --rgw-zonegroup=us \ --endpoints=http://${RGW_HOST}:8000 --master --default {     "id": "db23c836-9184-4090-a6dc-8bb0489c72ba",     "name": "us",     "api_name": "us",     "is_master": "true",     "endpoints": [         "http:\/\/<RGW_HOST>:8000"     ],     "hostnames": [],     "hostnames_s3website": [],     "master_zone": "",     "zones": [],     "placement_targets": [],     "default_placement": "",     "realm_id": "0fea4ced-14fb-436d-8a4d-3d362adcf4e1" }

创建区域

在这里,我们创建将存储对象数据的常规区域。

$ radosgw-admin zone create --rgw-zonegroup=us --rgw-zone=us-east-1 \   --endpoints=http://${RGW_HOST}:8000 --access-key=$SYSTEM_ACCESS_KEY \   --secret=$SYSTEM_SECRET_KEY --default --master {     "id": "a9b9e45a-4fa6-49e8-9236-db31e84169b8",     "name": "us-east-1",     "domain_root": "us-east-1.rgw.meta:root",     "control_pool": "us-east-1.rgw.control",     "gc_pool": "us-east-1.rgw.log:gc",     "lc_pool": "us-east-1.rgw.log:lc",     "log_pool": "us-east-1.rgw.log",     "intent_log_pool": "us-east-1.rgw.log:intent",     "usage_log_pool": "us-east-1.rgw.log:usage",     "user_keys_pool": "us-east-1.rgw.meta:users.keys",     "user_email_pool": "us-east-1.rgw.meta:users.email",     "user_swift_pool": "us-east-1.rgw.meta:users.swift",     "user_uid_pool": "us-east-1.rgw.meta:users.uid",     "system_key": {         "access_key": "NgKnw4Q9ocFUJUykxHiu",         "secret_key": "QahZhmhRg12oiKOq1bVsO6qO43Yqd8OMu8jrwVSq"     },     "placement_pools": [         {             "key": "default-placement",             "val": {                 "index_pool": "us-east-1.rgw.buckets.index",                 "data_pool": "us-east-1.rgw.buckets.data",                 "data_extra_pool": "us-east-1.rgw.buckets.non-ec",                 "index_type": 0,                 "compression": ""             }         }     ],     "metadata_heap": "",     "tier_config": [],     "realm_id": "0fea4ced-14fb-436d-8a4d-3d362adcf4e1" }

创建系统用户

系统用户将用于访问 radosgw REST API。

$ radosgw-admin user create --uid=zone.user --display-name="Zone User" \     --access-key=$SYSTEM_ACCESS_KEY --secret=$SYSTEM_SECRET_KEY --system {     "user_id": "zone.user",     "display_name": "Zone User",     "email": "",     "suspended": 0,     "max_buckets": 1000,     "auid": 0,     "subusers": [],     "keys": [         {             "user": "zone.user",             "access_key": "NgKnw4Q9ocFUJUykxHiu",             "secret_key": "QahZhmhRg12oiKOq1bVsO6qO43Yqd8OMu8jrwVSq"         }     ],     "swift_keys": [],     "caps": [],     "op_mask": "read, write, delete",     "system": "true",     "default_placement": "",     "placement_tags": [],     "bucket_quota": {         "enabled": false,         "check_on_raw": false,         "max_size": -1,         "max_size_kb": 0,         "max_objects": -1     },     "user_quota": {         "enabled": false,         "check_on_raw": false,         "max_size": -1,         "max_size_kb": 0,         "max_objects": -1     },     "temp_url_keys": [],     "type": "rgw" }

更新周期

这提交了我们提出的联合变更,以便区域将开始相互通信。

$ radosgw-admin period update --commit

{     "id": "96535dc9-cb15-4c3d-96a1-d661a2f6e71f",     "epoch": 1,     "predecessor_uuid": "691ebbf4-7104-4c78-aa42-7d20061e31ff",     "sync_status": [ ...     "realm_id": "0fea4ced-14fb-436d-8a4d-3d362adcf4e1",     "realm_name": "gold",     "realm_epoch": 2 }

启动 RGW

请注意,此步骤可能因特定的操作系统和环境而异。 一种方法是

$ radosgw --rgw-frontends="civetweb port=8000" \ --log-file=/var/log/ceph/radosgw-us-east-1.log

配置同一集群中的第二个区域,用于元数据索引

这是将元数据发送到 Elasticsearch 的“区域”(即 radosgw 守护进程)。

$ radosgw-admin zone create --rgw-zonegroup=us --rgw-zone=us-east-es \ --access-key=$SYSTEM_ACCESS_KEY --secret=$SYSTEM_SECRET_KEY \ --endpoints=http://${RGW_HOST}:8002 {     "id": "24b0a61c-8a99-4f30-9bce-a99900dba818",     "name": "us-east-es",     "domain_root": "us-east-es.rgw.meta:root",     "control_pool": "us-east-es.rgw.control",     "gc_pool": "us-east-es.rgw.log:gc",     "lc_pool": "us-east-es.rgw.log:lc",     "log_pool": "us-east-es.rgw.log",     "intent_log_pool": "us-east-es.rgw.log:intent",     "usage_log_pool": "us-east-es.rgw.log:usage",     "user_keys_pool": "us-east-es.rgw.meta:users.keys",     "user_email_pool": "us-east-es.rgw.meta:users.email",     "user_swift_pool": "us-east-es.rgw.meta:users.swift",     "user_uid_pool": "us-east-es.rgw.meta:users.uid",     "system_key": {         "access_key": "NgKnw4Q9ocFUJUykxHiu",         "secret_key": "QahZhmhRg12oiKOq1bVsO6qO43Yqd8OMu8jrwVSq"     },     "placement_pools": [         {             "key": "default-placement",             "val": {                 "index_pool": "us-east-es.rgw.buckets.index",                 "data_pool": "us-east-es.rgw.buckets.data",                 "data_extra_pool": "us-east-es.rgw.buckets.non-ec",                 "index_type": 0,                 "compression": ""             }         }     ],     "metadata_heap": "",     "tier_config": [],     "realm_id": "0fea4ced-14fb-436d-8a4d-3d362adcf4e1" }

在这里,我们指定该区域的类型为 elasticsearch,并指定本地 Elasticsearch API 的端点。

$ radosgw-admin zone modify --rgw-zone=us-east-es \ --tier-type=elasticsearch \ --tier-config=endpoint=https://:9200,num_shards=10,num_replicas=1 {     "id": "24b0a61c-8a99-4f30-9bce-a99900dba818",     "name": "us-east-es" ...     "tier_config": [         {             "key": "endpoint",             "val": "https://:9200"         },         {             "key": "num_replicas",             "val": "1"         },         {             "key": "num_shards",             "val": "10"         }     ],     "realm_id": "0fea4ced-14fb-436d-8a4d-3d362adcf4e1" }

更新周期

这些更改也需要提交

$ radosgw-admin period update --commit ...

启动第二个 RGW

同样,这在您的环境中可能会有所不同。 一种方法是

$ radosgw --rgw-zone=us-east-es --rgw-frontends="civetweb port=8002" \   --log-file=/var/log/ceph/radosgw-us-east-es.log

创建用户,上传内容

$ radosgw-admin user create --uid=yehsad --display-name=yehuda ...

这里我们使用 obo 工具(可以在这里找到:https://github.com/ceph/obo)来创建存储桶并上传一些数据。 这只是一个示例,用户可以使用任何 S3 或 Swift 兼容的客户端工具上传数据。 您需要根据用户创建命令的输出填写访问密钥和密钥。

$ export S3_ACCESS_KEY_ID=... $ export S3_SECRET_ACCESS_KEY=... $ export S3_HOSTNAME=$RGW_HOST:8000 $ obo create buck $ obo put buck/foo --in-file=foo $ obo put buck/foo1 --in-file=foo

查询元数据

元数据搜索操作在 obo 中实现,并可以按如下方式使用。 首先,确保我们将 obo 指向正确的 radosgw,然后提交查询

$ export S3_HOSTNAME=$RGW_HOST:8002 $ obo mdsearch buck --query='name>=foo1' {     "SearchMetadataResponse": {         "Marker": {},         "IsTruncated": "false",         "Contents": [             {                 "Bucket": "buck",                 "Key": "foo2",                 "Instance": "null",                 "LastModified": "2017-04-06T23:18:39.053Z",                 "ETag": "\"7748956db0bddb51a2bb81a26395ff98\"",                 "Owner": {                     "ID": "yehsad",                     "DisplayName": "yehuda"                 },                 "CustomMetadata": {}             },             {                 "Bucket": "buck",                 "Key": "foo1",                 "Instance": "null",                 "LastModified": "2017-04-06T23:18:15.029Z",                 "ETag": "\"7748956db0bddb51a2bb81a26395ff98\"",                 "Owner": {                     "ID": "yehsad",                     "DisplayName": "yehuda"                 },                 "CustomMetadata": {}             }         ]     } }

配置自定义元数据

默认情况下,我们不索引任何自定义元数据。 我们可以通过以下 obo 命令打开自定义元数据索引

$ obo mdsearch buck --config='x-amz-meta-foo; string, x-amz-meta-bar; integer'

请注意,这仅适用于新数据(索引旧数据需要重新初始化特定存储桶上的同步过程)。

再次查询元数据

上传一些更多的对象,这次带有自定义元数据

$ obo put buck/foo3 --in-file=LICENSE --x-amz-meta foo=abc bar=12 $ obo put buck/foo4 --in-file=LICENSE --x-amz-meta foo=bbb bar=8 $ obo put buck/foo2 --in-file=LICENSE --x-amz-meta foo=aaa

我们可以运行以下查询

$ obo mdsearch buck --query='x-amz-meta-foo==aaa or x-amz-meta-bar < 12' {     "SearchMetadataResponse": {         "Marker": {},         "IsTruncated": "false",         "Contents": [             {                 "Bucket": "buck",                 "Key": "foo4",                 "Instance": "null",                 "LastModified": "2017-04-07T00:04:15.584Z",                 "ETag": "\"7748956db0bddb51a2bb81a26395ff98\"",                 "Owner": {                     "ID": "yehsad",                     "DisplayName": "yehuda"                 },                 "CustomMetadata": {                     "Entry": [                         {                             "Name": "foo",                             "Value": "bbb"                         },                         {                             "Name": "bar",                             "Value": "8"                         }                     ]                 }             },             {                 "Bucket": "buck",                 "Key": "foo2",                 "Instance": "null",                 "LastModified": "2017-04-07T00:05:00.666Z",                 "ETag": "\"7748956db0bddb51a2bb81a26395ff98\"",                 "Owner": {                     "ID": "yehsad",                     "DisplayName": "yehuda"                 },                 "CustomMetadata": {                     "Entry": {                         "Name": "foo",                         "Value": "aaa"                     }                 }             }         ]     } }

结论

通过最少的配置,RGW 可以利用 Elasticsearch 根据基本元数据(如对象名称)查询对象存储。 通过更多的努力,它可以用于基于您环境中可能使用的自定义元数据字段进行索引。

这基于 RGW 多站点基础设施实现,该基础设施证明非常灵活。 请继续关注未来的版本,以获取将数据复制到(甚至从)云存储服务(如 S3)的同步插件!