容器到容器同步

概述

Swift 具有一项功能,可以通过后台同步将容器的所有内容镜像到另一个容器。Swift 集群操作员配置其集群以允许/接受来自其他集群的同步请求,并且用户指定将容器同步到何处以及同步密钥。

注意

如果您正在使用 大对象 功能并同步到另一个集群,则需要确保清单文件和分段文件已同步。如果分段文件位于与它们的清单不同的容器中,则必须同步清单的容器和分段的容器。同步分段文件的目标容器必须始终与它们的源容器具有相同的名称,以便同步的清单可以解析它们。

请注意,即使它们位于同一容器中并且在分段文件之后创建,清单文件也可能在分段文件之前同步。

对于 静态大对象,对分段尚未完全同步的清单的 GET 请求将失败,并且返回的大对象内容可能为空或仅为部分内容。

对于 动态大对象,对分段尚未完全同步的清单的 GET 请求将失败或返回意外(并且很可能不正确)的内容。

注意

如果您在对象正在同步的集群中使用加密中间件,则应遵循 容器同步配置 中的说明以与加密兼容。

注意

如果您在对象正在同步的集群中使用符号链接中间件,则应遵循 容器同步配置 中的说明以与符号链接兼容。

请注意,即使它们位于同一容器中并且在目标对象之后创建,符号链接也可能在目标之前同步。在这种情况下,对符号链接的 GET 请求将以 404 Not Found 错误失败。如果目标已被覆盖,GET 可能会生成旧版本(对于动态链接)或 409 Conflict 错误(对于静态链接)。

配置容器同步

创建一个 container-sync-realms.conf 文件,指定允许的集群及其信息

[realm1]
key = realm1key
key2 = realm1key2
cluster_clustername1 = https://host1/v1/
cluster_clustername2 = https://host2/v1/

[realm2]
key = realm2key
key2 = realm2key2
cluster_clustername3 = https://host3/v1/
cluster_clustername4 = https://host4/v1/

每个部分名称是同步域的名称。同步域是一组已同意相互允许容器同步的集群。领域名称将不区分大小写。

key 是集群到集群的总体密钥,与外部用户在其容器的 X-Container-Sync-Key 元数据标头值中设置的密钥结合使用。这些密钥将用于签署容器同步守护程序发出的每个请求,并用于验证每个传入的容器同步请求。

key2 是可选的,是传入请求将要检查的附加密钥。这样您就可以旋转密钥;您将现有的 key 移动到 key2 并创建一个新的 key 值。

领域部分中的任何名称以 cluster_ 开头的条目都将指示集群的名称和端点,外部用户将在其容器的 X-Container-Sync-To 元数据标头值中使用它们,格式为 //realm_name/cluster_name/account_name/container_name。领域和集群名称不区分大小写。

端点是容器同步守护程序在向该集群发送请求时将使用的端点。请记住,此端点必须可以被所有容器服务器访问,因为容器同步守护程序就在那里运行。请注意,端点以 /v1/ 结尾,然后容器同步守护程序将在其后添加 account/container/obj 名称。

将此 container-sync-realms.conf 文件分发到所有代理服务器和容器服务器。

您还需要将 container_sync 中间件添加到代理管道。它需要在任何 memcache 中间件之后和任何身份验证中间件之前。 [filter:container_sync] 部分只需要 use 项。例如

[pipeline:main]
pipeline = healthcheck proxy-logging cache container_sync tempauth proxy-logging proxy-server

[filter:container_sync]
use = egg:swift#container_sync

容器同步守护程序将使用内部客户端来同步对象。即使您没有配置内部客户端,容器同步守护程序也将使用默认配置工作。默认配置与 internal-client.conf-sample 相同。如果您想配置内部客户端,请更新 container-server.conf 中的 internal_client_conf_path。路径中的配置文件将用于内部客户端。

旧式:配置集群的可同步主机

本节介绍使用容器同步的旧式方法。有关新式方法,请参阅上一节“配置容器同步”。

使用旧式方法时,Swift 集群操作员必须在用户启用容器同步之前允许与一组主机同步。首先,后端容器服务器需要在 container-server.conf 文件中提供此主机列表

[DEFAULT]
# This is a comma separated list of hosts allowed in the
# X-Container-Sync-To field for containers.
# allowed_sync_hosts = 127.0.0.1
allowed_sync_hosts = host1,host2,etc.
...

[container-sync]
# You can override the default log routing for this app here (don't
# use set!):
# log_name = container-sync
# log_facility = LOG_LOCAL0
# log_level = INFO
# Will sync, at most, each container once per interval
# interval = 300
# Maximum amount of time to spend syncing each container
# container_time = 60

记录容器同步

