3.2.5 元数据/数据同步
CephRGW的多数据中心(Multisite)机制用于实现多个 Ceph 对象存储集群之间的元数据、数据同步。
1. 元数据同步
(1)多数据中心简介
Ceph 的多数据中心有如下几个概念:Realm、ZoneGroup、Zone,如图 3-31所示。
图 3-31 多数据中、示意
每个 Realm 都为一个独立的命名空间,桶名在所在命名空间内是唯一的,即一旦在某个 Realm下创建了桶A,则 A这个名字就被使用了,在该 Realm 下其他人就无法创建出名字为A的桶。
一个 Realm下可以有多个 ZoneGroup。顾名思义,每个ZoneGroup可以对应多个Zone,即 ZoneGroup是一组 Zone的集合,Zone之间同步数据和元数据。
通常一个 Zone为多台服务器组成的一个 Ceph集群,由一组 RGW 对外提供服务,一个集群上部署多个 RGW网关,以对请求进行负载均衡。
在一个 Realm下的多个 ZoneGroup中, 必须有一个 MasterZoneGroup,MasterZoneGroup中必须有一个 MasterZone,在 MasterZoneGroup下的 MasterZone中执行用户的创建、删除、修改操作会记录一些日志信息(MDlog),这些日志信息被克隆到其他 ZoneGroup下的 Zone中,其他Zone中的 RGW网关依照日志信息从 MasterZoneGroup 下的 MasterZone 中已配置的 endpoints 拉取元数据信息并执行相应操作。
在 Ceph的配置中有以下几个参数与多数据中心机制相关,分别为 rgw_realm、rgw_zonegroup、rgw_zone。一旦设置好这 3 个参数并启动 RGW网关,RGW就会默认到.rgw.root下寻找相应的 Realm、ZoneGroup、Zone 信息,如果无法找到则启动失败。用户也可以通过修改参数rgw_realm_root_pool、rgw_zonegroup_root_pool、rgw_zone_root_pool 的配置值,来告诉RGW网关到指定的存储池下读取 Realm、ZoneGroup、Zone 等信息。默认情况下,一个Realm下的不同ZoneGroup之间只会进行元数据同步,元数据包括
用户信息、bucket、bucket.instance信息。RGW实例在启动时,会启动 RGWMetaSync-
ProcessorThread 线程进行多数据中心元数据的同步服务。
一个新的ZoneGroup加入已存在的Realm 时,会执行全量同步,完成全量同步后就会进入增量同步阶段。在此阶段,新加入的 ZoneGroup下的Zone内的 RGW网关每隔 20s
(通过 INCREMENTAL_INTERVAL参数配置)到 MasterZoneGroup中拉取日志信息MDlog。在增量同步阶段,每隔 20s执行如下动作。
1) 查询请求带上 marker参数发送到MasterZoneGroup,查询是否有新增的MDlog,如果有,则拉取新增的MDlog到本地集群;
2) 读取保存在本集群的新增的 MDlog并处理,按照 MDlog记录信息发送读取元数据请求到 MasterZoneGroup;
3)保存从 MasterZoneGroup 读取到的元数据信息到本地集群;4)更新 marker参数。
(2) MDLog简介
Mdlog为 MasterZoneGroup上的RGW网关记录, 日志信息记录在 Log池下的meta.log.PERIOD.SHARD_ID对象的 OMAP上,shard_id默认为 0~31。
以下命令可查询第 0个 shard上的 omapkey,key为 1_开头,后面带记录 Log的时间戳。
#rados -p zgp2-z1.rgw.log listomapkeys meta.log.315d0473-9ff8-4828-83fd-96fdc36ed618.01_1598684563.695864_0.1
也可以使用radosgw-admin命令查看MDlog日志信息的状态,状态信息中有marker和 last_update的时间信息,marker记录上次同步的位置,last_update记录上次同步的时间。
#radosgw-admin mdlog status[
{
"marker": "1_1598768113.971442_11.1""last_update":"2020-08-3006:15:13.971442Z"
}
]
status信息保存在 Log池下的 RADOS 对象中,可通过如下命令进行查询。
#rados -p zgp2-z1.rgw.log lsmdlog.sync-status.shard.0mdlog.sync-status
当主数据中心没有元数据更新记录到MDlog时,以下是从主数据中心拉取 MDlog请求时没有新 MDlog返回的请求和响应。
GET/admin/log?type=metadata&id=0&period=315d0473-9ff8-4828-83fd-
96fdc36ed618&max-entries=100&marker=当前的 marker&rgwx-zonegroup=7085627f-27f8-
4779-9552-ebdd13c265e2HTTP/1.1
HTTP/1.1200 OK
Content-Length:44
{
"marker":""
"entries": []"truncated":false
}
当主数据中心有元数据更新记录到MDlog时,以下是从主数据中心拉取 MDlog请求时有新 MDlog返回的请求和响应。
GET/admin/log?type=metadata&id=0&period=315d0473-9ff8-4828-83fd-
96fdc36ed618&max-entries=100&marker= 当前的marker&rgwx-zonegroup=7085627f-27f8-
4779-9552-ebdd13c265e2HTTP/1.1
HTTP/1.1200 OK
Content-Length:598
{
"marker":"1_1598774754.303770_13.1"
"entries":[
{
...
}
"timestamp":"2020-08-3008:05:54.234410256Z"
"section":"user"
"data": {
"status": {
"status":"write"
如果从主数据中心拉取 MDlog请求时有新的 MDlog返回,发起拉取请求的 RGW则进入 MDlog的处理流程,即发送新的获取元数据请求到 MasterZoneGroup去拉取新的信息覆盖本地旧的元数据信息。如下为在MasterZoneGroup中创建一个用户后,非MasterZoneGroup向 MasterZoneGroup的网关发起的拉取用户信息的请求。
GET/admin/metadata/user/user001?key=user001&rgwx-zonegroup=abcHTTP/1.1
HTTP/1.1200 OK
Content-Length:713
{
"data": {
...
"default_storage_class": """keys": [
{
}
]
...
}
"access_key":""
"secret_key":""
"user":"user001"
拉取到新的用户信息写入本地集群后,更新marker参数,这样在下一个20s时带上该 Marker参数,就会只返回本Zone没有的 MDlog,对处理过的 MDlog就不会返回,从而实现了元数据的增量更新。
RGW 进程只有同时满足如下条件,才会进行MDlog的记录。
◆ rgw_zonegroup为 MasterZoneGroup。
◆ 当前的ZoneGroup内的Zone个数多于1个或者当前Realm下的ZoneGroup多于1个。
(3) 示例:非主数据中心创建桶
用户信息在 MasterZoneGroup中创建后通过MDlog的方式同步到其他 ZoneGroup下所有的 Zone中,例如非 MasterZoneGroup下的某个RGW 网关接收到创建桶的请求,该 RGW网关会将请求转发到 MasterZoneGroup的网关处理。
voidRGWCreateBucket::execute()
{
...
if (!store->svc.zone->is_meta_master()) {JSONParserjp;
op_ret=forward_request_to_master(s NULLstore in_data&jp);
...
}
如果主数据中心创建失败(例如主数据中心没有该用户,该用户在非MasterZoneGroup中创建),则创建桶失败。因此所有用户必须在MasterZoneGroup中创建,并通过 MDlog同步到其他数据中心,用户的修改和删除也必须在 MasterZoneGroup中执行。如果 MasterZoneGroup成功返回转发创建桶的请求,则继续执行剩余的创建桶流程,将 bucketinfo保存到本数据中心的RADOS。MasterZoneGroup接收到该创建桶的请求后,其下的RGW也会执行 RGWCreateBucket::execute()。由于它是主数据中心,同时也会记录 MDlog,在元数据同步线程中 MDlog会克隆到其他ZoneGroup,所有的ZoneGroup下都会保存该 bucketinfo信息,例如 MasterZoneGroup为 beijing1,创建桶的请求发到ZoneGroup为beijing2的 RGW网关,这个请求会被beijing2 的RGW网关转发到 beijng1的RGW网关,同时 MDlog信息会被同步到beijing2、guangzhou1、hunan1等所有 ZoneGroup。在创建好桶之后,如果向这个桶上传数据,则需要将上传数据请求发到ZoneGroup为beijing2的 RGW网关。如果上传请求发到ZoneGroup 为guangzhou1的 RGW网关,guangzhou1的 RGW网关会返回301 状态码,表明请求所访问的桶不在本数据中心。
对于其他桶的元数据操作,大部分的请求会被转发到MasterZoneGroup,例如删除桶、设置桶的静态网站、设置桶的多版本控制、设置桶的桶策略、设置桶的 ACL、设置桶的生命周期规则、设置桶的跨域访问配置等。
2. 数据同步
Ceph 的数据同步是指在多数据中心不同Zone 之间同步用户上传的文件数据。
可以将实体数据的同步分为 3 个部分:记录日志、数据更新通知以及数据更新。下面分别介绍。
(1)记录日志
在对数据进行操作时(比如上传、删除等操作),RGW会记录一些日志信息,为之后的实体数据同步服务。日志主要分两部分。
◆ bucketindexlog:在更新 bucketindex、设置 olh(objectlogicalhead,对象逻辑头)时会记录该日志,包含了对象、操作类型等信息;
◆ datalog:记录发生变化的 bucketshard 等信息。
在进行同步时,依据 datalog,可以知道哪些 bucketshard 发生了数据的变化。而通过bucketindexlog,则可以对对应的对象进行操作。比如,indexlog中记录的是add操作,就会从 SourceZone 获取具体的对象,如果是 remove 操作,就会把相应的对象删除。
(2)数据更新通知
与元数据同步类似,实体数据的同步同样拥有一个线程周期性地进行数据更新通知。
◆ 初始化
实体数据的更新通知由 RGWDataNotifier线程负责。它的初始化以及启动都是在RGWRados的初始化函数中。
data_notifier = new RGWDataNotifier(this);data_notifier->start();
◆ 运行
在被启动之后,每隔一段时间(由 rgw_md_notify_interval_msec配置,默认 200ms)
会进行数据更新通知,将记录在datalog中发生变化的 shard发送到其他 Zone。其他Zone在接收到更新通知后,会唤醒相应的数据同步线程。
(3)数据更新
为了与不同的 Zone进行实体数据的同步,RGW 会启动单独的同步线程。
◆ 初始化
数据同步线程的初始化,同样在 RGW 的初始化中,数据同步线程可能会有多个。对于需要进行数据同步的Zone,都会启动一个线程来专门负责从该Zone 获取同步数据。
◆ 运行
同步线程启动之后,会开始调用 RGWDataSyncProcessorThread的 process来对数据进行处理,其主要流程如下。
1) 从 RADOS中读取 datasyncstatus,以及各个 Logshard的 syncmarker。如果是第一次同步,RADOS 中还不存在这些信息,会先对 status 进行初始化。
2) datasyncstatus共 有 3种 状 态, 即 StateInit、StateBuildingFullSyncMaps、StateSync。依据状态的不同,会执行不同的操作。
a)StateInit:该状态下,会执行一些同步的初始化操作,例如,往LogPool 中写入sync_status对象,从远端获取各个 Logshard的 syncmarker写入LogPool,将 sync_status设置为 StateBuildingFullSyncMaps等。
b)StateBuildingFullSyncMaps:该状态下,会从远端获取所有的 bucket 信息。之后,会以 bucket shard为单位,将所有需要同步的bucketshard写入 OMAP中,并更新 Logshard的同步 marker,最后将状态置为StateSync。
c)StateSync:该状态即为正常的同步状态,在进行同步时,以 Logshard为单位,对每一个 Logshard进行数据同步操作。
3) 在对某个 Logshard进行数据同步时,依据Logshard的 syncmarker来判断执行全量还是增量同步。同样,在执行过一次全量同步后,之后执行的就是增量同步了。
4) 在每一个 Logshard中,包含了多个条目,每一个条目是一个bucketshard信息。对 Logshard的同步,实际上就是遍历并同步这些bucketshard。
5) 同步 bucketshard过程中有不同的状态。依据不同的状态,来依次执行初始化、全量同步以及增量同步。同样,初始化与全量同步只会执行一次,之后便是增量同步。为 了同步 bucketshard,RGW会从对端Zone的RGW获取该bucketshard的bucketindexlog,并依据这些 Log,最终决定是从远端获取实体对象,还是从本地删除对象,或者进行其他的操作,此时才会涉及真正的实体数据的操作。
(4)删除日志
如果不删除日志,则随着时间的增加,日志会越来越大。因此,RGW每隔一段时间都会删除已经同步完成的数据日志。间隔时间由rgw_sync_log_trim_interval确定,默认是1200s。在进行日志删除时,需要从各个 Zone获取数据同步状态信息,并根据这些信息, 判断出哪些日志已经被同步完成了,并将其从RADOS中删除。
(5)总结
从实体数据同步过程中可以看到涉及多种同步状态的判断。之所以有这么多的状态, 是因为 RGW 在进行数据同步时,将其在逻辑上分成了多个层级。我们可以将同步的层级分成如下4个层次。
1)Zone级别
该层级表示了与某个 Zone 的同步状态,表征该层级的同步状态由下面的结构给出。
struct rgw_data_sync_info {enum SyncState {StateInit = 0
StateBuildingFullSyncMaps=1
StateSync = 2
};
uint16_t state;uint32_tnum_shards;
rgw_data_sync_info():state((int)StateInit)num_shards(0){}
}
2)Logshard级别
记录数据变化时会将日志分成多个 Log,从而在数据量较大时,获得较好的性能。下面是表示 Logshard层级的同步状态结构。
struct rgw_data_sync_marker {enum SyncState {
FullSync = 0
IncrementalSync= 1
};
uint16_t state;stringmarker;
string next_step_marker;uint64_t total_entries;uint64_tpos;
real_timetimestamp;
rgw_data_sync_marker():state(FullSync)total_entries(0)pos(0){}
}
3)bucketshard级别
默认情况下,bucketshard大小为 0。但为了让单个容器可以承载更多的对象,不得不以牺牲 list对象的性能为代价,增大bucketshard的值。目前在线上环境中,该值通常都不配置为 0。因此,我们可以认为,每个bucket实际上都存在多个 shard,各自承载着部分对象。而在同步过程中,每一个 bucketshard都归属于某个 Logshard。对于logshard的同步,实际上就是对其下的各个 bucketshard进行同步。
4)对象级别
在每一个 bucketshard中都保存了很多对象, 同步 bucketshard,就是同步其下面的这些对象。该层级涉及对实体数据的操作,并且也是同步过程中的最底层(不考虑RADOS),没有专门的结构来保存同步信息。
3. 小结
因为 Multisite是一个Zone层面的功能处理机制,所以默认情况下是 Zone级的数据同步,即配置了Multisite之后,整个 Zone中的数据都会被同步处理。
整个Zone 层面的数据同步,操作粒度过于粗糙,在很多场景下都是非常不适用的。当前,CephRGW还支持通过bucketsyncenable/disable来启用/禁用存储桶级的数据同步,操作粒度更细,灵活度也更高。