跳转到: 导航, 搜索

Manila/design/manila-mitaka-data-replication

< Manila‎ | 设计

Manila 灾难恢复 / 数据复制设计

简介

复制的共享将在 Manila 中实现,而无需添加任何新服务或新驱动程序。创建复制共享以及添加/删除副本和其他与复制相关的操作的代码都将进入现有的共享驱动程序。允许此操作的最重要更改是共享驱动程序将负责与实现任何复制任务所需的所有存储控制器通信,即使这意味着向其他可用区 (AZ) 中的其他存储控制器发送命令。

虽然我们无法了解每个存储控制器的工作方式以及每个供应商将如何实现复制,但这种方法应该为驱动程序编写者提供所需的所有灵活性,以便以最少的 Manila 核心复杂性来实现他们需要的内容。已经有驱动程序读取其他驱动程序的配置节的例子,当操作需要与 2 个后端通信时。Manila 共享管理器预计将向其请求操作的后端通信所有必要的后端细节,这些后端存在于 AZ 之间的共享副本中。

支持 AZ 内复制,但在理想的解决方案中,AZ 应该被视为故障域。因此,此功能倾向于在 AZ 间复制用例中发挥作用。

支持的复制类型

我们希望长期支持 3 种数据复制风格

  1. 可写 - 类似于 Amazon EFS 的同步复制共享,其中所有副本都是可写的。不需要支持晋升,也不需要。
  2. 可读 - 镜像式复制,具有主 (可写) 副本和一个或多个辅助 (只读) 副本,这些副本可以在晋升辅助副本后变为可写。
  3. dr(用于灾难恢复)- 通用复制,辅助副本在晋升其中一个辅助副本之前无法访问。

副本状态

每个副本都有一个 replica_state,它有 4 个可能的值

  1. active - 所有可写副本都是活动的。
  2. in_sync - 被动副本与活动副本同步,可以晋升为活动副本。
  3. out_of_sync - 被动副本已过时,或尚未同步的新副本。
  4. error - 调度器未能调度此副本,或者在晋升非活动副本期间发生了一些不可恢复的损坏。驱动程序可以将 replica_state 设置为 error,如果在副本的整个生命周期中的任何时候发现了一些不可恢复的损坏。(通过定期的 replica_state 更新)
Manila instance replication states.png


新的共享状态

replication_change - 由活动副本的变化触发的新瞬态状态。在此状态下,将切断对共享的访问。

晋升

对于 readabledr 风格,我们将切换非活动副本与 active 副本的任务称为 晋升。对于 writable 风格的复制,晋升 没有意义,因为所有副本在任何给定时间点都是 active(可写的)。晋升 replica_state 设置为 out_of_syncerror 的副本可能不受后端支持。但是,我们希望允许此操作作为管理员功能,并且如果可能,后端可能会尊重这样的尝试。

当存在多个副本时,可能需要在后端重新定义共享之间的多个复制关系。如果驱动程序在此阶段失败,副本可能会处于不一致的状态。管理器会将所有副本实例的 replica_statestatus 设置为 error。从这个阶段恢复需要管理员干预。

用户工作流

创建支持复制的共享

  • 管理员可以创建一个带有 extra-spec replication_type 的共享类型,指定后端支持的复制风格。
  • 用户可以使用共享类型来创建允许/支持复制的新共享。
  • 复制的共享始终从一个副本实例开始,即共享本身。这不应与共享已经具有副本混淆。用户可以通过请求共享的详细信息来验证共享是否具有任何副本。

创建副本

  • POST 到 /share-replicas

用户必须指定要复制的共享的共享名称/id,副本存在的可用区以及可选的 share_network_id。根据现有设计,共享的副本不能存在于与共享相同的可用区。

  • 新创建的 share_replica 从 out_of_sync 状态开始,并且当驱动程序报告此状态时,可能会过渡到 in_sync 状态。

列出和显示副本

  • GET 从 /share-replicas

用户可以列出所有副本并验证它们的 statusreplica_state

  • GET 从 /share-replicas?share_id=<share_id>

用户可以列出特定共享的副本。

  • GET 从 /share-replicas/<id>

用户可以查看副本的详细信息。

晋升非活动副本

  • POST 到 /share-replicas/action,body 为 {'promote': None}

对于允许晋升的复制类型,用户可以通过启动 promote_replica 调用,将具有 in_sync replica_state 的副本晋升到 active replica_state。

  • 只有管理员才能尝试晋升 errorout_of_sync replica_state 的副本。