当前,日志处理是跟踪同步进度、问题甚至容器同步的常规活动唯一的方法。鉴于此,您可能希望将上述 log_ 选项设置为将容器同步日志定向到不同的文件,以便更轻松地监视。此外,应注意的是,除了对两个容器执行 HEAD 并比较总体信息外,最终用户无法监视同步进度或检测问题。

容器同步统计信息

容器同步 INFO 级别日志包含活动指标和会计信息,以便进行深入的跟踪。当前收集两种不同的统计信息

大约每小时一次,将容器同步执行的所有操作的累积统计信息报告到日志文件中,格式如下

Since (time): (sync) synced [(delete) deletes, (put) puts], (skip) skipped, (fail) failed
时间

上次报告时间

同步

已成功同步的启用同步的容器数

删除

目标集群上成功的 DELETE 对象请求数

put

目标集群上成功的 PUT 对象请求数

skip

同步已关闭但尚未从同步存储中清除的容器数

fail

由于异常、超时或其他原因而导致失败的容器数

对于同步的每个容器,将使用以下格式报告每个容器的统计信息

Container sync report: (container), time window start: (start), time window end: %(end), puts: (puts), posts: (posts), deletes: (deletes), bytes: (bytes), sync_point1: (point1), sync_point2: (point2), total_rows: (total)
container

account/container 统计信息适用于

start

报告开始时间

end

报告结束时间

puts

目标容器上成功的 PUT 对象请求数

posts

N/A (0)

deletes

目标容器上成功的 DELETE 对象请求数

bytes

发送到目标容器的网络字节数

point1

进度指示器 - 容器的 x_container_sync_point1

point2

进度指示器 - 容器的 x_container_sync_point2

total

容器中处理的对象总数

可能多个服务器同步一个容器,因此需要评估所有服务器的日志文件

使用 swift 工具设置同步的容器

注意

swift 工具可从 python-swiftclient 库获得。

注意

您必须是帐户管理员才能设置同步目标和密钥。

您只需告诉每个容器同步到何处并为其提供一个秘密同步密钥。首先,让我们获取两个集群帐户的帐户详细信息

$ swift -A http://cluster1/auth/v1.0 -U test:tester -K testing stat -v
StorageURL: http://cluster1/v1/AUTH_208d1854-e475-4500-b315-81de645d060e
Auth Token: AUTH_tkd5359e46ff9e419fa193dbd367f3cd19
   Account: AUTH_208d1854-e475-4500-b315-81de645d060e
Containers: 0
   Objects: 0
     Bytes: 0

$ swift -A http://cluster2/auth/v1.0 -U test2:tester2 -K testing2 stat -v
StorageURL: http://cluster2/v1/AUTH_33cdcad8-09fb-4940-90da-0f00cbf21c7c
Auth Token: AUTH_tk816a1aaf403c49adb92ecfca2f88e430
   Account: AUTH_33cdcad8-09fb-4940-90da-0f00cbf21c7c
Containers: 0
   Objects: 0
     Bytes: 0

现在,让我们创建我们的第一个容器并告诉它同步到我们稍后将创建的第二个容器

$ swift -A http://cluster1/auth/v1.0 -U test:tester -K testing post \
  -t '//realm_name/clustername2/AUTH_33cdcad8-09fb-4940-90da-0f00cbf21c7c/container2' \
  -k 'secret' container1

-t 指示要同步的集群,即 container-sync-realms.conf 中的部分名称,后跟该部分中的集群名称(没有 cluster_ 前缀),后跟我们要同步到的帐户和容器名称。 -k 指定两个容器将共享的同步密钥;集群密钥也在 container-sync-realms.conf 中后台使用。

现在,我们将对第二个集群的容器执行类似的操作

$ swift -A http://cluster2/auth/v1.0 -U test2:tester2 -K testing2 post \
  -t '//realm_name/clustername1/AUTH_208d1854-e475-4500-b315-81de645d060e/container1' \
  -k 'secret' container2

就是这样。现在我们可以将大量内容上传到第一个容器,并观察它如何同步到第二个容器

$ swift -A http://cluster1/auth/v1.0 -U test:tester -K testing \
  upload container1 .
photo002.png
photo004.png
photo001.png
photo003.png

$ swift -A http://cluster2/auth/v1.0 -U test2:tester2 -K testing2 \
  list container2

[Nothing there yet, so we wait a bit...]

注意

如果您是运行 SAIO (Swift All In One) 并仅进行测试的操作员,那么每次配置容器进行同步并将对象放置在源容器中时,都需要确保 container-sync 在尝试从目标容器检索对象之前运行。也就是说,您需要运行

swift-init container-sync once

现在预计会看到对象从第一个容器复制到第二个容器

$ swift -A http://cluster2/auth/v1.0 -U test2:tester2 -K testing2 \
  list container2
photo001.png
photo002.png
photo003.png
photo004.png

