本站文章大部分为作者原创,非商业用途转载无需作者授权,但务必在文章标题下面注明作者 刘世民(Sammy Liu)以及可点击的本博客地址超级链接 http://www.cnblogs.com/sammyliu/ ,谢谢合作

理解 OpenStack Swift (2):架构、原理及功能 [Architecture, Implementation and Features]

本系列文章着重学习和研究OpenStack Swift,包括环境搭建、原理、架构、监控和性能等。

(1)OpenStack + 三节点Swift 集群+ HAProxy + UCARP 安装和配置 

(2)原理、架构和性能

(3)监控

1. 架构

1.1 总体架构

Swift 的总体架构非常的清晰和独立:

# 分层(Tier) 组件(Service) 功能(Function) 特性 部署考量
1 访问层(Access Tier) Load Balancer 硬件(比如F5)或者软件(比如HAProxy)负载均衡器,将客户端的请求按照配置的策略分配到无状态的 proxy service。    按照实际需求选择硬件还是软件LB
2 Proxy Server
  • 提供 REST API 给客户端
  • 无状态的 WSGI 服务,由多个 Proxy Server 组成一个集群
  • 将客户端请求转给某个存储节点上的 Account,Container 或者 Object 服务

是 CPU 和网络带宽敏感的

  • 分配更多的CPU和网络带宽,比如 10GbE 网络
  • 使用 Memcached 来做 token、account 和 container 数据缓存
  • 和存储服务分开部署
  • 使用负载均衡器
  • 往往每五个存储节点使用两个 proxy server 节点,然后再按比例扩展
  • 将 public network (用于客户端访问)和 backend network (用于访问存储服务)分开
  • 需要考虑安全性,可以将 SSL Termination 放在 Proxy Server 上
3 存储层 (Capactity Tier)

Account Server

提供 Account 操作 REST API 是磁盘性能和网络带敏感的
  • 对 Account 和 Container 服务使用更好的磁盘,比如 SAS 或者 SSD,来提高性能;
  • 使用 1GbE 或者 10GbE 网络
  • 需要的话,可以将 replicaiton network 独立出来
  • 需要的话,可以将 Account 和 Container 服务部署到独立的服务器上
4 Container Server 提供 Container 操作 REST API
5 Object Server 提供 Object 操作 REST API
 6 Consistency Servers 包括 Replicators, Updaters 和 Auditors 等后台服务,用于保证 object 的一致性

这是一张比较经典的 Swift 物理部署图:

1.2 网络架构

以一个对外提供对象存储服务的集群为例,其网络架构可以为:

  • 外部流量被放在一个单独的(上图中紫色)VLAN 中,终点为 LB
  • 控制(管理)网络连接所有节点
  • Swift 前端(front end / public)网络连接 LB 和 所有 Proxy server 节点
  • Swift 后端 (backend / private) 网络连接所有 Proxy server 节点和 存储节点
  • 需要的话,还可以从后端网络中分离出复制(replication)网络

在网络带宽选择上,

  • 考虑到复制数据的容量较大(往往是几个TB起步),后端网络往往是用 10GbE 网络
  • 根据前端负载,前端网络可以使用 1GbE 网络,或者有条件时使用 10GbE 网络
  • 管理/IPMI网络往往是用 1GbE 网络

这是 SwiftStack 的一个例子:

2. 数据存放

2.1 Swift 的数据存放

2.1.1 Swift 的数据模型