删除副本

  • DELETE 到 /share-replicas/<id>

用户可以删除副本。使用此 delete_replica 调用无法删除最后一个 active 副本。

系统工作流

  • 创建支持复制的共享
    • 与创建新共享的过程相同。
    • 共享类型中的 replication_type extra-spec 复制到 DB 上的共享s 数据中。
    • 调度器使用 replication_type extra-spec 来过滤可用的主机,并在支持该特定风格的 replication_type 的适当后端上调度共享。
  • 创建副本
    • 在数据库上创建一个共享实例。
    • 将共享实例 replica_state 更新为 out_of_sync
    • 向调度器发出调用,以查找适当的主机来调度副本。
    • 在调度器中,找到副本的加权主机。
      • 如果无法选择主机,请将副本的 statusreplica_state 更新为数据库中的 error。抛出异常。
    • 根据加权主机选择,将 create_share_replica 调用传递给驱动程序。
    • 在调用驱动程序之前,收集有关现有 active 副本实例、现有共享访问规则以及副本的 share_server 的信息,并在调用 create_replica 时将这些信息传递给驱动程序。
    • 驱动程序可以返回 export_locationsreplica_state。如果返回它们,则数据库将使用这些值进行更新。
    • 如果 replication_typewritable,则驱动程序必须将 replica_state 设置为 active
    • 所有新副本的访问规则都设置为数据库中的 active 状态。
    • 驱动程序可以抛出异常,更新副本的 statusreplica_stateerror
  • 列出/显示共享
    • 对于支持复制的共享,列出/显示共享的结果将具有新的字段,replication_type:表示支持的复制风格,以及 has_replicas,表示共享是否具有副本。
    • 对于复制的共享,主实例必须优先于那些具有 replication_change 状态或具有 replica_state 设置为 active 的那些副本。
  • 列出副本
    • 对于列出,从数据库中获取具有 replica_state 在 {in_sync, active, out_of_sync, error} 中的共享实例。
    • 如果提供了 share_id,则仅获取属于该共享并具有 replica_state 在 {in_sync, active, out_of_sync, error} 中的共享实例。
    • 必须尊重列表调用中的限制和偏移量。
  • 晋升非活动副本
    • 如果副本已经处于活动状态,则无需执行任何操作。
    • 将正在晋升的副本的 status 更新为 replication_change
    • 获取所有可用副本并调用适当的驱动程序s promote_replica 调用,传递可用的副本和新副本。
    • 如果驱动程序在此阶段抛出异常,则很有可能后端上的副本已以某种方式更改。循环遍历副本并将它们的 replica_states 设置为 error,并保持 status 不变。还将未能晋升的副本的 status 设置为 available,就像在此操作之前一样。后端可以选择在副本监控调用期间更新实际的 replica_state。
    • 驱动程序可以返回更新的副本列表。将 export_locationsreplica_states 更新到数据库。
    • 已晋升副本的 status 应从 replication_change 返回到 available
  • 定期副本更新
    • 共享管理器实现了一个循环调用,默认间隔为 5 分钟,以查询来自每个驱动程序和每个后端的所有非活动副本的 replica_state
    • 驱动程序允许将 replica_state 设置为 in_syncout_of_sync,以及在特殊情况下,error
    • 如果驱动程序将副本的 replica_state 设置为 error,则假定副本实例发生了一些不可恢复的损坏。副本实例的 status 也必须设置为 error
  • 删除共享副本
    • 如果副本没有主机,则只需从数据库中删除它。
    • 副本的 status 设置为数据库中的 deleting,并调用适当的驱动程序方法来删除副本。
    • 如果驱动程序未能删除副本,则副本的 status 更新为数据库中的 error_deleting
    • 如果驱动程序没有返回错误,则副本实例将从数据库中删除。

调度器影响

  • host_manager 必须更新来自后端的 replication_type 功能。后端必须在主机/池级别报告此功能。
  • filter_scheduler 必须将 replication_type 功能与复制共享类型要求的capability 匹配。
  • replication_type 必须是上述风格之一。

数据库影响

  • 共享导出位置