您还可以设置同步容器链,如果您想要超过两个容器。您将指向 1 -> 2,然后 2 -> 3,最后 3 -> 1,用于三个容器。它们都需要共享相同的秘密同步密钥。

使用 curl(或其他工具)代替

swift 在后台做什么?没什么特别复杂的。它将 -t <value> 选项转换为 X-Container-Sync-To: <value> 标头,并将 -k <value> 选项转换为 X-Container-Sync-Key: <value> 标头。

例如,当我们创建第一个容器并告诉它同步到第二个容器时,我们可以使用此 curl 命令

$ curl -i -X POST -H 'X-Auth-Token: AUTH_tkd5359e46ff9e419fa193dbd367f3cd19' \
  -H 'X-Container-Sync-To: //realm_name/clustername2/AUTH_33cdcad8-09fb-4940-90da-0f00cbf21c7c/container2' \
  -H 'X-Container-Sync-Key: secret' \
  'http://cluster1/v1/AUTH_208d1854-e475-4500-b315-81de645d060e/container1'
HTTP/1.1 204 No Content
Content-Length: 0
Content-Type: text/plain; charset=UTF-8
Date: Thu, 24 Feb 2011 22:39:14 GMT

旧式:使用 swift 工具设置同步的容器

注意

swift 工具可从 python-swiftclient 库获得。

注意

您必须是帐户管理员才能设置同步目标和密钥。

这是使用 allowed_sync_hosts 进行容器同步的旧式方法。

您只需告诉每个容器同步到何处并为其提供一个秘密同步密钥。首先,让我们获取两个集群帐户的帐户详细信息

$ swift -A http://cluster1/auth/v1.0 -U test:tester -K testing stat -v
StorageURL: http://cluster1/v1/AUTH_208d1854-e475-4500-b315-81de645d060e
Auth Token: AUTH_tkd5359e46ff9e419fa193dbd367f3cd19
   Account: AUTH_208d1854-e475-4500-b315-81de645d060e
Containers: 0
   Objects: 0
     Bytes: 0

$ swift -A http://cluster2/auth/v1.0 -U test2:tester2 -K testing2 stat -v
StorageURL: http://cluster2/v1/AUTH_33cdcad8-09fb-4940-90da-0f00cbf21c7c
Auth Token: AUTH_tk816a1aaf403c49adb92ecfca2f88e430
   Account: AUTH_33cdcad8-09fb-4940-90da-0f00cbf21c7c
Containers: 0
   Objects: 0
     Bytes: 0

现在,让我们创建我们的第一个容器并告诉它同步到我们稍后将创建的第二个容器

$ swift -A http://cluster1/auth/v1.0 -U test:tester -K testing post \
  -t 'http://cluster2/v1/AUTH_33cdcad8-09fb-4940-90da-0f00cbf21c7c/container2' \
  -k 'secret' container1

-t 指示要同步到的 URL,即我们从 cluster2 检索到的 StorageURL 加上容器名称。 -k 指定两个容器将共享的同步密钥。现在,我们将对第二个集群的容器执行类似的操作

$ swift -A http://cluster2/auth/v1.0 -U test2:tester2 -K testing2 post \
  -t 'http://cluster1/v1/AUTH_208d1854-e475-4500-b315-81de645d060e/container1' \
  -k 'secret' container2

就是这样。现在我们可以将大量内容上传到第一个容器,并观察它如何同步到第二个容器

$ swift -A http://cluster1/auth/v1.0 -U test:tester -K testing \
  upload container1 .
photo002.png
photo004.png
photo001.png
photo003.png

$ swift -A http://cluster2/auth/v1.0 -U test2:tester2 -K testing2 \
  list container2

[Nothing there yet, so we wait a bit...]
[If you're an operator running SAIO and just testing, you may need to
 run 'swift-init container-sync once' to perform a sync scan.]

$ swift -A http://cluster2/auth/v1.0 -U test2:tester2 -K testing2 \
  list container2
photo001.png
photo002.png
photo003.png
photo004.png

您还可以设置同步容器链,如果您想要超过两个容器。您将指向 1 -> 2,然后 2 -> 3,最后 3 -> 1,用于三个容器。它们都需要共享相同的秘密同步密钥。

旧式:使用 curl(或其他工具)代替

这是使用 allowed_sync_hosts 进行容器同步的旧式方法。

swift 在后台做什么?没什么特别复杂的。它将 -t <value> 选项转换为 X-Container-Sync-To: <value> 标头,并将 -k <value> 选项转换为 X-Container-Sync-Key: <value> 标头。

例如,当我们创建第一个容器并告诉它同步到第二个容器时,我们可以使用此 curl 命令

$ curl -i -X POST -H 'X-Auth-Token: AUTH_tkd5359e46ff9e419fa193dbd367f3cd19' \
  -H 'X-Container-Sync-To: http://cluster2/v1/AUTH_33cdcad8-09fb-4940-90da-0f00cbf21c7c/container2' \
  -H 'X-Container-Sync-Key: secret' \
  'http://cluster1/v1/AUTH_208d1854-e475-4500-b315-81de645d060e/container1'
