3.2.4 元数据 / 数据布局
前面提到过,Ceph RGW 将数据组织分为 3 种类型:Metadata、bucket index 及 data。Metadata 是 对 象 的 元 数 据, 包 含 user、bucket、bucket.instance、OTP 等 信 息;bucket index 是对象的索引,严格意义上,也可以将其归类到元数据范畴内;data 是对象的数据,每个 RGW 对象(rgw-object)都会保存在一个或多个 RADOS 对象(system-object)里。
Ceph RADOS 层中的对象(system-object),通常以下列 3 种形式进行组织。
◆ RADOS 对象 Data ;
◆ RADOS 对象扩展属性 xattr ;
◆ RADOS 对象 OMAP。
1. RGW Metadata
Ceph RGW 的元数据包含以下信息。
◆ user :用来保存用户信息。
◆ bucket :用来维护 bucket name 和 bucket instance ID 的映射。
◆ bucket.instance :用来保存 bucket instance 信息。
◆ OPT(One-time Password mechanism):Ceph N 版新增特性,可以根据虚拟或硬件
MFA(Multi-factor Authentication)设备,基于 OTP 算法生成一个密码。
可以使用如下命令查看当前 RGW 元数据的类别。
# ./bin/radosgw-admin metadata list [ "bucket" "bucket.instance" "otp” "user" ]
(1)User 元数据
通过以下命令查看某个用户的元数据。
# ./bin/radosgw-admin metadata get user:john { "key": "user:john" "ver": { "tag": "xxx" "ver": 1 } "mtime": "2020-08-30T08:14:09.884832Z" "data": { "user_id": "john" "display_name”: “john” "email": "" "suspended": 0 "max_buckets": 1000 "subusers": [] "keys": [ { "user": "john" "access_key": "john" "secret_key": "john" } ] "swift_keys": [] "caps": [] "op_mask": "read write delete" "default_placement": "" "default_storage_class": "" "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” "mfa_ids": [] "attrs": [] } }
这些 user 信息存储在存储池 {zone}.rgw.meta 的 users:{field} 命名空间里,field 字段当前有 keys、swift、email、uid,分别对应用户的 S3 密钥对、Swift 密钥对、邮箱、用户 ID 等信息。
例如,users.uid 命名空间里,包含了用户 john 对应的两个 RADOS 对象 {uid} 和 {uid}.bucket(如 john 和 john.bucket),john 对象里存储着用户基本信息数据,john.buckets 则以 OMAP 形式保存着用户的存储桶信息。通过 RADOS 命令可以查看。
# ./bin/rados ls -p default.rgw.meta --namespace=users.uid | grep john john.buckets john
{user_id}.buckets 以 OMAP 形式保存。OMAP Key 为用户拥有的存储桶名,当用户需要列出自己的桶列表时,就对 OMAP Key 遍历来获取。OMAP Value 为桶的基本信息,如桶名、桶 ID、桶内对象大小、对象数量、桶创建时间等。
可以通过以下命令查看。
#### 使用 RADOS 命令查看用户 john 拥有的存储桶 # ./bin/rados listomapkeys -p default.rgw.meta --namespace=users.uid john.buckets john-bkt1 #### 获得 OMAP Value,然后解析出内容 # ./bin/rados getomapval -p default.rgw.meta --namespace=users.uid john. buckets john-bkt1 john-bkt1.txt Writing to john-bkt1.txt # ./bin/ceph-dencoder import john-bkt1.txt type cls_user_bucket_entry decode dump_json { "bucket": { "name": "john-bkt1" "marker": "54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1" "bucket_id": "54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1" } "size": 6775 "size_rounded": 8192 "creation_time": "2020-08-30T08:21:57.057841Z" "count": 1 "user_stats_sync": "true" }
(2)Bucket/Bucket Instance 元数据
使用以下命令获得 bucket 的元数据信息。
# ./bin/radosgw-admin metadata get bucket:john-bkt1 { "key": "bucket:john-bkt1" "ver": { "tag": "_d4u-u5S_CzORuXmXK-O280O" "ver": 1 } "mtime": "2020-08-30T08:21:56.996327Z" "data": { "bucket": { "name": "john-bkt1" "marker": "54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1" "bucket_id": "54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1" "tenant": "" "explicit_placement": { "data_pool": "" "data_extra_pool": "" "index_pool": "" } } "owner": "john" "creation_time": "2020-08-30T08:21:56.873461Z" "linked": "true" "has_bucket_info": "false" } }
使用以下命令获得 bucket instance 元数据信息。
# ./bin/radosgw-admin metadata get bucket.instance:john-bkt1:54dba15f-9c2e- 40ea-8b87-fc5f2eb01236.154118.1 { "key": "bucket.instance:john-bkt1:54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1" "ver": { "tag": "_xo05nh_v_OZBNq8zkM20Yuz" "ver": 1 } "mtime": "2020-08-30T08:21:56.981329Z" "data": { "bucket_info": { "bucket": { "name": "john-bkt1" "marker": "54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1" "bucket_id": "54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1" "tenant": "" "explicit_placement": { "data_pool": "" "data_extra_pool": "" "index_pool": "" } } "creation_time": "2020-08-30T08:21:56.873461Z" "owner": "john" "flags": 0 "zonegroup": "fddc0981-34db-49f1-b6c7-aea8d36dda1c" "placement_rule": "default-placement" "has_instance_obj": "false" "quota": { "enabled": false "check_on_raw": false "max_size": -1 "max_size_kb": 0 "max_objects": -1 } "num_shards": 11 "bi_shard_hash_type": 0 "requester_pays": "false" "has_website": "false" "swift_versioning": "false" "swift_ver_location": "" "index_type": 0 "mdsearch_config": [] "reshard_status": 0 "new_bucket_instance_id": "" } "attrs": [ { "key": "user.rgw.acl" "val": "AgJ7AAAAAwIQAAAABAAAAGpvaG4EAAAAam9obgQDXwAAAAEBAAAABAA AAGpvaG4PAAAAAQAAAAQAAABqb2huBQM0AAAAAgIEAAAAAAAAAAQAAABqb2huAAAAAAAAAAACAgQA AAAPAAAABAAAAGpvaG4AAAAAAAAAAAAAAAAAAAAA" } ] } }
bucket/bucket instance 元数据信息都存储在存储池 {zone}.rgw.meta 的 root 命名空间里。
# ./bin/rados ls -p default.rgw.meta --namespace=root | grep john john-bkt1 .bucket.meta.john-bkt1:54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1
其中,{bucket} 对象作为数据存储,记录了 bucket instance 和它的 owner 的信息。
.bucket.meta.{tenant}.{bucket}:{marker} 对象存储了存储桶元数据信息,其 xattr 里记录了访问该桶的授权信息。
#### 解析 bucket meta 信息 # ./bin/rados get -p default.rgw.meta --namespace=root .bucket.meta.johnbkt1:54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1 john-bkt1.instance.txt # ./bin/ceph-dencoder import john-bkt1.instance.txt type RGWBucketInfo decode dump_json { "bucket": { "name": "john-bkt1" "marker": "54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1" "bucket_id": "54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1" "tenant": "" "explicit_placement": { "data_pool": "" "data_extra_pool": "" "index_pool": "" } } "creation_time": "2020-08-30T08:21:56.873461Z" "owner": "john" "flags": 0 "zonegroup": "fddc0981-34db-49f1-b6c7-aea8d36dda1c" "placement_rule": "default-placement" "has_instance_obj": "false" "quota": { "enabled": false "check_on_raw": false "max_size": -1 "max_size_kb": 0 "max_objects": -1 } "num_shards": 11 "bi_shard_hash_type": 0 "requester_pays": "false" "has_website": "false" "swift_versioning": "false" "swift_ver_location": "" "index_type": 0 "mdsearch_config": [] "reshard_status": 0 "new_bucket_instance_id": "" } #### 解析 bucket ACL 信息 # ./bin/rados listxattr -p default.rgw.meta --namespace=root .bucket.meta. john-bkt1:54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1 ceph.objclass.version user.rgw.acl # ./bin/rados getxattr -p default.rgw.meta --namespace=root .bucket.meta. john-bkt1:54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1 user.rgw.acl > johnbkt1.acl.txt # ./bin/ceph-dencoder import john-bkt1.acl.txt type RGWAccessControlPolicy decode dump_json { "acl": { "acl_user_map": [ # 用户 ACL 信息 { "user": "john" "acl": 15 } ] "acl_group_map": [] # 预定义组的授权信息 "grant_map": [ # 授权用户 ACL 信息 { "id": "john" "grant": { "type": { "type": 0 } "id": "john" "email": "" "permission": { "flags": 15 } "name": "john" "group": 0 "url_spec": "" } } ] } "owner": { "id": "john" "display_name": "john" } }
(3)Bucket Index 元数据
使用以下命令获得 bucket 对应的 bucket index 信息。
# ./bin/radosgw-admin bi list --bucket john-bkt1 [ { "type": "plain" "idx": "ceph.conf” "entry": { "name": "ceph.conf" "instance": "" "ver": { "pool": 7 "epoch": 481480 } "locator": "" "exists": "true" "meta": { "category": 1 "size": 6775 "mtime": "2020-08-30T08:22:04.626979Z" "etag": "155e629efda9bd340ebf8494fed41ba4" "storage_class": "STANDARD" "owner": "john" "owner_display_name": "john" "content_type": "text/plain" "accounted_size": 6775 "user_data": "" "appendable": "false" } "tag": "54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154109.3286743" "flflags": 0 "pending_map": [] "versioned_epoch": 0 } } ]
bucket index 信 息 存 储 在 存 储 池 {zone}.rgw.buckets.index 里, 命 名 格 式 为:.dir.{bucket_id}.{shard_id}。
通过如下命令查看存储桶索引。
# ./bin/rados -p default.rgw.buckets.index ls | grep 54dba15f-9c2e-40ea-8b87- fc5f2eb01236.154118.1 .dir.54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1.6 .dir.54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1.1 .dir.54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1.4 .dir.54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1.2 .dir.54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1.5 .dir.54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1.7 .dir.54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1.10 .dir.54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1.0 .dir.54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1.8 .dir.54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1.9 .dir.54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1.3
Bucket index 维护着 bucket 和 bucket 里对象的映射信息,这个映射信息存储在RADOS 对象的 OMAP 里。如果存储桶开启了分片功能,这些映射关系会被切分,保存在多个 RADOS 对象的 OMAP 里。
OMAP Key 为 RGW 对象名,当列出桶内对象时,实际上就是遍历这些存储桶索引的OMAP 的所有 Key。
# ./bin/rados listomapkeys -p default.rgw.buckets.index .dir.54dba15f-9c2e- 40ea-8b87-fc5f2eb01236.154118.1.7 ceph.conf
OMAP Value 为对象的一些基本元数据信息。
# ./bin/rados getomapval -p default.rgw.buckets.index .dir.54dba15f-9c2e- 40ea-8b87-fc5f2eb01236.154118.1.7 ceph.conf ceph.conf.txt Writing to ceph.conf.txt # ./bin/ceph-dencoder import ceph.conf.txt type rgw_bucket_dir_entry decode dump_json { "name": "ceph.conf" "instance": "" "ver": { "pool": 7 "epoch": 481480 } "locator": "" "exists": "true" "meta": { "category": 1 "size": 6775 "mtime": "2020-08-30T08:22:04.626979Z" "etag": "155e629efda9bd340ebf8494fed41ba4" "storage_class": "STANDARD" "owner": "john" "owner_display_name": "john" "content_type": "text/plain" "accounted_size": 6775 "user_data": "" "appendable": "false" } "tag": "54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154109.3286743" "flflags": 0 "pending_map": [] "versioned_epoch": 0 }
每个 OMAP 都有 header 信息,里面包含了存储桶统计、对象数量、总大小等信息。
获 得 某 个 bucket index 对 象 对 应 的 header 信 息, 里 面 包 含 了 这 个 index 对 象 的OMAP header 对应的统计信息和分片信息。
# ./bin/rados getomapheader -p default.rgw.buckets.index .dir.54dba15f-9c2e- 40ea-8b87-fc5f2eb01236.154118.1.7 index-header.txt Writing to index-header.txt # ./bin/ceph-dencoder import index-header.txt type rgw_bucket_dir_header decode dump_json { "ver": 2 "master_ver": 0 "stats": [ 1 { "total_size": 6775 "total_size_rounded": 8192 "num_entries": 1 "actual_size": 6775 } ] "new_instance": { "reshard_status": "not-resharding" "new_bucket_instance_id": "" "num_shards": -1 } }
(4)OTP 元数据
Ceph M 版本支持 MFA。MFA(Multi-Factor Authentication)即多重要素认证,是AWS S3 提供的一个提高数据安全性的功能。它能够在用户名称和密码之外再额外增加一层保护,当用户启用 MFA 功能后,在进行操作时,除了要提供用户名和密码外,还需要提供来自 MFA 设备的身份验证代码。
当前在 AWS S3 中,MFA 主要应用于对启用版本控制功能的存储桶的数据删除场景。当前 Ceph 支持基于时间的一次性密码算法(TOTP)。
为用户配置了 MFA 后,OTP 元数据通过以下命令查看。
# ./bin/radosgw-admin metadata list otp [ "user:john" ]
相关元数据会存储在 {zone}.rgw.otp 池里。
# ./bin/rados ls -p default.rgw.otp user:john # ./bin/rados listomapkeys -p default.rgw.otp user:john header otp/1577324965
OMAP Key 的 header 对应用户的 MFA ID,otp/{totp-serial} 对应用户相应的 MFA的配置。
2. RGW Data
RGW 对象数据存储在 {zone}.rgw.buckets.data 池里,一个 RGW 对象包含一个或多个 RADOS 对象。
当 RGW 收到写请求时,会基于 rgw_obj_stripe_size 配置的值(默认为 4MB)将数据切分为 stripe,并基于 rgw_max_chunk_size 配置(默认为 4MB)将这些 stripes 划分为更小的 chunks,并将这些 chunks 写入 RADOS 集群。
第一个 chunk 写入时会创建 Head 对象,随后的 chunks 作为 tail 追加到对象后面写入。其中 Head 对象包含了对象的一些元数据信息,如 ACL、manifest、etag 等,作为 xattr 保存。Head 对象本身可以包含 4MB 的数据。如果对象大于 4MB,就会生成 tail 对象。
Head 对象的 manifest 描述了对象的布局信息。
查看对象的 xattr 和 manifest,可使用如下命令。
# ./bin/rados listxattr -p default.rgw.buckets.data 54dba15f-9c2e-40ea-8b87- fc5f2eb01236.154118.1_ceph.conf user.rgw.acl user.rgw.content_type user.rgw.etag user.rgw.idtag user.rgw.manifest user.rgw.pg_ver user.rgw.source_zone user.rgw.storage_class user.rgw.tail_tag user.rgw.x-amz-content-sha256 user.rgw.x-amz-date user.rgw.x-amz-meta-s3cmd-attrs # ./bin/ceph-dencoder import manifest.txt type RGWObjManifest decode dump_json { "objs": [] "obj_size": 6775 "explicit_objs": "false" "head_size": 6775 "max_head_size": 4194304 "prefix": ".dhrTwsaLcBPQOYPeIx2bImXjxO_WCKk_" "rules": [ { "key": 0 "val": { "start_part_num": 0 "start_ofs": 4194304 "part_size": 0 "stripe_max_size": 4194304 "override_prefix": "" } } ] "tail_instance": "" "tail_placement": { "bucket": { "name": "john-bkt1" "marker": "54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1" "bucket_id": "54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1" "tenant": "" "explicit_placement": { "data_pool": "" "data_extra_pool": "" "index_pool": "" } } "placement_rule": "default-placement" } "begin_iter": { "part_ofs": 0 "stripe_ofs": 0 "ofs": 0 "stripe_size": 6775 "cur_part_id": 0 "cur_stripe": 0 "cur_override_prefix": "" "location": { "placement_rule": "default-placement" "obj": { "bucket": { "name": "john-bkt1" "marker": "54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1" "bucket_id": "54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1" "tenant": "" "explicit_placement": { "data_pool": "" "data_extra_pool": "" "index_pool": "" } } "key": { "name": "ceph.conf" "instance": "" "ns": "" } } "raw_obj": { "pool": "" "oid”: "" "loc": "" } "is_raw": false } } "end_iter": { "part_ofs": 4194304 "stripe_ofs": 0 "ofs": 6775 "stripe_size": 6775 "cur_part_id": 0 "cur_stripe": 0 "cur_override_prefix": "" "location": { "placement_rule": "default-placement" "obj": { "bucket": { "name": "john-bkt1" "marker": "54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1" "bucket_id": "54dba15f-9c2e-40ea-8b87-fc5f2eb01236.154118.1" "tenant": "" "explicit_placement": { "data_pool": "" "data_extra_pool": "" "index_pool": "" } } "key": { "name": "ceph.conf" "instance": "" "ns": "" } } "raw_obj": { "pool": "" "oid": "" "loc": "" } "is_raw": false } } }
RGW 中的对象对应 RADOS 对象(一对多关系),对象上传分整体上传和分段上传,不同的上传方式,对应 RADOS 对象的方式不同。
首先介绍 3 个概念。
◆ rgw_max_chunk_size
RGW 下发至 RADOS 集群的单个 I/O 的大小,同时也决定了应用对象分成多个RADOS 对象时首对象的大小。
◆ rgw_obj_stripe_size
条带大小,也是 RADOS 对象最大大小,如果大于 rgw_max_chunk_size 的对象文件,后续部分会根据这个参数切成多个 RADOS 对象。
◆ rgw object manifest
管理应用对象和 RADOS 对象的对应关系。
基于以上概念,我们分别介绍普通上传以及分块上传。
普通上传的流程如下。
(1)当对象大小小于等于 rgw_max_chunk_size 时,用户上传的一个对象只对应一个RADOS 对象,该 RADOS 对象以对象名称命名,对象元数据也保存在该 RADOS 对象的扩展属性中。
(2)当对象大小大于 rgw_max_chunk_size 时,对象被划分为一个大小等于分块大小的 head 以及多个大小等于 rgw_obj_stripe_size 的中间对象,和一个大小小于或等于 rgw_obj_stripe_size 的 tail 对象。head 以对象名称命名,该对象的数据部分保存了对象前 rgw_max_chunk_size 字节的数据,扩展属性部分保存了对象的元数据信息和 manifest 信息。
中间对象和 tail 对象保存对象剩余的数据,对象名称为:''shadow_' + '.' + '32bit 随机字符串' + '_' + ' 条带编号 ',其中条带编号从 1 开始。
分块上传的流程如下。
(1)RGW 根据条带大小 rgw_obj_stripe_size 将对象的每一个分块分成多个 RADOS对象,每个分块的第一个 RADOS 对象名称为:'_multipart_' + ' 用户上传对象名称 ' + ' 分块上传 ID' + ' 分段编号 ',其余对象的名称为:'_shadow_' + ' 用户上传对象名称 ' + ' 分块上传 ID' + ' 分段编号 ' + '_' + ' 条带编号 '。
(2)当所有的分块上传结束后,RGW 会从 data_extra_pool 中的分块上传的临时对象中读取各个分段信息,将各分段的 manifest 信息组成一个 manifest ;然后生成一个新的RADOS 对象,即 head 对象,用来保存分块上传的对象的元数据信息和 manifest 信息。