1. 简介
Swift 前身是 Rackspace Cloud Files 项目, 有 Rackspace 于 2010 年贡献给 OpenStack, 当时与 Nova 一起作为 OpenStack 最初仅有的两个项目.
作为对象存储的一种,Swift 比较适合存放静态数据.所谓静态数据是指长期不会发生更新的数据,或者在一定使其内更新频率比较低的数据.例如,虚拟机的镜像、多媒体数据及数据的被封.如果需要实时的更新数据,那么 Swift 并不是一个特别好的选择,在这种情况下, Cinder 块存储更为合适.
若下图所示,Swift 提供了 RESTful API 作为访问的入口,存储的每个对象都是一个 RESTful 资源,拥有一个唯一的 URL,我们可以发送 HTTP 请求将一些数据作为一个对象还给 Swift,也可以从 Swift 中请求一个之前存储的对象,至于该对象是以何种形式存储的且存储于和中设备的什么位置,我们并不需要去关心.
- Swift 体系结构
如下图所示,Swift 架构可以划分为两个层次:访问层(Access Tier) 与存储层(Storage Nodes).
访问层的更能类似于网络设备中的 Hub, 主要包括两个部分:代理服务器节点(Proxy Node)与认证(Authentication).分别负责 RESTful 请求的处理与身份的认证.在 Proxy Node 上运行的 Proxy Server 用来负责处理用户的 RESTful 请求,在接受到用户请求时,需要对用户的神风进行认证,此时用户所提供的身份资料会被转发到认证服务进行处理. Proxy Server 可以使用 Memcached进行数据和对象的缓存,来减少书库读取的次数,提高用户的访问速度.
存储层有一系列的物理存储节点组成,负责对象数据的存储,Proxy Node 在接受到用户的访问请求时,会将其转发到相应的存储节点上.为了在系统出现问题的情况下有效地将故障隔离在最小的物理范围内,存储层在物理上又分为如下一些层次:
- Region: 地理上隔绝的取余,也就是说不同的 Region 通常在地理位置上被隔绝开来.例如,两个数据中心可以划分为两个 Region.每个 Swift 系统默认至少有一个 Region.
- Zone: 在每个 Region 的内部又划分了不同的 Zone 来实现硬件上的隔绝.一个 Zone 可以是一个硬盘、一台主机、一个机柜或一个交换机,我们可以简单理解为一个 Zone 代表了一组独立的存储节点.
- Storage Node:存储对象数据的物理节点,基于通用标准的硬件设备提供了不低于专业存储设备水平的对象存储服务.
- Device: 可以简单理解为磁盘
- Partition: 这里的 Partition 仅仅是指在 Device 上的文件系统中的目录,和我们通常所理解的硬盘分区是完全不同的概念.
2.1. Swift 对象组织结构
每个 Storage Node 上存储的对象在逻辑上又由3 个层次组成:Account、Container 及 Object.Swift 对象组织结构如下图所示
这里每一层包含的节点数都没有限制,Account 在对象的存储过程实现顶层的隔离,代表的并不是个人账户,而是租户,一个 Account 可以被多个个人账户共同使用;Container 代表了 一组对象的封装,类似文件夹或目录,但是 Container 不能嵌套;Object 视具体的对象,由元数据和内容两部分组成.Swift 要求一个对象必须存储在某个 Container 中,因此一个 Account 至少有一个 Container 来提供对象的存储.
与上述的 3 层组织结构相对应,Storage Node 上运行了 3 中服务.
- Account Server:提供 Account 相关服务,包括 Container 列表及 Account 的元数据等.Account 的信息被存储在一个 SQLite 数据库中.
- Container Server: 提供 Container 相关服务,包括 Ojbect 的列表及 Container 的元数据等.与 Account 一样,Container 的信息也被存储在一个 SQLite 数据库中.
- Object Server: 提供对象的存取和元数据服务,每个对象的内容会以二进制文件的形式存储在文件系统中,元数据会作为文件的扩展属性来存储,也就是说,存储对象的物理节点上,本地文件系统必须支持文件的扩展属性,有些文件系统(如ext3)的文件扩展属性默认是关闭的.
2.2. 冗余策略
为了保证数据在某个存储硬件损坏的情况下也不会丢失,Swift 为每个对象建立了一定数量的副本(默认为 3),并且每个副本存放在不同的 Zone 中,这样即使某个 Zone 发生故障,Swift 仍然可以通过其他 Zone 继续提供服务.
在 Swift 中,副本是以 Partition 为单位的,也就是说,对象的副本其实是通过 Partition 的副本来实现的(类似于 Ceph 中的 PG,可以减少系统复杂度).
2.3 副本一致性
对象保存了多份就需要着重考虑系统的一致性,当对象内容更新的时候副本也必须同时得到更新或总能读到最新的数据.而且其中一份损坏时必须能迅速复制一份来完整替换.
Swift 通过以下 3 种服务来解决数据一致性的问题.
- Auditor: 通过持续扫描磁盘来检查 Account、Container 和 Object 的完整性,如果返现数据有损坏的情况,Auditor 就会对文件进行隔离,然后通过 Replicator 从其他节点上获取对应的副本用已恢复本地数据.
- Updater: 在创建一个 Container 的时候需要对包含该 Container的 Account 的信息进行更新,使得该 Account 数据库里面的 Container 列表包含这个新创建的 Container.同样在创建一个新的 Object 的时候,需要对包含该 Object 的 Container 的信息进行更新,使得该 Container 的数据里面的 Object 列表包含这个新创建的 Object.但是当 Account Server 或 Container Server 繁忙的时候,这样的更新操作并不是在每个节点上都能成功完成.对于那些没有成功更新的操作,Swift 会使用 Updater 服务继续处理这些失败的更新操作.
- Replicator: 负责检测各个节点上的数据及副本是否一致.当发现不一致时会将过时的副本更新为最新版本,并且负责将标记为删除的数据真正从物理介质上删除.
2.4 物理位置选择 - 环
Swift 引入环的概念来解决如何将对象与真正的物理存储位置相互映射的问题.环记录了存储对象与物理位置之间的映射关系,Account、Container 和 Object 都有自己独立的环,当 Proxy Server 接收到用户请求时,会根据所操作的实体(Account、Container 和 Object)寻找对应的环,来确定他们在存储服务器集群中的具体位置,Account、Container 和 Object 在环中的位置信息,也由 Proxy Server 来进行维护.
环通过 Zone、Device、Partition 和 Replica 的概念来维护映射信息.每个 Partition 的位置由环来维护,并存储在映射中.环需要在 Swift 部署时使用"swift-ring-builder"工具手动进行构建,之后每次增减存储节点时,都需要重新平衡环文件中的项目,以保证系统因此而发生前已的文件数量最少.
2.5 Swift 架构
如下图所示, Proxy Srever 是运行在 Proxy Node上的 WSGI Server,Account Server、Container Server 与 Object Server 是运行在存储节点上的 WSGI Server,Proxy Server 收到用户 HTTP 请求后,将请求路由到响应的 Controller(Account Controller、Container Controller 与 Object Controller),Controller 会从对应的环文件中获取到请求数据所在的存储节点,然后将这个请求转发给该节点上的 WSGI Server (Account Server、Container Server 与 Object Server)
3. 环
Swift 通过引入环来实现对物理节点的管理,包括记录对象与物理存储位置间的映射,物理节点的添加和删除等.为了解决普通 hash 算法不能动态添加删除节点的问题,Swift 也是用一致性 hash 算法来构建环(Ceph 的 CRUSH 和一致性 hash 的思想是相同).
3.1 一致性 hash 算法
在其他文章我们介绍了一致性 hash--一致性 hash 算法及 golang 实现,需要熟悉一致性 hash 算法才能继续学习本章内容
3.2 环数据结构
Swift 中所谓的环就是基于一致性 hash 算法构造的环,环包括一下 3 中重要的数据结构:
- 设备表: Swift 将所有 Device 进行编号,设备表中的每一项都对应一个 Device,其中记录了该 Device 的具体位置信息,包括 Device ID,所在的 Region、Zone、IP 地址及端口号,以及用户为该 Device 定义的权重等.
- 当 Device 的容量大小不一致时,可以通过权重保证 Partition 均匀分布,容量较大的 Device 拥有更大的权重,也能容量那更多的 Partition.例如,一个 1TB 大小的 Device 有 100 的权重而一个 2TB 大小的 Device 将有 200 的权重.
- 设备查询表(Device Lookup Table):存储 Partition 的各个副本(默认为 3)与具体 Device 的映射信息.设备查询表中的每 1 列对应 1 个 Partition,每 1 行对应 Partition 的 1 个副本,每个表格中的信息为设备表中 Device 的编号,根据这个编号,可以在设备表中检索到该 Device 的具体信息(Device ID、IP 地址及端口号等信息)
- Partition 移位值:表示在 hash 之后将 Object 名字进行二进制位移的位数.
为了减少由于增加、减少借点带来的数据迁移量,Swift 在对象存储节点的映射之间增加了 Partition 的概念,使对象到存储节点的映射 过程,变成了由对象到Partition再到存储节点的映射过程。Partition的个数一旦确认,在整个运行过程中是不会改变的,所以对象到Partition的 映射是不会发生变化的。在增加或减少节点的情况下,通过改变 Partition到存储节点的映射过程来完成数据的迁移。
而对象到Partition这层映射是通过哈希算法及二进制位移操作的, Partition到存储节点的映射是通过设备查询表完成的。
总结
本文是对 Swift 的简单介绍,主要介绍了 Swift 的体系架构和环的概念.Swift 是 OpenStack 的对象存储项目,可以安全高效的存储对象数据,并且整个系统可以无限的扩展,系统会自动的达到平衡状态,Swift 提供了类似 Amazon S3 的服务,可以作为网盘类产品的存储引 擎,也非常适合用于存储日志文件和作为数据备份仓库,在OpenStack 中,它可以与镜像服务Glance相结合,为其存储镜像文件。