首选导出位置仅来自 replica_state 设置为 active 的实例。

 def export_locations(self):
     # TODO(gouthamr): Return AZ specific export locations for replicated
     #  shares
     # NOTE(gouthamr): For a replicated share, export locations of the
     # 'active' instances are taken, if 'available'.
     all_export_locations = []
     select_instances = list(filter(
         lambda x: x['replica_state'] == constants.REPLICA_STATE_ACTIVE,
         self.instances)) or self.instances
     for instance in select_instances:
         if instance['status'] == constants.STATUS_AVAILABLE:
             for export_location in instance.export_locations:
                 all_export_locations.append(export_location['path'])
   return all_export_locations
  • 共享实例

首选实例将是 status 设置为 replication_changestatus 设置为 availablereplica_state 设置为 active 的任何实例。

 def instance(self):
  # NOTE(gouthamr): The order of preference: status 'replication_change',
  # followed  by 'available' and 'creating'. If replicated share and
  # not undergoing a 'replication_change', only 'active' instances are
  # preferred.
  result = None
  if len(self.instances) > 0:
      order = [constants.STATUS_REPLICATION_CHANGE,
               constants.STATUS_AVAILABLE, constants.STATUS_CREATING]
      other_statuses = ([x['status'] for x in self.instances
                         if x['status'] not in order])
      order.extend(other_statuses)
      sorted_instances = sorted(
          self.instances, key=lambda x: order.index(x['status']))
      select_instances = sorted_instances
      if (select_instances[0]['status'] !=
              constants.STATUS_REPLICATION_CHANGE):
          select_instances = (
              list(filter(lambda x: x['replica_state'] ==
                          constants.REPLICA_STATE_ACTIVE,
                          sorted_instances)) or sorted_instances
          )
      result = select_instances[0]
  return result
  • 共享上的新字段
 replication_type = Column(String(255), nullable=True)
  • 共享实例上的新字段
 replica_state = Column(String(255), nullable=True)

API 设计

 GET /share-replicas/
 GET /share-replicas?share-id=<share_id>
 GET /share-replicas/<replica_id>
 POST /share-replicas/<share-id>
 Body:
 {
   'share_replica':
   {
     'availability_zone':<availability_zone_id>,
     'share_id':<share_id>,
     'share_network_id':<share_network_id>
   }
 }
 POST /share-replicas/<replica-id>/action
 Body:
 {'os-promote_replica': null}
 DELETE /share-replicas/<replica-id>

政策

policy.json - 所有复制组策略应默认为默认策略

   "share_replica:get_all": "rule:default",
   "share_replica:show": "rule:default",
   "share_replica:create" : "rule:default",
   "share_replica:delete": "rule:default",
   "share_replica:promote": "rule:default"


驱动API