Swift 的数据模型使用了以下三个概念来(见下图1):

  • Account: 账户/租户。Swift 是天生支持多租户的。如果使用 OpenStack Keystone 做用户校验的话,account 与 OpenStack project/tenant 的概念相同。Swift 租户的隔离性体现在metadata上,而不是体现在 object data 上。数据包括自身元数据 和 container 列表,被保存在 SQLite 数据库中。
  • Container: 容器,类似于文件系统中的目录,由用户自定义,它包含自身的元数据和容器内的对象列表。数据保存在 SQLite 数据库中。在新版中,Swift 支持在容器内添加文件夹。
  • Object: 对象,包括数据和数据的元数据,以文件形式保存在文件系统上。

     

 (图1)                                                     (图2)

 

  • Containers 是用户创建的,用来 hold objects。
  • objects 可以是 0 bytes 长度,或者包含数据。
  • container 中的 object 最大大小为 5GB;超过的话,会做特殊处理
  • 每个 object 使用它的 name 来被 referenced;Swift 没有目录概念
  • 在 object name 中可以使用任意的可以被 ‘URL-encoded’ 的 字符,最大长度为 URL - coded 之后 1034 个字符
  • object name 中可以带 '/' 字符,它会带来目录结构的幻觉(illusion),比如 dir/dir2/name。即使看起来象个目录,但是它仍然只是一个 object name。此时,不需要 dir 或者 dir/dir2 container 的存在。
  • 如果一个 container 所有 objects 的大小为0,那么它将看起来象一个目录。
  • 客户端使用 HTTP 或者 HTTPS 访问 Swift,包括读、写、删除 objects。也支持 COPY 操作,它会创建一个新的 object,使用一个新的 object name,包含老 object 的 data。没有 rename 操作,它会首先 copy 出一个新的,然后再将老的删除。

2.1.2 选择数据存放位置

  Swift 保存每个对象为多分拷贝,它按照物理位置的特点,尽量将这些拷贝放在不同的物理位置上,来保证数据的地理位置上的可靠性。它主要考虑以下几种位置属性:

  • Region:地理位置上的区域,比如不同城市甚至不同国家的机房,这主要是从灾备方面考虑的。
  • Zone:一个数据中心根据物理网络、供电、空调等基础设施分开的独立的域,往往将一个机架(Rack)内的服务器分在一个 Zone 内。
  • Node (节点):物理服务器
  • Disk (磁盘):物理服务器上的磁盘

Swift 在确定对象的放置位置时,会尽量将对象及其拷贝放在不会同时损失的物理位置上。见上图2.

