Swift/ClusterFederationBlueprint
- Launchpad Swift 蓝图: cluster-federation
目录
摘要
这项工作旨在使帐户内容分散到多个集群,其动机是 (a) 帐户可能增长到单个集群的剩余容量之外,以及 (b) 集群提供差异化的服务级别,例如不同级别的冗余或不同的存储层级。在波特兰峰会上听取反馈后,该工作最初限制在容器级别进行分散,即帐户内的每个容器可以存储在不同的集群上,而容器内的每个对象将存储在同一集群上。
请注意,这与容器同步不同,因为对象仅存储在一个容器中。
拟议的解决方案
拟议的方法在帐户和单个“主”集群之间保持关联。容器是在帐户的主集群上创建的。当配置和/或策略指示时,此“主容器”可能会被注释为指向在不同集群(“目标集群”)中的关联“目标容器”,对象和用户元数据将实际存储在该目标容器中。此指针将作为元数据存储在主容器数据库中。主集群上的新中间件将处理所有后续容器和对象请求,方法是尝试从主容器检索指针,如果成功,则将请求转发到目标容器。
目标容器通常位于目标集群上的重复帐户中(“目标集群”)。在必要时,会按需在目标集群上创建重复帐户。所有集群都使用通用的 keystone 身份验证服务来验证帐户上的请求。
转发的主容器从不存储对象。它有两个目的:首先,它是存储指向目标容器的指针的便捷方法;其次,它的存在导致容器出现在帐户列表中。
当配置和/或策略指示目标容器与主容器相同(即容器中的对象应存储在主集群上)时,则不会向主容器添加任何指针元数据,并且它表现为正常的容器。
示例操作:容器创建
考虑对容器路径的 PUT 请求(图 1)。主集群中间件将首先尝试从主容器元数据中检索指针。使用现有的 container_info() 函数,此元数据可能位于 memcache 中;否则,将向容器服务器发出 HEAD 请求。
假设容器尚不存在,配置或策略确定新容器的目标集群。首先创建主容器,其中包含指向目标容器的指针的元数据。然后,向目标集群发出 PUT 请求以创建目标容器。
Figure 1: Container creation
home cluster_A home cluster_A target cluster_B
middleware proxy proxy
------------------------ ---------------- ------------------
---- >PUT /a/c
|
|-------------------------------->HEAD a/c
|<---- 404 Not Found
|
|
| [choose target cluster]
|
|
|-------------------------------->PUT a/c
| {ptr:cluster_B/a/c}
|
|
|---------------------------------------------------->PUT a/c
|
<----
示例操作:对象 PUT
对象 PUT 需要从主容器元数据(可能已保存在 memcache 中)中检索指针,然后将对象 PUT 转发到目标集群(图 2)。请注意,没有对象存储在主容器中。
Figure 2: Object creation
home cluster_A home cluster_A target cluster_B
middleware proxy proxy
------------------------ ---------------- ------------------
----->PUT /a/c/o
|
|-------------------------------->HEAD a/c
|<----{ptr:cluster_B/a/c}
|
|---------------------------------------------------->PUT a/c/o
|
<----
示例操作:容器删除
删除容器时,主集群中间件同样从主容器元数据中检索指针,然后尝试删除目标容器,如果成功,则删除主容器(图 3)。
Figure 3: Container deletion
home cluster_A home cluster_A target cluster_B
middleware proxy proxy
------------------------ ---------------- ------------------
----->DELETE /a/c
|
|-------------------------------->HEAD a/c
|<----{ptr:cluster_B/a/c}
|
|---------------------------------------------------->DELETE a/c
|
|
|-------------------------------->DELETE a/c
|
<----
容器统计信息
由于没有对象放入主容器,因此其统计信息(对象计数、字节计数)在对象 PUT 期间不会立即更新。一个新的后台进程,类似于容器更新守护程序,将定期扫描所有主容器数据库并更新其统计信息,方法是对目标容器执行 HEAD 操作以获取权威数据。请注意,此 HEAD 是发往目标集群代理的:这只需要容器服务器具有其他集群代理的网络可见性(如容器同步)。因此,帐户列表(GET a?format=json)或帐户元数据检索(HEAD a)可能会返回过时的统计信息,直到更新守护程序将主容器统计信息与目标容器统计信息同步。
请注意,对容器元数据的请求(HEAD a/c 或 GET a/c)将始终转发到目标容器,因此将返回准确的容器统计信息。
主/目标容器一致性
我们面临的一个挑战是在主集群和目标集群上实现容器生命周期事件的一致排序。例如,考虑对容器的并发客户端 PUT 和 DELETE 请求:除非我们可以保证主集群和目标集群上这些操作的一致排序,否则它们的结果可能不一致,并导致目标容器没有主容器,因此没有指向目标的指针(参见图 4)。更糟糕的是,并发对象 put(PUT a/c/o)可能会导致对象成功放入目标容器,而我们没有主容器。
Figure 4: risk of concurrent requests resulting in inconsistent home/target states
home cluster_A home cluster_A target cluster_B
middleware proxy proxy
------------------------ ---------------- ------------------
----->PUT a/c
|
|-------------------------------->(t1)PUT a/c
|
| DELETE a/c
| -----
| |--------------->HEAD a/c
| |<----{ptr:cluster_B/a/c}
| |
| |
| |----------------------------------->(t2)DELETE a/c
| |
| |--------------->(t3)DELETE a/c
| <----
|
|
|---------------------------------------------------->(t4)PUT a/c
|
<----
Outcome: container deleted container exists
t3 > t1 t4 > t2
可以通过确保在主集群和目标集群上复制的请求使用相同的时间戳来避免不一致结果的风险。(请注意,容器的状态由容器数据库中 put 和 delete 时间戳的值决定,而不是由写入这些值的顺序决定。)因此,我们建议将时间戳从主集群代理转发到目标集群代理(使用 X-Timestamp 标头),并让目标集群代理在请求中存在时使用转发的时间戳。(已经使用类似的机制来确保对象 PUT 到同步容器目的地时的时间戳一致性)。
Figure 5: forwarding timestamps guarantees consistent outcomes
home cluster_A home cluster_A target cluster_B
middleware proxy proxy
------------------------ ---------------- ------------------
----->PUT a/c
|
|-------------------------------->(t1)PUT a/c
|
| DELETE a/c
| -----
| |--------------->HEAD a/c
| |<----{ptr:cluster_B/a/c}
| |
| |
| |----------------------------------->(t2)DELETE a/c
| | {X-Timestamp:t3}
| |
| |--------------->(t3)DELETE a/c
| <----
|
|
|---------------------------------------------------->(t4)PUT a/c
|<---- 409 Conflict {X-Timestamp:t1}
|
<----
Outcome: container deleted container deleted
t3 > t1 t3 > t1
故障场景
如果联合操作失败,我们可能会留下一个主容器,其中包含指向不存在的目标容器的指针,例如,我们创建了主容器但未能创建目标容器。如果发生这种情况,我们有两种选择
- 所有对容器的请求都将失败(DELETE 除外);在一段时间内,主容器可能会出现在帐户容器列表中;最终,新的更新程序将发现目标容器不存在并删除主容器。
- 对容器的任何请求都会导致在需要时创建缺失的目标容器。
请注意,不可能存在没有相应主容器的目标容器,因为我们总是先 PUT 主容器,然后 PUT 目标容器,并且先 DELETE 目标容器,然后 DELETE 主容器。
安全
联合需要目标集群的一些行为,具体取决于请求是直接从客户端接收的,还是在联合内转发的。例如,X-Timestamp 标头应接受来自另一个集群转发的请求,但不接受来自客户端接收的请求。这需要某种验证请求来源的方式。
由于我们的初始用例是在单个数据中心内联合集群,因此我们最初将假定此验证源自网络可见性,即目标集群对外部客户端不可见,因此只能从转发的主集群接收请求。检查源 IP 地址提供了一些弱身份验证。
真正的对称联合,其中任何集群都可以直接从客户端或在联合内转发接收请求,将需要一些集群间身份验证机制,以及用户身份验证。例如,集群可以将凭据添加到转发的请求中,目标集群可以使用这些凭据来验证请求是否来自授权的对等集群。
在所有情况下,我们需要确保在将请求转发到目标集群时保留用户的凭据,以便目标集群中的操作被授权,就好像系统没有联合一样。
预计代码添加摘要
- 实现联合逻辑的新中间件
- 启用目标容器指针存储在容器数据库中并从中检索
- 启用接受带有容器请求的 X-Timestamp 标头
- 从目标容器更新主容器统计信息的新容器服务器守护程序