对于给定的共享,一次只会调用一个复制驱动程序方法,因为它们被共享 ID 锁定。驱动程序应避免将错误报告给管理器,对于这些方法中的任何一个。如果驱动程序遇到问题,它应尝试返回正确的模型更新,并将正确的错误状态设置为代表资源的实际状态。驱动程序引发异常将导致所有副本处于错误状态,副本状态保持不变。

 def create_replica(self, context, active_replica, new_replica,
                  access_rules, share_server=None):
     """Replicate the active replica to a new replica on this backend.
     :param context: Current context
     :param active_replica: A current active replica instance dictionary.
         EXAMPLE:
          .. code::
         {
         'id': 'd487b88d-e428-4230-a465-a800c2cce5f8',
         'share_id': 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f',
         'deleted': False,
         'host': 'openstack2@cmodeSSVMNFS1',
         'status': 'available',
         'scheduled_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
         'launched_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
         'terminated_at': None,
         'replica_state': 'active',
         'availability_zone_id': 'e2c2db5c-cb2f-4697-9966-c06fb200cb80',
         'export_locations': [
             <models.ShareInstanceExportLocations>._as_dict()
         ],
         'share_network_id': '4ccd5318-65f1-11e5-9d70-feff819cdc9f',
         'share_server_id': '4ce78e7b-0ef6-4730-ac2a-fd2defefbd05',
         'share_server': <models.ShareServer>._as_dict() or None,
         }
     :param new_replica: The share replica dictionary.
         EXAMPLE:
          .. code::
         {
         'id': 'e82ff8b6-65f0-11e5-9d70-feff819cdc9f',
         'share_id': 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f',
         'deleted': False,
         'host': 'openstack2@cmodeSSVMNFS2',
         'status': 'available',
         'scheduled_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
         'launched_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
         'terminated_at': None,
         'replica_state': 'out_of_sync',
         'availability_zone_id': 'f6e146d0-65f0-11e5-9d70-feff819cdc9f',
         'export_locations': [
             models.ShareInstanceExportLocations._as_dict()
         ],
         'share_network_id': '4ccd5318-65f1-11e5-9d70-feff819cdc9f',
         'share_server_id': 'e6155221-ea00-49ef-abf9-9f89b7dd900a',
         'share_server': <models.ShareServer>._as_dict() or None,
         }
     :param access_rules: A list of access rules that other instances of
     the share already obey.
     EXAMPLE:
          .. code::
          [ {
          'id': 'f0875f6f-766b-4865-8b41-cccb4cdf1676',
          'deleted' = False,
          'share_id' = 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f',
          'access_type' = 'ip',
          'access_to' = '172.16.20.1',
          'access_level' = 'rw',
          }]
     :param share_server: <models.ShareServer>._as_dict() or None,
     Share server of the replica being created.
     :return: (export_locations, replica_state)
     export_locations is a list of paths and replica_state is one of
     active, in-sync, out-of-sync or error.
     A backend supporting 'writable' type replication should return
     'active' as the replica_state.
     Export locations should be in the same format as returned by a
     share_create. This list may be empty or None.
         EXAMPLE:
         .. code::
             [{'id': 'uuid', 'export_locations': ['export_path']}]
     """
     raise NotImplementedError()


   def delete_replica(self, context, active_replica, replica,
                  share_server=None):
     """Delete a replica.
     :param context:Current context
     :param active_replica: A current active replica instance dictionary.
         EXAMPLE:
          .. code::
         {
         'id': 'd487b88d-e428-4230-a465-a800c2cce5f8',
         'share_id': 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f',
         'deleted': False,
         'host': 'openstack2@cmodeSSVMNFS1',
         'status': 'available',
         'scheduled_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
         'launched_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
         'terminated_at': None,
         'replica_state': 'active',
         'availability_zone_id': 'e2c2db5c-cb2f-4697-9966-c06fb200cb80',
         'export_locations': [
             models.ShareInstanceExportLocations._as_dict()
         ],
         'share_network_id': '4ccd5318-65f1-11e5-9d70-feff819cdc9f',
         'share_server_id': '4ce78e7b-0ef6-4730-ac2a-fd2defefbd05',
         'share_server': <models.ShareServer>._as_dict() or None,
         }
     :param replica: Dictionary of the share replica being deleted.
         EXAMPLE:
          .. code::
         {
         'id': 'e82ff8b6-65f0-11e5-9d70-feff819cdc9f',
         'share_id': 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f',
         'deleted': False,
         'host': 'openstack2@cmodeSSVMNFS2',
         'status': 'available',
         'scheduled_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
         'launched_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
         'terminated_at': None,
         'replica_state': 'in_sync',
         'availability_zone_id': 'f6e146d0-65f0-11e5-9d70-feff819cdc9f',
         'export_locations': [
             models.ShareInstanceExportLocations._as_dict()
         ],
         'share_network_id': '4ccd5318-65f1-11e5-9d70-feff819cdc9f',
         'share_server_id': '53099868-65f1-11e5-9d70-feff819cdc9f',
         'share_server': <models.ShareServer>._as_dict() or None,
         }
     :param share_server: <models.ShareServer>._as_dict() or None,
     Share server of the replica to be deleted.
     :return: None.
     """
     raise NotImplementedError()


   def promote_replica(self, context, replica_list, replica, access_rules, share_server=None):
     """Promote a replica to 'active' replica state.
     :param context:Current context
     :param replica_list: List of all replicas for a particular share.
     This list also contains the replica to be promoted. The 'active'
     replica will have its 'replica_state' attr set to 'active'.
         EXAMPLE:
          .. code::
         [
             {
             'id': 'd487b88d-e428-4230-a465-a800c2cce5f8',
             'share_id': 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f',
             'replica_state': 'in-sync',
                 ...
             'share_server_id': '4ce78e7b-0ef6-4730-ac2a-fd2defefbd05',
             'share_server': <models.ShareServer>._as_dict() or None,
             },
             {
             'id': '10e49c3e-aca9-483b-8c2d-1c337b38d6af',
             'share_id': 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f',
             'replica_state': 'active',
                 ...
             'share_server_id': 'f63629b3-e126-4448-bec2-03f788f76094',
             'share_server': <models.ShareServer>._as_dict() or None,
             },
             {
             'id': 'e82ff8b6-65f0-11e5-9d70-feff819cdc9f',
             'share_id': 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f',
             'replica_state': 'in-sync',
                 ...
             'share_server_id': '07574742-67ea-4dfd-9844-9fbd8ada3d87',
             'share_server': <models.ShareServer>._as_dict() or None,
             },
             ...
         ]
     :param replica: Dictionary of the replica to be promoted.
         EXAMPLE:
          .. code::
         {
         'id': 'e82ff8b6-65f0-11e5-9d70-feff819cdc9f',
         'share_id': 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f',
         'deleted': False,
         'host': 'openstack2@cmodeSSVMNFS2',
         'status': 'available',
         'scheduled_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
         'launched_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
         'terminated_at': None,
         'replica_state': 'in_sync',
         'availability_zone_id': 'f6e146d0-65f0-11e5-9d70-feff819cdc9f',
         'export_locations': [
             models.ShareInstanceExportLocations._as_dict()
         ],
         'share_network_id': '4ccd5318-65f1-11e5-9d70-feff819cdc9f',
         'share_server_id': '07574742-67ea-4dfd-9844-9fbd8ada3d87',
         'share_server': <models.ShareServer>._as_dict() or None,
         }
     :param access_rules: A list of access rules that other instances of
     the share already obey.
     EXAMPLE:
          .. code::
          [ {
          'id': 'f0875f6f-766b-4865-8b41-cccb4cdf1676',
          'deleted' = False,
          'share_id' = 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f',
          'access_type' = 'ip',
          'access_to' = '172.16.20.1',
          'access_level' = 'rw',
          }]
     :param share_server: <models.ShareServer>._as_dict() or None,
     Share server of the replica to be promoted.
     :return: updated_replica_list or None
         The driver can return the updated list as in the request
         parameter. Changes that will be updated to the Database are:
         'export_locations' and 'replica_state'.
     :raises Exception
         This can be any exception derived from BaseException. This is
         re-raised by the manager after some necessary cleanup. If the
         driver raises an exception during promotion, it is assumed
         that all of the replicas of the share are in an inconsistent
         state. Recovery is only possible through the periodic update
         call and/or administrator intervention to correct the 'status'
         of the affected replicas if they become healthy again.
     """
     raise NotImplementedError()


     def update_replica_state(self, context, replica, share_server=None):
       """Update the replica_state of a replica.
       Drivers should fix replication relationships that were broken if
       possible inside this method.
       :param context:Current context
       :param replica: Dictionary of the replica being updated.
           EXAMPLE:
            .. code::
           {
           'id': 'd487b88d-e428-4230-a465-a800c2cce5f8',
           'share_id': 'f0e4bb5e-65f0-11e5-9d70-feff819cdc9f',
           'deleted': False,
           'host': 'openstack2@cmodeSSVMNFS1',
           'status': 'available',
           'scheduled_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
           'launched_at': datetime.datetime(2015, 8, 10, 0, 5, 58),
           'terminated_at': None,
           'replica_state': 'active',
           'availability_zone_id': 'e2c2db5c-cb2f-4697-9966-c06fb200cb80',
           'export_locations': [
               models.ShareInstanceExportLocations._as_dict()
           ],
           'share_network_id': '4ccd5318-65f1-11e5-9d70-feff819cdc9f',
           'share_server_id': '4ce78e7b-0ef6-4730-ac2a-fd2defefbd05',
           }
       :param share_server: <models.ShareServer>._as_dict() or None
       :return: replica_state
           replica_state - a str value denoting the replica_state that the
           replica can have. Valid values are 'in_sync' and 'out_of_sync'
           or None (to leave the current replica_state unchanged).
       """
       raise NotImplementedError()

