rgw-multisite概述

rgw-multisite主要涉及涉及到三种日志:

  • metadata log
  • data log
  • bucket index log

基本概念

  • zone:每个zone都是独立的ceph集群,包含osd、mon、rgw等,不会跨集群,A zone in the RGW multisite system is a set of radosgw daemons serving the same data, backed by the same set of RADOS pools in Ceph(由一组rgw提供服务,对应一组后台的pool)
  • zonegroup:可以包含多个zone,zone之间同步元数据和数据,其中只有master zone能修改元数据
  • realm:独立命名空间,可以包含zonegroup,zonegroup之间同步元数据,只有master zonegroup能修改元数据,用以支持在同一组集群中运行不同配置
  • period:拥有唯一id和epoch,用以保持当前realm的状态,每个realm都有当前关联的period以及一组按照时间顺序排列的period
  • user:user在realm里是全局唯一的
  • bucket:在realm里是全局唯一的,且只属于一个zonegroup
  • A sync module is a set of callbacks that are called for each change that happens in data (and potentially in metadata, e.g., bucket creation, new user, etc.; note: object’s metadata change is regaded as change in data) in a single zone-group. sync module是针对底层数据变化实现的一系列回调,每个zone都有一个对应的sync module,这个sync module决定了这个对应的zone能否导出数据

日志格式

核心机制

集群配置同步(realm sync)

  • 变更极致类似于git,需要update后再提交才能被其他节点感知,首次使用时需要通过pull命令来获取更新,之后的推送是主动的(RGWPeriodPusher实现了RGWRealmWatcher接口)
  • RGWPeriodPusher:与其他节点一起通过realm watcher 来推送period的更新

元数据同步 (meta sync)

  • 元数据同步和数据同步分为全局同步和增量同步,元数据的修改只能发生在master zone上,更新的过程就是通过radosgw admin api来获取metadata log,并更新对应marker

数据同步(data sync)

  • 更新的过程就是通过RGW Admin API来获取bilog,并更新对应的marker,从日志处获取信息差异后请求对方zone已变化数据的元数据信息,并调用底层module的相关接口
  • zone_data sync status :init 、 full_sync 、 incremental
    • bucket_instance_state: full_sync 、 incremental
  • 执行流程(RGWDataSyncCR):
    • RGWReadDataSyncStatusCoroutine:获取对应zone的同步状态
      • 从log-pool中获取datalog.sync-status.{source-zone-id},该obejct存储了同步状态(State)以及日志分片数(state、num_shards)
      • 处理每个日志分片,从log-pool中获取datalog.sync-status.shard.{source-zone-id}.X,该object存储了同步阶段,以及同步的marker
    • StateStateInit,RGWInitDataSyncStatusCoroutine
      • 锁住datalog.sync-status.{source-zone-id},并创建一个新的object,创建完成之后再锁住
      • 处理每个日志分片,读取remote pool(rgw.log),remote-obj(data_logX)并保存shards_info[X]
      • 对上步获取到的信息shards_info,存入本地集群中local pool(rgw.log),local obj(datalog.sync-status.shard.{source-zone-id}.X)
      • 将状态改变为StateBuildingFullSyncMaps(修改前文锁住的object并解锁)
    • StateStateBuildingFullSyncMaps,RGWListBucketIndexesCR
      • 创建entries_index(RGWShardedOmapCRManager),local pool(rgw.log), objects( data.full-sync.index.{source-zone-id}.0,。。)obj数等于bucket分片数
      • 从remote zone处获取bucket instances(RGWReadRESTResourceCR)
      • 对于上步获取的每个bucket instance,获取详细信息(RGWReadRESTResourceCR ),详细信息包括:num_shards、name of bucket、index pool、data pool。。,对于每个bucket分片
        • 将key( {bucket-name-A}:{source-zone-id}.4133.1),value(empty)写入前文entries_index obj 处
      • 对于每个日志分片X,有[bucket,shard]对应与X,该对应关系被写入日志中
    • StateStateSync,对每个日志分片X分别处理( RGWDataSyncShardControlCR-》RGWDataSyncShardCR)

插件机制

sync plugin

  • data sync module:获取数据并写入本地,支持数据导出
  • log sync module:获取数据的扩展属性,不支持数据导出
  • elasticsearch module:获取同步数据的元数据信息

公有云同步

数据同步机制(data sync)

Multi-site 同步示例