HTTP/1.1 204 No Content
Content-Length: 0
Content-Type: text/plain; charset=UTF-8
Date: Thu, 24 Feb 2011 22:39:14 GMT

集群内部发生了什么?

容器环设备有一个名为 containers 的目录,容器数据库就位于其中。除了 containers 之外,每个容器环设备还有一个名为 sync-containers 的目录。sync-containers 包含指向使用 x-container-sync-tox-container-sync-key 元数据键配置为容器同步的容器数据库的符号链接。

swift-container-sync 进程负责将更新发送到远程容器。这是通过扫描 sync-containers 目录中的容器数据库来完成的。对于找到的每个容器数据库,自上次同步以来较新的行将触发对另一个容器的 PUT 或 DELETE 操作。

sync-containers 的维护方式如下:每当容器服务器处理携带 x-container-sync-tox-container-sync-key 元数据键的 PUT 或 POST 请求时,服务器会在 sync-containers 中创建指向容器数据库的符号链接。每当容器服务器删除同步的容器时,将从 sync-containers 中删除相应的符号链接。

除了容器服务器之外,容器复制器进程还负责识别应该同步的容器。这是通过扫描本地设备中的容器数据库并检查 x-container-sync-tox-container-sync-key 元数据值来完成的。如果存在这些值,则会在同一设备上的 sync-containers 子目录中创建指向容器数据库的符号链接。

同样,当容器同步元数据键被删除时,容器服务器和容器复制器将负责从 sync-containers 中删除符号链接。

注意

swift-container-sync 进程在集群中的每个容器服务器上运行,并与远程集群中的代理服务器(或负载均衡器)通信。因此,必须允许容器服务器启动到远程代理服务器(或负载均衡器)的出站连接。

实际的同步过程稍微复杂一些,以便利用容器的三个(或副本数)主节点,而无需每个节点都尝试执行完全相同的工作,但也不要遗漏任何工作,以防其中一个节点发生故障。

在每个容器数据库中保留两个同步点。同步容器时,容器同步进程会确定它拥有容器的哪个副本。在标准的 3 副本场景中,该进程将拥有副本编号 0、1 或 2。这用于确定哪些行属于此同步进程,哪些行不属于。

一个例子可能有所帮助。假设副本数为 3,数据库行 ID 为 1..6。此外,假设这是容器同步首次在此容器上运行,因此 SP1 = SP2 = -1。

SP1
SP2
 |
 v
-1 0 1 2 3 4 5 6

首先,容器同步进程查找 ID 在 SP1 和 SP2 之间的行。由于这是首次运行,SP1 = SP2 = -1,因此没有这样的行。

SP1
SP2
 |
 v
-1 0 1 2 3 4 5 6

其次,容器同步进程查找 ID 大于 SP1 的行,并同步它拥有的那些行。所有权基于对象名称的哈希值,因此不一定保证每三个行中正好有一个,但通常会非常接近。为了举例说明,假设此进程最终拥有行 2 和 5。

一旦完成尝试同步这些行,它会将 SP1 更新为它看到的最大行 ID,在本例中为 6。

SP2           SP1
 |             |
 v             v
-1 0 1 2 3 4 5 6

与此同时,客户端将新的对象上传到容器,在数据库中创建新的行。

SP2           SP1
 |             |
 v             v
-1 0 1 2 3 4 5 6 7 8 9 10 11 12

在下一次运行中,容器同步首先查看 ID 在 SP1 和 SP2 之间的行。这一次,有很多这样的行。同步进程尝试同步所有这些行。如果成功,它会将 SP2 设置为等于 SP1。如果失败,它会将 SP2 设置为失败的对象,并将继续尝试所有其他对象直到 SP1,将 SP2 设置为第一个失败的对象。

在正常情况下,容器同步进程已经处理了 SP1 和 SP2 之间的所有行的同步,从而进行了一系列快速检查。但是,如果由于某种原因,同步进程失败,那么这将是一个重要的回退机制,以确保容器中的所有对象都得到同步。如果没有这项看似冗余的工作,任何容器同步失败都将导致对象不同步。请注意,容器同步将持续重试同步任何有故障的对象,直到成功,同时记录每次失败。

完成回退行后,假设没有发生故障,SP2 将被推进到 SP1。

              SP2
              SP1
               |
               v
-1 0 1 2 3 4 5 6 7 8 9 10 11 12

然后,同步 ID 大于 SP1 的行(前提是此容器同步进程负责它们),并将 SP1 移动到看到的最大行 ID。

              SP2            SP1
               |              |
               v              v
-1 0 1 2 3 4 5 6 7 8 9 10 11 12