Manila 客户端

   share-replica-create Creates a replica
   share-replica-delete Remove one or more share replicas.
   share-replica-list  List share replicas.
   share-replica-promote Promote replica to active replica.
   share-replica-show  Show details about a replica.

配置选项

replica_state_update_interval 此值(以秒为单位指定)确定共享管理器将轮询每个副本实例的健康状况(replica_state)的频率。

第一方驱动程序

DR 在可以从实验特性升级之前需要第一方驱动程序支持,主要有两个原因。

  1. 如果只有单个供应商可以开发该特性,因为它仅受专有驱动程序支持,则该特性实际上并非开放。
  2. 为了确保特性的质量,必须在上游 gate 中进行测试。

常见问题解答

  1. 如何在多 SVM 复制中处理网络问题?

    如果我们选择将复制功能限制为单 SVM 功能,则 share-network API 不需要更改。为了支持使用 share-networks 的复制,我们还需要修改 share-network 创建 API,允许创建具有 AZ 到子网映射表的 share network。这种方法允许我们为每个共享保留单个 share-network(具有关联的安全服务),同时允许租户指定足够的信息,以便每个共享实例可以连接到每个 AZ 中的适当网络。多 AZ share network 对于非复制用例也很有用。

  2. 我们是否支持池级别复制?

    一些供应商建议,某些后端可以比单个共享更有效地镜像一组共享或整个池。此设计仅解决单个共享的镜像问题。将来,我们可能会允许复制一组共享,但前提是这些组包含在单个租户中并由租户定义。

  3. 我们允许从哪里复制到哪里?是云内复制还是云间复制?我们是否允许复制到不由 Manila 管理的内容?

    云内复制。复制到 Manila 之外的内容可以提供更多的自由度,但价值却大大降低,因为我们几乎无法自动化灾难的故障转移/故障恢复部分。对于涉及 Manila 外部复制的用例,我们需要涉及具有更广泛/范围的其他工具来管理该过程。

  4. 谁配置复制?管理员?最终用户?Manila 调度器?

    最终用户。在原始设计中,我们假设实际的复制关系应该对最终用户隐藏,但这与我们添加到 Manila 的 AZ 概念不太匹配。如果用户需要控制其数据的原始副本位于哪个 AZ,那么他们也需要控制其他副本位于哪里。这意味着管理员的工作是确保对于任何已复制的共享类型,它可以从任何 AZ 复制到任何其他 AZ。

  5. 复制是否仅支持区域内复制或区域间复制?区域内 AZ 与区域间 AZ 呢?

    此设计仅适用于区域内复制。区域间复制显然是我们想要的一个特性,但它与区域内复制看起来会非常不同;此设计不涵盖该特性。区域内 AZ 复制受支持,但是建议使用区域间 AZ 复制,因为 AZ 应该被视为故障域。