2.1.3 保证数据一致性

    对象及其拷贝放置在某个磁盘上后,Swift 会使用Replicators, Updaters 和 Auditors 等后台服务来保证其数据的最终一致性。

  • Replicator – 拷贝对象,确保系统的最终一致性(Replicate objects and make a system in a consistent state);恢复磁盘和网络错误(Recover disk failure, network outages situation)
  • Updater – 更新元数据(Update metadata),从容器和账户元数据高负载导致的问题上恢复(Recover failure caused by container, account metadata high load)
  • Auditor – 删除问题账户,容器和对象,然后从别的服务器上拷贝过来(Delete problematic account, container or objects and replicate from other server);恢复数据库和文件数据错误(Recover dbs or files which have bit rot problem.

其中,Replicator 服务以可配置的间隔来周期性启动,默认是 30s,它以 replication 为最小单位,以 node 为范围,周期性地执行数据拷贝。详细过程请参考文末的参考文档。考虑到Swift 实现的是最终一致性而非强一致性,它不合适于需要数据强一致性的应用,比如银行存款和订票系统等。需要做 replication 的情形包括但不限于:

  • Proxy server 在写入第三份时失败,它依然会向客户端返回成功,后台服务会写第三份拷贝
  • 后台进程发现某个replication 数据出现损坏,它会在新的位置重新写入
  • 在跨 Region 的情况下,Proxy server 只会向它所在 Region 的存储上写入,远处 region 上的数据由后台进程复杂写入
  • 在更换磁盘或者添加磁盘的情况下,数据需要重新平衡时

2.2 Swift  是如何实现这些需求的:使用 Ring + 哈希算法

    Swift 根据由管理员配置的 Ring 使用相对简单直接的算法来确定对象的存放位置。对象会以文件的形式保存在本地文件系统中,使用文件的扩展属性来保存对象的元数据,因此,Swift 需要支持扩展属性的文件系统,目前官方推荐是 XFS。

2.2.1 Ring 的内容和算法

    简单来说,Swift 的 Proxy Server 根据account,container 和 object 各自的 Ring 来确定各自数据的存放位置,其中 account 和 container 数据库文件也是被当作对象来处理的。

因此,Swift 需要 Ring 的配置文件分布在所有 Proxy节点上。同时,Consistency Servers 需要它来确定后台对象拷贝的位置,因此也需要部署在所有存储节点上。Ring 以文件的形式保存:

  • object.ring.gz
  • container.ring.gz
  • account.ring.gz

在分析 Ring 是如何工作的之前,先来看看 Ring 的几个关键配置:

  • Region,zone 和 disk:前面说过了,略过
  • partition:Swift 再将每个磁盘分成若干 partition (分区)。这是后端一致性检查服务处理拷贝(replication)的基本单位。
  • Replica:对象和拷贝的总份数,常规推荐值是 3。

管理员使用 Swift 提供的 ring 生成工具(swift-ring-builder,位于源码的bin目录下,是swift最基本的命令,它与swift/common/ring/下的文件一起实现ring文件创建,添加,平衡,等操作),加上各种配置参数,得出该ring的内容。以 Object ring 为例,

root@swift1:/etc/swift# swift-ring-builder object.builder
object.builder, build version 6
1024 partitions, 3.000000 replicas, 1 regions, 3 zones, 6 devices, 0.00 balance, 0.00 dispersion
The minimum number of hours before a partition can be reassigned is 1
The overload factor is 0.00% (0.000000)
Devices:    id  region  zone      ip address  port  replication ip  replication port      name weight partitions balance meta
             0       1     1   9.115.251.235  6000   9.115.251.235              6000      sdb1 100.00        512    0.00
             1       1     1   9.115.251.235  6000   9.115.251.235              6000      sdc1 100.00        512    0.00
             2       1     2   9.115.251.234  6000   9.115.251.234              6000      sdb1 100.00        512    0.00
             3       1     2   9.115.251.234  6000   9.115.251.234              6000      sdc1 100.00        512    0.00
             4       1     3   9.115.251.233  6000   9.115.251.233              6000      sdb1 100.00        512    0.00
             5       1     3   9.115.251.233  6000   9.115.251.233              6000      sdc1 100.00        512    0.00

该 Ring 的配置为:1 个 region,3 个 zone,3 个 node,6 个磁盘,每个磁盘上 512 个分区。

内部实现上,Swift 将该 Ring 的配置保存在其 _replica2part2dev 数据结构中:

其读法是:

  • 行:将集群所有的分区都顺序编号,每个分区有唯一的一个ID
  • 列:包含 Ring 中的 id,该 id 唯一确定了一个 disk;和 replica 的编号。

因此,Swift 通过该数据结构可以方便地查到某个 replica 应该通过哪些节点上的存储服务放在哪个 disk 上。

除了生成 Ring 外,对 Ring 的另一个重要操作是 rebalance(再平衡)。在你修改builder文件后(例如增减设备),该操作会重新生成ring文件,使系统中的partition分布平衡。当然,在 rebalance 后,需要重新启动系统的各个服务。 详情可以参考 OpenStack Swift源码分析(二)ring文件的生成

2.2.2 数据放置和读取过程

当收到一个需要保存的 object 的 PUT 请求时,Proxy server 会:

  1. 根据其完整的对象路径(/account[/container[/object]])计算其哈希值,哈希值的长度取决于集群中分区的总数。
  2. 将哈希值的开头 N 个字符映射为数目同 replica 值的若干 partition ID。
  3. 根据 partition ID 确定某个数据服务的 IP 和 port。
  4. 依次尝试连接这些服务的端口。如果有一半的服务无法连接,则拒绝该请求。
  5. 尝试创建对象,存储服务会将对象以文件形式保存到某个磁盘上。(Object server 在完成文件存储后会异步地调用 container service 去更新container数据库)
  6. 在3份拷贝中有两份被成功写入后, Proxy Server 就会向客户端返回成功。

 

当 Proxy server 收到一个获取对象的 GET 请求时,它:

(1)(2)(3)(4)同前面的 PUT 请求,确定存放所有 replica 的 所有磁盘

(5)排序这些 nodes,尝试连接第一个,如果成功,则将二进制数据返回客户端;如果不成功,则尝试下一个。直到成功或者都失败。

应该说该过程蛮简单直接,这也符合Swift的总体设计风格。至于具体的哈希算法实现,有兴趣可以看相关论文。大致来说,它实现的是 “unique-as-possible” 即 “尽量唯一” 的算法,按照 Zone,Node 和 Disk 的顺序。对于一个 replica,Swift 首先会去选择一个没有该对象 replica 的 zone,如果没有这样的 zone,选择一个已使用 zone 中的没用过的 node,如果没有这样的 node,就选择已使用的 node 上的一个没使用过的 disk。

2.2.3 Hash 计算和对象位置查找

(1)存放的目录取决于哈希值

这里也表明,对象的存放目录和其名称是直接关联的。因此,在Swift中的,对对象重命名,意味着对象位置的修改,该过程会产生数据拷贝,而且在很多时候是需要跨节点的远程拷贝。在某些应用中,比如 Hadoop 大数据应用,如果采用 Swift 作为存储,在其mapreduce 过程中,是会产生文件 rename 操作的,这在 Swift 中会带来严重的性能下降。

(2)获取某对象的存放路径

root@swift1:~/s1# swift-get-nodes /etc/swift/object.ring.gz AUTH_dea8b51d28bf41599e63464828102759/container1/1

Account         AUTH_dea8b51d28bf41599e63464828102759
Container       container1
Object          1
Partition       277
Hash            456a95e2e66aad55d72756c6b0cd3b75

Server:Port Device      9.115.251.235:6000 sdb1
Server:Port Device      9.115.251.234:6000 sdc1
Server:Port Device      9.115.251.233:6000 sdc1

curl -I -XHEAD "http://9.115.251.235:6000/sdb1/277/AUTH_dea8b51d28bf41599e63464828102759/container1/1"
curl -I -XHEAD "http://9.115.251.234:6000/sdc1/277/AUTH_dea8b51d28bf41599e63464828102759/container1/1"
curl -I -XHEAD "http://9.115.251.233:6000/sdc1/277/AUTH_dea8b51d28bf41599e63464828102759/container1/1"

(3)远程登录后者 ssh 后可以看到保存对象的文件

root@swift1:~/s1# ls /srv/node/sdb1/objects/277/b75/456a95e2e66aad55d72756c6b0cd3b75 -l
total 8
-rw------- 1 swift swift 12 Nov  8 17:17 1447003035.84393.dataroot@swift1:~/s1# cat /srv/node/sdb1/objects/277/b75/456a95e2e66aad55d72756c6b0cd3b75/1447003035.84393.data
222222222222

2.2.4 对象分段

  Swift 对于小的文件,是不分段直接存放的;对于大的文件(大小阈值可以配置,默认是 5G),系统会自动将其分段存放。用户也可以指定分段的大小来存放文件。比如对于 590M 的文件,设置分段大小为 100M,则会被分为 6 段被并行的(in parallel)上传到集群之中:

root@controller:~/s1# swift upload container1 -S 100000000 tmpubuntu
tmpubuntu segment 5
tmpubuntu segment 2
tmpubuntu segment 4
tmpubuntu segment 1
tmpubuntu segment 3
tmpubuntu segment 0
tmpubuntu
root@controller:~/s1# swift list container1
1
admin-openrc.sh
cirros-0.3.4-x86_64-disk.raw
tmpubuntu

从 stat 中可以看出它使用一个 manifest 文件来保存分段信息:

root@controller:~/s1# swift stat container1 tmpubuntu
       Account: AUTH_dea8b51d28bf41599e63464828102759
     Container: container1
        Object: tmpubuntu
  Content Type: application/octet-stream
Content Length: 591396864
 Last Modified: Fri, 13 Nov 2015 18:31:53 GMT
          ETag: "fa561512dcd31b21841fbc9dbace118f"
      Manifest: container1_segments/tmpubuntu/1446907333.484258/591396864/100000000/
    Meta Mtime: 1446907333.484258
 Accept-Ranges: bytes
    Connection: keep-alive
   X-Timestamp: 1447439512.09744
    X-Trans-Id: txae548b4b35184c71aa396-0056462d72

但是 list 的时候依然只看到一个文件,原因是因为 manifest 文件被保存到一个独立的 container (container1_segments)中。这里可以看到 6 个对象:

root@controller:~/s1# swift list container1_segments
tmpubuntu/1446907333.484258/591396864/100000000/00000000
tmpubuntu/1446907333.484258/591396864/100000000/00000001
tmpubuntu/1446907333.484258/591396864/100000000/00000002
tmpubuntu/1446907333.484258/591396864/100000000/00000003
tmpubuntu/1446907333.484258/591396864/100000000/00000004
tmpubuntu/1446907333.484258/591396864/100000000/00000005

每个对象大小是100M(考虑到存储效率,不建议每个对象大小小于100M):

root@controller:~/s1# swift stat container1_segments tmpubuntu/1446907333.484258/591396864/100000000/00000000
       Account: AUTH_dea8b51d28bf41599e63464828102759
     Container: container1_segments
        Object: tmpubuntu/1446907333.484258/591396864/100000000/00000000
  Content Type: application/octet-stream
Content Length: 100000000

而且用户可以单独操作比如修改某一段。Swift 只会负责将所有段连接为用户所见的大的对象。

关于大文件支持的更多细节,可以参考 官方文档 和 Rackspace 的文档。从上面的描述可以看出,Swift 对文件分段的支持是比较初级的(固定,不灵活),因此,已经有人提出 Object stripping (对象条带化)方案,比如下面的方案,不知道是否已经支持还是将要支持。

2.3 Region

    通过将对象存放在不同物理位置上的 Region 内,可以进一步增强数据的可用性。其基本原则是:对于 N 份 replica 和 M 个 region,每个 region 中的 replica 数目为 N/M 的整数,剩余的 replica 在 M 个region 中随机选择。以 N = 3, M = 2 为例,一个 region 中有 1 个 replica,另一个 region 中有两个 replica,如下图所示:

对于一个 PUT 操作来说,Proxy server 只会将 replica 写入它所在的 region 中的 node,远端 region 中的 replica 由 replicator 写入。因此,Swift 的算法应该尽量保证 proxy server 所在的 region 中的 replica 份数相对多一些,这也称为 replica 的 proxy server 亲和性。

显然,跨 region 的数据复制加重了对网络带宽的要求。

两种形式的 Region:

(1)远端 region 实时写入 replica

(2)远端 region 的 replica 异步写入

2.4 Storage Polices (存储策略)

    上面的描述中,一个Swift 集群只支持一套 Ring 配置,这意味着整个机器的配置是唯一的。类似 Ceph 中 pool 的定义,Swift 在 2.0 版本(包含在 OpenStack Juno 版本中)中,添加了一个非常大的功能:Storage policy。在新的实现中,一个 Swift 可以由多套 Ring 配置,每套 Ring 的配置可以不相同。比如,Ring 1 保存 3 份对象拷贝,Ring 2 保存 2 份对象拷贝。几个特点:

  • Policy 被实现在 container 级别
  • 创建 container 时可以指定 policy。一旦被指定,不可以修改。
  • 一个 policy 可以被多个 container 共享使用

    通过应用该新的功能,Swift 用户可以制定不同的存储策略,来适应不同应用的存储需求。比如对关键应用的数据,制定一个存储策略使得数据被保存到 SSD 上;对于一般关键性的数据,指定存储策略使得数据只保存2份来节约磁盘空间。比如说:

详细信息,请参考 OpenStack 官方文档 和 SwiftStack 官方文档

3. 版本及主要功能

3.1 Juno以及之前主要版本和功能

 

(1)Large object support

(2)Static web hosting

(3) S3 compatible API

(4) Object expiration

(5) Temp url

(6) Global cluster

(7) Storage policy

3.2 Kilo 版本中的更新

新功能

纠删码(beta)

Swift现在支持纠删码(EC)存储策略类型。这样部署人员、以极少的RAW容量达到极高的可用性,如同在副本存储中一样。然而,EC需要更多的CPU和网络资源,所以并不适合所有应用场景。EC非常适合在一个独立的区域内极少访问的、大容量数据。

Swift纠删码的实现对于用户是透明的。对于副本存储和纠删码存储的类型,在API上没有任何区别。

为了支持纠删码,Swift现在需要依赖PyECLib和liberasurecode。liberasurecode是一个可插件式的库,允许在你选择的库中实现EC算法。

更详细文档请参阅 http://swift.openstack.org/overview_erasure_code.html

复合型令牌(Composite tokens)

复合型令牌允许其他OpenStack服务以客户端名义将数据存储于Swift中,所以无论是客户端还是服务在更新数据时,都不需要双方彼此的授权。

一个典型的例子就是一个用户请求Nova存放一个VM的快照。Nova将请求传递给Glance,Glance将镜像写入Swift容器中的一组对象中。在这种场景下,用户没有来自服务的合法令牌时,无法直接修改快照数据。同样,服务自身也无法在没有用户合法令牌的情况下更新数据。但是数据的确存在于用户的Swift账户中,这样使得账户管理更简单。

更详细的文档请参阅http://swift.openstack.org/overview_backing_store.html

更小规模、不平衡集群的数据位置更新

Swift数据的存放位置现在根据硬件权重决定。当前,允许运维人员逐渐的添加新的区域(zones)和地域(regions),而不需要立即触发大规模数据迁移。同时,如果一个集群是非平衡的(例如,在一个区域(zones)的集群中,其中一个的容量是另外一的两倍),Swift会更有效的使用现有空间并且当副本在集群空间不足时发出警告。

全局性集群复制优化

区域(regions)之间复制时,每次复制只迁移一个副本。这样远程的区域(region)可以在内部复制,避免更多的数据在广域网(WAN)拷贝。

已知问题

  • 作为beta更新,纠删码(EC)的功能接近完成,但是对于某些功能仍然不完整(像多范围(multi-range)读取),并且没有一个完整的性能测算。这个功能为了持久性依赖于ssync。部署人员督促我们做更大规模的测试,并且不要在生产环境部署中使用纠删码存储策略。

升级提示

像往常一样,你能在不影响最终用户体验的前提下,升级到这个版本的Swift。

  • 为了支持纠删码,Swift需要一个新的依赖PyECLib(和liberasurecode等)。并且eventlet的最低版本要求也升高了。

3.3 Liberty 版本中的更新

L版本中Swift 没有加入大的新功能,详细情况可以参考 官方文档

3.4 优势

 

 

其它参考文档:

http://www.florentflament.com/blog/openstack-swift-ring-made-understandable.html

https://www.mirantis.com/blog/configuring-multi-region-cluster-openstack-swift/

OpenStack Swift源码分析

 

posted on 2015-11-16 08:56  SammyLiu  阅读(14824)  评论(1编辑  收藏  举报