本文环境中分为source site 和 secondarysite两个集群,在source site上修改bucket中的数据,在secondary site上观察数据同步状态

数据机制浅析

/** remote site ***/

涉及到的存储池:

  • {source-zone}.rgw.data.root:存储bucket instances
  • {source-zone}.rgw.buckets.data:存储Objects
  • {source-zone}.rgw.buckets.index:存储bucket对应的index objcet(与bucket index 的分片数有关)
  • {source-zone}.rgw.log:存储修改日志(与日志分片数有关)

注意:总共有两种分片,分别为bucket shard , log shard

将bucket 分片与日志分片做映射,假设测试环境中有3个bucket(bucket-0,bucket-1,bucket-2),bucket的分片数为3(rgw_override_bucket_index_max_shards=3),日志分片数为4(rgw_data_log_num_shards=4),那么需要将9个bucket shard 与4个log shard 做映射,之后针对每个bucket shard 的修改都会记录到对应的log shard 之上,下面以objcet的写入为例来分析数据同步流程:

假设将OBJ_test 写入bucket-0 shardS中,该bucket shard 对应与 log shardX,则流程如下:

1) 将OBJ_test写入pool({source-zone}.rgw.buckets.data)中

2) 将修改信息写入pool({source-zone}.rgw.buckets.index)的obj(.dir.{key-of-bucket-0}.S)的omap属性中:

​ <OBJ_test, 基本元信息>,<.0_00000000001.4.2,_ write OBJ_444 state=CLS_RGW_STATE_PENDING_MODIFY_>,<.0_00000000002.5.3,write OBJ_444 state=CLS_RGW_STATE_COMPLETE>

之后在omap的header中记录值 0_00000000002.5.3

3) 将修改信息写入pool ({source-zone}.rgw.log)的obj(data_log.X)的omap属性中:

1_1489979397.374156_23.1 =》some info like bucket-B shardS has modification, timestamp

之后再omap的header中记录值1_1489979397.374156_23.1

总结来说,当在某个bucket做了数据修改时,所做的修改都会以kv的形式记录在对应的obj的omap中,其中k为系统生成的有序的marker,v为对应的一些元数据信息,同时在omap的header中会记录最近的一个marker

实例

代码分析

本文接上文,对multi-site中数据同步的部分代码做分析,环境还是分为source-zone和 secondary-zone两个zone分别对应两个seal集群,实验过程中在source-zone上写入object,在secondary-zone上观察数据同步状态 。

RGWDataSyncCR为实际执行数据同步的入口协程,因为本文主要以该类为起点来分析multi-site中数据增量同步(inc_sync)的过程。multi-site的实现中在boost::asio::coroutine的基础之上封装了一个协程库,在该库之上使用了大量协程来完成数据的同步。下面以secondary-zone为视角来分析数据同步过程。