未回答的问题

  1. 有没有办法实现非破坏性故障转移?

    (bswartz) 我很希望发现我们最初的直觉是错误的,因为这将改变设计的许多方面。值得花时间集思广益并研究这方面的可能性。到目前为止,最有希望的想法包括
    • 使用 VirtFS 来调解文件系统访问,并以此实现非破坏性故障转移
    • 在客户机内部使用某种代理来调解文件访问

  2. 副本是否可以使用不同的 share_type? 有一个有效的用例,即用户希望在具有与原始共享所在后端不同功能的后端上创建共享副本。例如,副本可能需要在成本较低的后端上。在这种情况下,副本是否可以使用完全不同的 share_type?

    “目前”,我们继承共享的 share_type,并认为复制必须在对称的条件下进行,即两个后端都具有类似的功能。

  3. 我们是否允许驱动程序限制可用后端之间的复制支持? 后端可能仅支持与其他兼容后端进行复制。因此,它们必须向调度器报告某种信息,以便在为现有共享创建副本时,调度器将使用该信息来安排副本的创建。应该是什么信息?

    (gouthamr) 我们正在研究在 ReplicationFilter 中包含 'driver_class_name',包括后端报告的配置 'replication_partners'。

  4. 访问规则如何在副本/共享实例之间持久化?

    • 所有副本是否应用相同的访问规则? (gouthamr):目前正在进行中
    • 访问规则是否仅应用于“活动”副本?

  5. 迁移如何影响已复制的共享?
    • (gouthamr) 在东京峰会上,我们决定目前不允许迁移具有副本的共享。必须在迁移完成后中断并重新建立复制关系才能迁移具有副本的共享。

  6. 我们需要一个 API 来启动同步吗?(重新同步或强制更新)

    这是一个好主意,并且在计划的故障转移期间,它有一个特定的用例。(gouthamr)

  7. 如何从推广失败中恢复?

    (gouthamr) 目前,如果在驱动程序级别发生某些异常,则副本将变得无用。我们是否应该提供逃生舱口以避免完全丢失,以防这些异常是可恢复的?管理员有能力“重置状态”并影响任何副本的“状态”。目前,不存在用于副本的“重置副本状态”的 API。

示例

这些示例假定副本存在于不同的 AZ 中。

