存储策略¶
存储策略允许通过创建多个对象环来对集群进行一定程度的划分,以满足各种目的。存储策略功能贯穿整个代码库,因此是理解 Swift 架构的一个重要概念。
如 环 中所述,Swift 使用修改后的哈希环来确定数据应驻留在集群中的位置。有单独的环用于帐户数据库、容器数据库,并且每个存储策略都有一个对象环。每个对象环的行为完全相同,并以相同的方式维护,但通过策略,不同的设备可以属于不同的环。通过支持多个对象环,Swift 允许应用程序和/或部署者有效地在单个集群内隔离对象存储。这样做有很多原因
不同的持久性级别:如果提供商希望提供例如 2x 复制和 3x 复制,但不想维护 2 个独立的集群,他们将设置 2x 和 3x 复制策略,并将节点分配到各自的环。此外,如果提供商希望提供冷存储层,他们可以创建一个基于擦除编码的策略。
性能:就像 SSD 可以用作帐户或数据库环的唯一成员一样,也可以创建一个仅包含 SSD 的对象环,并用于实现低延迟/高性能策略。
将节点收集到组中:不同的对象环可能具有不同的物理服务器,以便特定存储策略中的对象始终放置在特定的数据中心或地理位置。
不同的存储实现:另一个例子是将使用不同 Diskfile(例如,Kinetic、GlusterFS)的一组节点收集在一起,并使用策略将流量仅导向这些节点。
不同的读写亲和设置:可以配置代理服务器,为每个策略使用不同的读写亲和选项。有关详细信息,请参阅 每个策略的配置。
注意
目前,Swift 支持两种不同的策略类型:复制和擦除编码。有关详细信息,请参阅 擦除编码支持。
另请注意,Diskfile 指的是后端对象存储插件架构。有关详细信息,请参阅 可插拔磁盘后端 API。
容器和策略¶
策略在容器级别实现。这种方法有很多优点,其中最重要的是它为想要利用它们的应用程序带来了便利。它还确保存储策略仍然是 Swift 的核心功能,独立于身份验证实现。策略未在帐户/身份验证层实现,因为这将需要更改 Swift 部署者使用的所有身份验证系统。每个容器都有一个新的特殊不变元数据元素,称为存储策略索引。请注意,Swift 内部依赖于策略索引,而不是策略名称。策略名称是为了便于人类阅读,并且翻译由代理管理。创建容器时,支持一个新的可选标头来指定策略名称。如果未指定名称,则使用默认策略(如果未定义其他策略,则认为 Policy-0 是默认策略)。我们将在下一节中介绍默认策略和 Policy-0 之间的区别。
策略在创建容器时分配。一旦容器被分配了策略,就无法更改它(除非被删除/重新创建)。对于大型数据集,数据放置/移动的影响使得这成为应用程序执行的最佳任务。因此,如果容器具有现有的 3x 复制策略,并且想要将该数据迁移到擦除编码策略,应用程序将创建一个指定其他策略参数的另一个容器,然后只需将数据从一个容器移动到另一个容器。策略按每个容器的基础应用,从而最大限度地减少了应用程序的感知;一旦容器使用特定策略创建,其中存储的所有对象都将按照该策略进行存储。如果删除具有特定名称的容器(要求容器为空),则可以创建一个具有相同名称的新容器,而先前共享相同名称的已删除容器不会对存储策略施加任何限制。
容器与策略之间存在多对一的关系,这意味着任何数量的容器可以共享一个策略。使用特定策略的容器数量没有限制。
将环与容器关联的概念引入了一个有趣的情景:如果两个同名的容器在网络中断的同时在网络的相对两侧创建会发生什么?此外,如果对象被放置在这些容器中,大量的对象,然后网络中断恢复会发生什么?嗯,如果没有特别的注意,这将是一个大问题,因为应用程序可能会使用错误的环来尝试查找对象。幸运的是,这个问题有一个解决方案,一个名为容器协调器的守护进程不知疲倦地工作,以识别和纠正这种潜在的情况。
容器协调器¶
由于无法在分布式最终一致性系统中强制执行容器创建的原子性,因此必须通过异步守护进程最终将写入错误存储策略的对象合并到正确的存储策略中。由 swift-container-reconciler 守护进程处理网络分区期间的对象写入,导致创建具有不同存储策略的分割大脑容器的情况。
容器协调器基于类似于对象过期器的队列工作。该队列在容器复制期间填充。将对象排队以供容器协调器评估从不被认为是不正确的,因为如果对象的放置没有问题,协调器将只是将其从队列中删除。容器协调器队列是对象实际位置的索引日志,用于发现容器的存储策略差异。
为了确定容器的正确存储策略,当容器的状态从已删除更改为重新创建时,必须更新容器_stat 表中的 status_changed_at 字段。此事务日志允许容器复制器在复制容器和处理 REPLICATE 请求时更新正确的存储策略。
由于每次对象写入都是一个单独的分布式事务,因此不可能确定给定容器数据库中整个事务日志相对于每个对象写入的存储策略的正确性。因此,容器数据库将始终记录对象写入,无论每个对象行上的存储策略如何。每个容器中的对象字节和计数统计信息都按存储策略跟踪,并使用正常的对象行合并语义进行协调。
对象行在复制期间使用正常的容器复制确保完全持久化。在容器复制器将其对象行推送到可用主节点后,任何放置错误的行都将批量加载到基于对象时间戳的容器中,位于 .misplaced_objects 系统帐户下。这些行最初写入本地节点上的一个暂存容器,并在复制通过结束时,.misplaced_objects 容器被复制到正确的主节点。
容器协调器按降序处理 .misplaced_objects 容器,并在成功协调表示的对象的行后清除其容器。容器协调器将始终使用直接容器 HEAD 请求(通过缓存加速)验证排队的对象的正确存储策略。
由于在规模上假设单个存储节点的故障是常见的,因此容器协调器将通过简单的法定多数取得进展。在故障和重新平衡的组合过程中,法定多数可能提供不完整的正确存储策略记录 - 因此,对象写入可能需要应用多次。由于存储节点和容器数据库不会处理小于或等于其现有记录的 X-Timestamp 的写入,因此在重新应用写入时,时间戳会略微增加。为了透明地将此增量应用于客户端,Swift 为内部使用添加了第二种时间向量。请参阅 Timestamp。
当协调器将对象写入应用到正确的存储策略时,它会清理不再适用于不正确的存储策略的写入,并从 .misplaced_objects 容器中删除这些行。在成功处理所有行后,它将休眠并定期检查是否有新排队的行在容器复制期间发现。
默认值与“Policy-0”¶
存储策略是一项多功能的特性,旨在以相同的灵活性支持新集群和现有集群。因此,我们引入了 Policy-0 概念,它与“默认”策略不同。如您将在我们开始配置策略时看到,每个策略都有一个名称和一个任意数量的别名(人性化、可配置),以及一个索引(或简单地说,策略编号)。Swift 保留索引 0,以映射到所有安装中存在的对象环(例如,/etc/swift/object.ring.gz)。您可以将此策略命名为任何您喜欢的名称,如果未定义任何策略,它将报告自身为 Policy-0,但是您无法更改索引,因为必须始终存在索引为 0 的策略。
另一个重要概念是默认策略,它可以是集群中的任何策略。默认策略是在发送不指定存储策略的容器创建请求时自动选择的策略。配置策略 描述了如何设置默认策略。与 Policy-0 的区别很微妙但非常重要。Policy-0 是 Swift 在访问不具有策略的预存储策略容器时使用的 - 在这种情况下,我们不会使用默认值,因为它可能与旧容器的策略不同。当未定义其他策略时,Swift 将始终选择 Policy-0 作为默认值。
换句话说,默认值意味着“如果没有指定其他内容,则使用此策略进行创建”,而 Policy-0 意味着“如果容器没有一个,则使用旧策略”,这实际上意味着使用 object.ring.gz 进行查找。
注意
使用基于存储策略的代码,不可能创建一个没有策略的容器。如果未提供任何内容,Swift 仍然会选择默认值并将其分配给容器。对于在引入存储策略之前创建的容器,将使用旧的 Policy-0。
弃用策略¶
有时不再需要某个策略;但是,简单地删除策略和关联的环对于现有数据来说会存在问题。为了确保集群中的资源不会被遗弃(留在磁盘上但无法访问),并向应用程序提供有关需要退役策略的适当消息,使用了弃用概念。配置策略 描述了如何弃用策略。
Swift 对弃用策略的行为如下
弃用的策略不会出现在 /info 中
允许对使用弃用策略创建的现有容器进行 PUT/GET/DELETE/POST/HEAD 操作
客户端可以通过 HEAD 操作访问预先存在的容器上的策略统计信息
客户端在尝试使用弃用策略创建新容器时会收到“400 Bad Request”错误
注意
策略不能同时被弃用和设置为默认值。如果弃用默认策略,则必须指定新的默认值。
您还可以使用弃用功能来推出新策略。如果您想在将新配置和环推广到所有节点之前测试新的存储策略,可以在最初将它们推出到所有节点时弃用该策略。被弃用将使其无效且无法使用。要测试它,您需要配置一个单代理实例(或一组仅在内部可访问的代理服务器),该实例已使用新的策略配置,但未标记为已弃用。创建具有新存储策略的容器后,任何有权使用该容器的客户端都可以添加和访问存储在该容器中的数据。满意后,您可以推出一个新的 swift.conf,该配置不将策略标记为已弃用,以推广到所有节点。
配置策略¶
注意
请参阅 将存储策略添加到现有的 SAIO,以获取将策略添加到 SAIO 设置的分步指南。
部署者必须充分理解配置策略的语义。配置策略是一个三步过程
编辑您的
/etc/swift/swift.conf文件以定义您的新策略。创建相应的策略对象环文件。
(可选)创建特定于策略的代理服务器配置设置。
定义策略¶
每个策略由 /etc/swift/swift.conf 文件中的一个部分定义。部分名称必须是 [storage-policy:<N>] 的形式,其中 <N> 是策略索引。策略索引按顺序排列没有理由,但执行以下规则
如果未声明索引为
0的策略,并且未定义其他策略,Swift 将创建一个索引为0的默认策略。策略索引必须是非负整数。
策略索引必须是唯一的。
警告
策略索引一旦创建并使用后,不应更改。更改策略索引可能会导致数据访问丢失。
每个策略部分包含以下选项
name = <policy_name>(必需)策略的主要名称。
策略名称不区分大小写。
策略名称只能包含字母、数字或破折号。
策略名称必须是唯一的。
策略名称可以更改。
名称
Policy-0只能用于索引为0的策略。为了避免与策略索引混淆,强烈建议策略名称不要是数字(例如“1”)。但是,为了向后兼容,支持数字名称。
aliases = <policy_name>[, <policy_name>, ...](可选)一个逗号分隔的策略别名列表。
默认值为一个空列表(即没有别名)。
所有别名名称必须遵循
name选项的规则。可以向列表中添加和删除别名。
如果主名称已更改,别名可以用于保留对旧主名称的支持。
default = [true|false](可选)如果
true,则当客户端未指定策略时,将使用此策略。默认值为
false。可以通过在所需的策略部分中设置
default = true随时更改默认策略。如果没有将任何策略声明为默认策略,也没有定义其他策略,则将索引为
0的策略设置为默认策略;否则,必须声明一个默认策略。
不能将已弃用的策略声明为默认策略。
有关更多信息,请参阅 默认策略与“Policy-0”。
deprecated = [true|false](可选)如果
true,则不能使用此策略创建新的容器。默认值为
false。可以通过将
deprecated选项添加到所需的策略部分来弃用任何策略。但是,已弃用的策略不能同时声明为默认策略。因此,由于始终必须有一个默认策略,因此也始终必须至少有一个未弃用的策略。有关更多信息,请参阅 弃用策略。
policy_type = [replication|erasure_coding](可选)选项
policy_type用于区分不同的策略类型。默认值为
replication。定义 EC 策略时,请使用值
erasure_coding。
diskfile_module = <entry point>(可选)选项
diskfile_module用于加载替代的后端对象存储插件架构。默认值为
egg:swift#replication.fs或egg:swift#erasure_coding.fs,具体取决于策略类型。方案和包名称是可选的,默认值为egg和swift。
EC 策略类型具有其他必需的选项。有关详细信息,请参阅 使用纠删码策略。
以下是配置良好的 swift.conf 文件的示例。有关使用此示例配置设置一体机的完整说明,请参阅 将存储策略添加到现有的 SAIO。
[swift-hash]
# random unique strings that can never change (DO NOT LOSE)
# Use only printable chars (python -c "import string; print(string.printable)")
swift_hash_path_prefix = changeme
swift_hash_path_suffix = changeme
[storage-policy:0]
name = gold
aliases = yellow, orange
policy_type = replication
default = yes
[storage-policy:1]
name = silver
policy_type = replication
diskfile_module = replication.fs
deprecated = yes
创建环¶
配置 swift.conf 以使用新策略后,必须创建一个新的环。环工具不了解策略名称,因此在使用新策略的环文件时,使用正确的策略索引至关重要。使用 swift-ring-builder 以与旧环相同的方式创建其他对象环,只是在生成器文件名中“object”一词之后附加了“-N”,其中“N”与 swift.conf 中使用的策略索引匹配。因此,要为策略索引 1 创建环
swift-ring-builder object-1.builder create 10 3 1
在添加设备、重新平衡等时,继续使用相同的命名约定。此命名约定也用于每个策略存储节点数据目录的模式。
注意
相同的驱动器确实可以用于多个策略,并且磁盘上如何管理这些策略的细节将在后面的章节中介绍,在设置一个之前了解这种配置的影响非常重要。确保这是你真正想要做的,在许多情况下会是,但在其他情况下可能不是。
代理服务器配置(可选)¶
使用策略¶
使用策略非常简单 - 仅在最初创建容器时才指定策略。没有其他 API 更改。创建容器无需任何特殊策略信息即可完成
curl -v -X PUT -H 'X-Auth-Token: <your auth token>' \
http://127.0.0.1:8080/v1/AUTH_test/myCont0
这将导致创建与策略名称“gold”关联的容器,假设我们使用的是上面的 swift.conf 示例。它将使用“gold”,因为它被指定为默认值。现在,当我们把对象放入这个容器时,它将被放置在属于策略“gold”的环中的节点上。
如果我们想显式说明我们想要策略“gold”,则命令只需要包含一个新的标头,如下所示
curl -v -X PUT -H 'X-Auth-Token: <your auth token>' \
-H 'X-Storage-Policy: gold' http://127.0.0.1:8080/v1/AUTH_test/myCont0
就是这样!应用程序不再需要指定策略名称。但是,有一些非法操作
如果指定了无效的(拼写错误、不存在)策略:400 Bad Request
如果你尝试通过 PUT 或 POST 更改策略:409 Conflict
如果你想查看集群中的存储使用情况,只需 HEAD 帐户,你将看到累积数字,如前所述,以及每个策略的统计信息。在下面的示例中,总共有 3 个对象,其中 2 个在策略“gold”中,1 个在策略“silver”中
curl -i -X HEAD -H 'X-Auth-Token: <your auth token>' \
http://127.0.0.1:8080/v1/AUTH_test
你的结果将包括(为了可读性删除了一些输出)
X-Account-Container-Count: 3
X-Account-Object-Count: 3
X-Account-Bytes-Used: 21
X-Storage-Policy-Gold-Object-Count: 2
X-Storage-Policy-Gold-Bytes-Used: 14
X-Storage-Policy-Silver-Object-Count: 1
X-Storage-Policy-Silver-Bytes-Used: 7
底层¶
现在我们已经解释了什么是策略以及如何配置/使用它们,让我们探索存储策略在底层级别如何适应。
解析和配置¶
模块 存储策略 负责解析 swift.conf 文件,验证输入,并通过类 StoragePolicyCollection 创建配置策略的全局集合。该集合由类 StoragePolicy 的策略组成。集合类包含方便的函数,用于按名称或索引获取策略、获取策略信息等。还有一个非常重要的函数,get_object_ring()。对象环是 StoragePolicy 类中的成员,并且直到调用 load_ring() 方法才会被实例化。代码库中的任何调用者,需要访问对象环,都必须使用 POLICIES 全局单例来访问 get_object_ring() 函数并提供策略索引,这将根据需要调用 load_ring();但是,在启动请求处理服务(如 代理服务器)时,环会主动加载,以防止配置错误导致运行时错误。全局实例在 Swift 启动时创建,并提供了一种为测试代码修补策略的机制。
Middleware¶
中间件可以通过 POLICIES 全局和导入 get_container_info() 来利用策略,以获取与容器关联的策略索引。从索引可以获取 POLICIES 单例来获取正确的环。例如,列出端点 使用描述的方式了解策略。另一个例子是 Recon,它将报告所有环的 md5 总和。
代理服务器¶
代理服务器 模块在存储策略中的作用基本上是确保使用正确的环。在策略出现之前,当 Application 类实例化时,会实例化一个对象环,并且可以通过测试代码通过 init 参数覆盖。但是,有了策略,就没有 init 参数,Application 类而是依赖于 POLICIES 全局单例来检索环,该环会在首次需要时实例化。因此,对象环不再是 Application 类的一个成员,而是一个访问器函数,get_object_ring(),它从 POLICIES 获取环。
通常,当任何在代理上运行的模块需要对象环时,它会首先从缓存的容器信息中获取策略索引。例外情况是在创建容器期间,它使用请求标头中的策略名称来查找 POLICIES 全局中的策略索引。一旦代理确定了策略索引,它就可以使用前面描述的 get_object_ring() 方法来访问正确的环。然后,它有责任通过标头 X -Backend-Storage-Policy-Index 将索引信息(而不是策略名称)传递给后端服务器。反之,代理也会从返回给客户端的标头中删除索引,并确保它们只看到友好的策略名称。
磁盘上的存储¶
每个策略在后端服务器上都有自己的目录,并由其存储策略索引标识。通过策略索引组织后端目录结构有助于跟踪事物,并允许在策略之间共享磁盘,这可能对提供商来说有意义,也可能没有意义。稍后会对此进行更多介绍,但现在请注意以下目录命名约定
/objects映射到与 Policy-0 关联的对象/objects-N映射到存储策略索引 #N/async_pending映射到 Policy-0 的异步待处理更新/async_pending-N映射到存储策略索引 #N 的异步待处理更新/tmp映射到 Policy-0 的 DiskFile 临时目录/tmp-N映射到策略索引 #N 的 DiskFile 临时目录/quarantined/objects映射到 Policy-0 的隔离目录/quarantined/objects-N映射到策略索引 #N 的隔离目录
请注意,这些目录名称实际上由特定的 Diskfile 实现拥有,上面显示的名称由默认 Diskfile 使用。
对象服务器¶
对象服务器 不直接参与选择存储策略放置。但是,由于后端目录结构是按策略设置的,如前所述,对象服务器模块确实发挥作用。当对象服务器获取 Diskfile 时,它会传递策略索引,并让实际的目录命名/结构机制留给 Diskfile。通过传递索引,正在使用的 Diskfile 实例将确保数据根据其策略正确地位于树中。
出于同样的原因,对象更新器 也了解策略。如前所述,不同的策略使用不同的异步待处理目录,因此更新器需要知道如何扫描它们。
对象复制器 了解策略,因为根据策略,它可能需要做截然不同的事情,或者可能不需要。例如,2x 与 3x 的复制处理差异微不足道;但是,3x 与纠删码之间的复制处理差异绝对不是。事实上,术语“复制”对于某些策略(如纠删码)来说并不合适;但是,收集和处理作业的框架大部分是通用的。因此,这些函数在复制器中被利用于所有策略,然后根据需要添加策略特定的代码,并在定义策略时添加。
ssync 功能同样感知策略,原因相同。其他一些模块可能不会明显受到影响,但由 Diskfile 拥有的后端目录结构需要策略索引参数。因此,ssync 感知策略实际上意味着传递策略索引。有关 ssync 的更多信息,请参阅 ssync_sender 和 ssync_receiver。
对于 Diskfile 本身,感知策略完全在于使用提供的策略索引来管理后端结构。换句话说,获取 Diskfile 实例的调用者提供一个策略索引,而 Diskfile 的工作是通过此索引(以它选择的任何方式)来分隔数据,以便策略可以共享相同的媒体/节点(如果需要)。包含的 Diskfile 实现布置了前面描述的目录结构,但这归属于 Diskfile;外部模块无法访问该细节。提供了一个通用函数,用于根据其策略索引映射各种目录名称和/或字符串。例如,Diskfile 定义了 get_data_dir(),它基于通用的 get_policy_string() 来一致地构建用于各种用途的感知策略字符串。
容器服务器¶
容器服务器 容器服务器 在存储策略中扮演着非常重要的角色,它负责处理将策略分配给容器以及防止不良情况,例如更改策略或在未指定任何内容时选择错误的策略(回想一下之前关于 Policy-0 与默认值的讨论)。
容器更新器 容器更新器 感知策略,但它的工作非常简单,就是通过请求头将策略索引传递给 帐户服务器。
容器后端 容器后端 负责更改现有的数据库模式以及确保创建具有支持存储策略的模式的新数据库。“按需”迁移容器模式允许 Swift 在不中断服务的情况下升级(sqlite 的 alter 语句无论行数多少都很快)。为了支持滚动升级(和降级),对 container_stat 表的不兼容模式更改被应用到 container_info 表,并且 container_stat 表被替换为一个包含 INSTEAD OF UPDATE 触发器的视图,使其表现得像旧表。
策略索引存储在这里,用于报告有关容器的信息以及管理由 split-brain 场景引起的容器及其存储策略之间的差异。此外,在 split-brain 期间,容器必须准备好跟踪来自多个策略的对象更新,因此对象表也包含一个 storage_policy_index 列。使用 INSERT 和 DELETE 触发器(类似于先前更新 container_stat 的触发器)在 policy_stat 表中更新每个策略的对象计数和字节数。
容器复制器 容器复制器 守护程序将在其正常的一致性检查过程中,作为其正常一致性检查过程的一部分,主动迁移遗留模式,并在 container_info 表中的 reconciler_sync_point 条目更新时进行迁移。这确保了读取量大的容器,即使没有遇到任何写入,仍然会被迁移以完全兼容后存储策略查询,而无需回退并重试使用遗留模式的查询来服务容器读取请求。
容器同步 容器同步 功能只需要感知策略,因为它访问对象环。因此,它需要从容器信息中提取策略索引,并使用它从 POLICIES 全局中选择适当的对象环。
帐户服务器¶
帐户服务器 帐户服务器 在存储策略中的作用确实仅限于报告。当对帐户发出 HEAD 请求时(请参阅前面提供的示例),帐户服务器会收到存储策略索引,并为客户端构建基于策略的 object_count 和 byte_count 信息。
由于一些策略特定的数据库模式更改,帐户服务器能够报告每个存储策略的对象和字节计数。一个策略特定的表,policy_stat,以与 account_stat 表相同的方式,以每个策略为基础维护信息(每个策略一行)。account_stat 表仍然具有相同的用途,并且不会被 policy_stat 替换,它保存总帐户统计信息,而 policy_stat 仅具有细分。后端还负责通过更改数据库模式并填充 policy_stat 表中的 Policy-0 的当前 account_stat 数据来迁移存储策略之前的帐户。
每个存储策略的对象和字节计数不会在每次对象 PUT 和 DELETE 请求时更新,而是通过 swift-container-updater 异步执行到帐户服务器的容器更新。
升级和确认功能¶
升级到具有存储策略支持的 Swift 版本并不困难,事实上,集群管理员无需进行任何特殊配置更改即可开始使用。Swift 将自动开始将现有的对象环用作默认环和 Policy-0 环。声明策略 0 是完全可选的,如果不存在,则隐式策略 0 的名称将为“Policy-0”。假设为了测试目的,您想要获取已经具有大量数据的现有集群并升级到具有存储策略的 Swift。然后您想继续创建策略并测试一些事情。您需要做的全部就是
将您的所有 Swift 节点升级到感知策略的 Swift 版本
在
/etc/swift/swift.conf中定义您的策略创建相应的对象环
创建容器和对象,并确认它们的放置符合预期
有关逐步完成这些步骤的示例,请参阅 将存储策略添加到现有的 SAIO
注意
如果您从启用存储策略的 Swift 版本降级到不支持策略的旧版本,您将无法访问存储在策略 0 以外的策略中的任何数据,但这些对象将出现在容器列表中(如果存在网络分区和未调和的对象,则可能作为重复项)。在启用其他存储策略之前,在升级的部署上执行必要的集成测试非常重要,以确保客户端获得一致的 API 体验。不要降级到不支持存储策略的 Swift 版本,一旦您公开了多个存储策略。