RGWDataSyncCR:

  1. RGWReadDataSyncStatusCoroutine:

    从本地集群中获取远端集群数据同步状态(“state”)和日志分片数(“num_shards”),并存入到sync_status.sync_info中,对于远端集群的每个日志分片data-log-shard X,从本地集群中读取该分片同步状态(full-sync inc-sync)、marker、next_step_marker、timestamp这些信息,并存入sync_status->sync_markers[X]中:

    1
    2
    local-pool: {secondary-zone}.rgw.log
    local-obj: datalog.sync-status.{remote-zone-id}
  2. 如果1中获取的”state”为”StateInit“,RGWInitDataSyncStatusCoroutine:

    2.1 锁住 datalog.sync-status.{remote-zone-id} 这个object

    2.2 重新创建这个object( datalog.sync-status.{remote-zone-id})

    2.3 再次锁住这个object

    2.4 对于每个日志分片data-log-shard X,从远程集群中

    1
    2
    remote-pool: {source-zone}.rgw.log
    remote-obj: data-log.X

    获取该日志分片的信息,写入shards_info[x],之后将shards_info[x]写入本地集群中

    1
    2
    local-pool : {secondary-zone}.rgw.log 
    local-obj : datalog.sync-status.shard.{source-zone-id}.X

    经过这步之后,日志分片的映射情况如下:

    1
    2
    3
    4
    source-zone                   secondary-zone
    data_log.0 ======> datalog.sync-status.shard.{source-zone-id}.0
    ......
    data_log.X ======> datalog.sync-status.shard.{source-zone-id}.X
    1. 5 将数据同步状态设置为”StateBuildingFullSyncMaps”,同时写入本地集群 datalog.sync-status.{remote-zone-id} 这个object中,并将该object解锁
  3. 如果1.1中获取的”state”为“StateBuildingFullSyncMaps”,RGWListBucketIndexesCR:

    注:如果是在source-zone和secondary-zone都为空的时候配置multi-site,步骤3不做任何事,因为在build full sync map的时候没有任何数据

    3.1 创建entries_index( RGWShardedOmapCRManage),用于管理建立full sync map过程中本地的一些obj

    1
    2
    3
    4
    5
    local-pool: {secondary-zone}.rgw.log
    local-obj: data.full-sync.index.{remote-zone-id}.0
    data.full-sync.index.{remote-zone-id}.1
    ...
    data.full-sync.index.{remote-zone-id}.N, N = rgw_data_log_num_shards

    由上可知,每个本地的data.full-sync.index object对应于远端集群的一个data_log,下面则需要同步远端bucket的信息,并将bucket分片与本地的data.full-sync.index object建立对应关系

    3.2 调用RGWReadRESTResourceCR从source zone处获取所有bucket instance

    1
    2
    3
    4
    5
    remote-pool : {source-zone}.rgw.data.root
    remote-obj: .bucket.meta.{bucket-name-A}:{remote-zone-id}.4133.1
    .bucket.meta.{bucket-name-B}:{remote-zone-id}.4139.2
    .bucket.meta.{bucket-name-C}:{remote-zone-id}.4138.3
    ....

    3.3 对于3.2步中获取的每个bucket instance(例如 {bucket-name-A}:{remote-zone-id}.4133.1)调用RGWReadRESTResourceCR 获取该bucket的详细信息,包括:bucket分片数(即该bucket的index分片数)、bucket的名字、index pool、data pool等,对于每个bucket分片,做如下处理(以分片i为例):

    1
    2
    3
    # 假设分片i映射到了日志分片data_log.X上
    set omap (key:{bucket-name-A}:{source-zone_id}.4133.1 val:empty)
    to local obj(local-pool: {secondary-zone}.rgw.log, local-obj:data.full-sync.index.{remote-zone-id}.X)

    最后,data.full-sync.index.{remote-zone-id}.X上记录了对应的remote-zone上的bucket 分片,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # rados -p {zone}.rgw.log listomapkeys data.full-sync.index.{remote-zone-id}.3
    testbuckAAA:900d449b-3c56-4949-8b88-bc8a6d4ee3b1.4122.1:1
    testbuckAAA:900d449b-3c56-4949-8b88-bc8a6d4ee3b1.4122.1:5
    testbuckBBB:900d449b-3c56-4949-8b88-bc8a6d4ee3b1.4123.2:1
    testbuckBBB:900d449b-3c56-4949-8b88-bc8a6d4ee3b1.4123.2:5
    testbuckCCC:900d449b-3c56-4949-8b88-bc8a6d4ee3b1.4124.3:1
    testbuckCCC:900d449b-3c56-4949-8b88-bc8a6d4ee3b1.4124.3:5
    这就是说:
    testbuckAAA, shard1
    testbuckAAA, shard5
    testbuckBBB, shard1
    testbuckBBB, shard5
    testbuckCCC, shard1
    testbuckCCC, shard5
    这几个bucket分片被映射到data_log.X这个分片上

    3.4 对于每个日志分片data_log.X,记录被映射到这个日志分片上的bucket 分片数量:

    1
    2
    local-pool :  {secondary-zone}.rgw.log
    local-obj : datalog.sync-status.shard.{remote-zone-id}.X

  4. 如果1中获取的”state”为“StateSync”,对于每个日志分片 data-log-shard X,调用RGWDataSyncShardCR::incremental_sync来进行增量同步:

    4.2 创建set_marker_tracker(RGWDataSyncShardMarkerTrack),用来记录一个bucket shard是否在流程中和更新一个marker

    1. 5 读取远端集群中data-log.X这个object的omap header,header中包含的信息由max_marker,max_time, 如果读取的max_marker比本地记录的marker更旧的话,那么此次增量同步结束

      4.6 如果读取的max_marker比本地记录的marker更新的话,从远端集群中的data-log.X中读取这个object的omap kv对(每个kv对为一个entry),从RGWDataChangesLog::add_entry方法中可以看出这些kv对是如何组成的:

    1
    2
    1_1489653363.684562_381.1 => some info including bucke-tname, bucked-id, bucket-shard, modification timestamp
    1_1489653363.684562_381.1这个key是在 cls/log/cls_log.cc:cls_log_add中根据时间戳生成的

    对于一个bucket的任何修改都会生成一个这样的kv对存储在对应的日志分片上,因此扫描一个日志分片data_log.X的话,结果如下:

    1
    2
    3
    4
    5
    6
    # rados -p {source-zone}.rgw.log listomapkeys data_log.X
    1_1490768507.881727_3.1 => some info like bucket-2 shard3 has modification timestamp1
    1_1490768519.257779_4.1 => some info like bucket-5 shard1 has modification timestamp2
    1_1490768527.759371_5.1 => some info like bucket-1 shard4 has modification timestamp3
    1_1490768541.378290_6.1 => some info like bucket-6 shard0 has modification timestamp4
    ......

    4.7 对于4.6中返回的每个entry,如果它还没有在处理流程中(由4.2中创建的marker_tracker判断),则通过协程RGWDataSyncSingleEntryCR来处理每个entry:

    • 解析该entry从而获取bucket_name和bucket_shard_id
    • 调用协程RGWRunBucketSyncCoroutine来处理该bucket shard的数据同步:

      • a) 调用协程RGWReadBucketSyncStatusCoroutine从本地集群中读取对应object的xattrs(包含的信息有:full_marker、inc_marker、lock.sync_lock、lock.sync_lock.incremental、state等)

        1
        2
        local-pool : {secondary-zone}.rgw.log
        local-obj : bucket.sync-status.{remote-zone-id}:{bucketname}:{bucket_id}:{shard_id}
      • b) 调用协程RGWGetBucketInstanceInfoCR从本地集群中读取bucket instance的信息

      • c) 如果步骤a中获取的state为rgw_bucket_shard_sync_info::StateInit(与步骤1中获取的状态不同,步骤一种状态为rgw_data_sync_info::StatInit),调用协程 RGWInitBucketShardSyncStatusCoroutine从远端集群中读取对应的bucket index log 的相关信息写入本地集群中,同时将状态置为”StateFullSync”

        1
        2
        3
        4
        5
        remote-pool : {source-zone}.rgw.buckets.index
        remote-obj : .dir.{key-of-bucket-B}.S

        local-pool : {secondary-zone}.rgw.log
        local-obj : bucket.sync-status.{remote-zone-id}:{bucketname}:{bucket_id}:{shard_id}
      • d) 如果步骤a中获取的state为StateFullSync,调用协程RGWBucketShardFullSyncCR将状态置为“ StateIncrementalSync”

      • e)如果步骤a中获取的状态为StateIncrementalSync,调用RGWBucketShardIncrementalSyncCR:

        • 调用RGWListBucketIndexLogCR从远端集群中获取bucket分片的相关信息(omap kv对):

          1
          2
          remote-pool: {source_zone}.rgw.buckets.index
          remote-obj : .dir.{key-of-bucket-B}.S

          对于每个kv对,调用RGWBucketSyncSingleEntryCR来对对应的object进行同步:

          • 若这个kv对应的操作没有完成(op_state != CLS_RGW_STATE_COMPLETE),则跳过

          • 若对应的操作已完成(op_state == CLS_RGW_STATE_COMPLETE),则根据具体的操作类型调用RGWDefaultDataSyncModule中的具体方法来实现对该object的操作,具体的方法包括:

            1
            2
            3
            sync_object: 将远端object同步到本地集群中
            remove_object: 将objcet从本地集群中删除
            create_delete_marker: 对于开启了version功能的bucket,给对应的object创建一个删除标记,而不是直接删除该object

rgw_admin.cc::get_data_sync_status分析

  • RGWDataSyncStatusManager::init():
    • source_log.init : 初始化source zone的data_log
    • source_log.read_log_info:获取source zone的data_log数目
  • RGWRemoteDataLog::read_sync_status(),将读取结果存储到sync_status中
    • RGWReadDataSyncStatusCoroutine
      • RGWSimpleRadosReadCR<rgw_data_sync_info>:获取sync info
      • RGWReadDataSyncStatusMarkersCR:获取日志分片的marker
  • 从sync_status中读取同步状态(init、preparing for full sync 、syncing),计算full sync、inc sync的分片数目
  • RGWRemoteDataLog::read_source_log_shards_info():统计尚未同步的分片数,获取最近的已同步的修改点