可写复制示例

  1. 管理员在 AZ b1 和 b2 中设置具有 capability replication_type=writeable 的后端
  2. 管理员创建一个名为 foo 的新的 share_type
  3. 管理员将 replication_type=writeable 附加规范设置为 share type foo
  4. 用户在 AZ b1 中创建类型为 foo 的新共享
  5. 共享创建了 replication_type=writeable,并在 AZ b1 中具有 1 个活动副本
  6. 用户在 AZ b1 中向共享授予对 client1 的访问权限,获取副本的导出位置,将共享挂载到客户端,并开始写入数据
  7. 用户在 AZ b2 中添加共享的新副本
  8. 在 AZ b2 中创建第二个副本,其初始状态为 out_of_sync
  9. 稍后,副本状态更改为 active(在副本完成与原始副本同步后)
  10. 用户在 AZ b2 中向共享授予对 client2 的访问权限,获取新副本的导出位置,挂载共享,并看到 client1 写入的相同数据
  11. Client2 向共享写入一些数据,该数据立即对 client1 可见

可读复制示例

  1. 管理员在 AZ b1 和 b2 中设置具有 capability replication_type=readable 的后端
  2. 管理员创建一个名为 bar 的新的 share_type
  3. 管理员将 replication_type=readable 附加规范设置为 share type bar
  4. 用户在 AZ b1 中创建类型为 bar 的新共享
  5. 共享创建了 replication_type=readable,并在 AZ b1 中具有 1 个活动副本
  6. 用户在 AZ b1 中向共享授予对 client1 的访问权限,获取副本的导出位置,将共享挂载到客户端,并开始写入数据
  7. 用户在 AZ b2 中添加共享的新副本
  8. 在 AZ b2 中创建第二个副本,其初始状态为 out_of_sync
  9. 稍后,副本状态更改为 in_sync(在副本完成与原始副本同步后)
  10. 用户在 AZ b2 中向共享授予对 client2 的访问权限,获取新副本的导出位置,挂载共享,并看到 client1 写入的相同数据
  11. Client2 无法向共享写入数据,但继续看到更新

故障转移/故障恢复示例

(从以上继续)

  1. AZ b1 发生停电
  2. 管理员向他的用户发送有关停电的公告“b1 中的变压器烧毁了,更换需要 12 小时,请耐心等待,等等”
  3. 用户注意到他的应用程序在 client1 上不再运行,并进行了调查
  4. 用户发现 client1 已经消失,并阅读了管理员的公告解释了原因
  5. 用户注意到他的共享中的 b1 副本仍然处于活动状态,而 b2 副本处于 in_sync 状态,同时共享的状态可用
  6. 用户调用将活动副本设置为 AZ b2 的共享
  7. 共享进入 replication_change 状态,并且在 client2 上短暂丢失对共享的访问权限
  8. 副本 b2 的状态更改为 active,副本 b1 的状态更改为 out_of_sync,因为 Manila 无法联系到原始主副本。共享的状态恢复为可用状态,并且在 client2 上恢复了访问权限
  9. 用户在 client2 上启动他的应用程序,应用程序从明显的崩溃中恢复,并具有应用程序数据的一致副本
  10. 用户应用程序恢复并运行,灾难得以避免
  11. 最终完成 AZ b1 的维护,并重新激活所有设备
  12. 管理员向他的用户发送有关停电结束的公告“我们用更好的变压器更换了它,一切都恢复在线,感谢您的理解,等等”
  13. Manila 注意到 out_of_sync 副本可访问,并启动重新同步,在短时间内使 b1 副本 in_sync
  14. 一段时间后,用户阅读公告并观察到他的共享正在再次复制。他决定故意返回 b1。
  15. 用户优雅地关闭他的应用程序并刷新 I/O
  16. 用户调用将活动副本设置为 AZ b1 的共享
  17. 共享进入 replication_change 状态,并且由于应用程序已关闭,因此没有观察到任何中断
  18. 副本 b1 的状态更改为 active,副本 b2 的状态更改为 in_sync,因为 Manila 再次反转了复制。共享的状态恢复为可用状态
  19. 用户在 client1 上启动他的应用程序,应用程序干净地启动,因为已优雅地关闭

实施进度

核心工作

 Blueprint for Core API/Scheduler implementation
 Blueprint for Client implementation

Ex 驱动程序实现

 Blueprint for cDOT Driver Implementation

旧设计页面