在前阶段的工作中,对于OpenStack整体部署已经日渐熟练。可以较快速地在多台节点环境下,安装部署小型云环境,并测试使用。
通过该阶段的学习,对于虚拟实例的使用,归纳了很多心得,对于实例的存储,由于理论繁复,特此用一篇文档记录相关体会。感谢学习过程中很多大牛的文档,来自sina的帮助和yz的指导!
在OpenStack体系内,实例的使用过程中的数据是不会写入img镜像文件的。即镜像文件做好之后,不通过虚拟机写入修改,单纯通过OpenStack调用是不会产生改变的。这种机制很好保护了镜像文件的完整性,利于面向多用户的使用工程。但对于用户而言,亟需一种存储机制,在不破坏镜像的前提下,可以长久保存用户数据,利于复用。
基于如上需求,OpenStack推出了nova-volume卷组存储和Swift存储两种可选方式,下面通过此篇文档向大家阐述二者的相关原理和部署方法。
2. Swift组件
OpenStack项目中,使用Swift组件,实现对象存储功能。在官方文档中对Swift的释义为“非文件系统或实时的数据存储系统,而是一种对象存储”。这种存储方式非常适合永久类型静态数据的长久保存,并对这些数据可以进行灵活的检索、调整、更新工作。一般用来存储虚拟机镜像文件、快照或存档文件等数据。
由于Swift中各节点功能平等,没有中心单元或主控节点,所以Swift拥有更强的扩展性、冗余和持久性。
2.1 主要特性
将Swift所有特性,总结起来,最重要也最根本的就是如下两条:持久性和对称性。其他一些特性正是已这两性质为基础,才得以扩展开来,以下是详细介绍:
2.1.1 持久性
良好的存储机制,完善的数据备份,使得Swift的存储持久性有着优异的表现,可以和亚马逊的S3服务相媲美。
根据国内团队的一个理论测试,在5个zone,5×10个存储节点的环境下,数据备份数为3,此时,数据持久性可以达到10个9(S3为11个9)。
2.1.2 对称性
在Swift存储架构中,每个存储节点完全对等,不存在主控存储点或中心单元,数据较为“均匀”地分布在各个存储点上。
正因为如此,Swift几乎具有无限的可扩展性,包括数据存储容量的无限扩展和性能的线性提升。通过增添机器,即可完成扩容,系统会自动进行数据迁移等工作,使得各存储节点重新达到平衡状态。
完全对称的节点分布,很好解决了单点故障的问题。Swift的元数据存储完全均匀随机分布,并保留多个备份,故障单点可以快速、便捷予以更换而不影响整体存储状况。
2.2 Swift架构分析
此处以官方给出的标准架构进行分析,如图一,分别为存储节点(Storage node)、代理节点(Proxy node)和认证节点(Auth node)三部分。
图一
2.2.1 Proxy node
代理节点可以说是Swift的核心,运行着swift-proxy-server进程。它提供Swift API的服务,负责Swift其余组件间的相互通信。对于每个客户端的请求,它在Ring中查询相应Account、Container以及Object的位置,并且转发这些请求。
它提供了Rest-full API,开发者可以通过这个接口快捷构建定制的客户端与Swift交互。
大量的存储错误也由Proxy Server处理:当一个服务器无法对一个对象的PUT操作进行响应,它将从Ring中查询一个可以接手的服务器并将请求传递给它。
2.2.2 Auth node
在存储节点和代理节点接入外网之前,首先要通过一个附加的认证节点对用户进行比对认证。在早期,Swift使用单独的认证服务对用户进行核对,随着OpenStack项目的逐渐发展,目前Swift以及结合了OpenStack的认证Keystone,形成统一的云认证体系。
2.2.3 Storage node
图中的Storage node实际中是多台节点,在官方文档中建议至少使用5个zone,一个zone就是一组独立节点,图二是官方给出的标配例图。关于zone以及Ring的概念将在后文详细展开。
图二
在整个存储点上,主要有两部分工作:一是Storage Server,二是Consistency Server。
[A] Storage Server:存储服务器运行三大进程:swift-account-server、swift-container-server、swift-object-server,分别管理账户数据库、容器数据库和对象数据库的存储工作。
账户服务器(Account Server):用于存储Containers清单,记录容器列表;
容器服务器(Container Server):主要职责是处理Objects列表,它不记录Objects具体存储何处,只存储Objects和存储它的容器的关联信息。Objects列表和Account列表都以sqlite数据库文件的形式存储,并且像Objects那样在集群中备份。包含存储对象总数量和容器使用容量的信息也被改服务器跟踪统计。
对象服务器(Object Server):是一个简单的用于存储、检索、删除存储在本地设备上的objects的二进制存储服务器(blob storage server)。Objects以二进制文件的形式,连带着存储在扩展属性(xattrs)中的元数据,被存储在文件系统中,这需要存储objects的基础文件系统支持文件的xattrs。像普通ext3的一些文件系统,默认情况下关闭了xattrs,目前推荐使用XFS文件系统,在大量的测试后也证明,Swift使用XFS能表现最好的整体性能。
[B] Consistency Server:一致性故障处理服务器查找并解决由数据损坏或硬盘故障引起的错误。主要分为三个部分:Auditor、Updater、Replicator。
审计器(Auditor):审计器在每个Swift服务器后台扫描磁盘,通过持续爬行检查对象、容器和账户的完整性。当一个文件被发现有损坏,就将此文件隔离,复制器将从其他副本复制一份此段数据来替换已损坏的隔离文件。对于不能处理的错误,将会记录日志。
更新器(Updater):有时,容器或账户数据不能被立即更新,这通常发生在出现错误或者系统过载时。当一个更新失败时,更新操作会在本地文件系统上排队,然后更新器会逐个处理这些失败的更新。
复制器(Replicator):复制器用以当系统面临临时性错误(比如断网或者设备损坏)时,保护系统一致性。复制进程通过将本地数据与每一个远程备份进行比较,来确保它们都是最新版本的数据。复制操作的更新基于PUSH方式,比如objects的复制操作,更新就是一个往其他节点同步文件的过程。Account和Container的复制操作通过HTTP来PUSH丢失的记录,或者远程同步全部的数据库文件。复制器同时也确保数据从系统的删除,当一个条目(objects、container、account)被删除,一个标记(官方成为“墓碑”)将作为此目标的最新版本被设置,复制器将见到此“墓碑”,并确保此目标被从所有的节点中删除。
2.2.4 Ring
Ring是包含在整个存储项目中的,并不与以上各节点形成并列关系,只是由于其重要程度,特地分为单独一节介绍。
Ring负责管理存储在磁盘上的一个实体的名字和这个实体所在物理位置之间的映射关系。对accounts、containers和objects都有独立的ring server与之对应,但他们的工作方式是一样的。当其它组件对object、container或account有任何操作时,都要和适当的ring server交互来确定他们在集群中的位置。Ring通过Zone(区域)、Device(设备)、Partition(分区)和Replica(副本)来维护映射信息。Ring中每个Partition在集群中都(默认)有3个Replica。每个Partition的位置由Ring来维护,并存储在映射中。Ring文 件在系统初始化时创建,之后每次增减存储节点时,需要重新平衡一下Ring文件中的项目,以保证增减节点时,系统因此而发生迁移的文件数量最少。
Zone:一个区域,用于保证数据隔离,一个Zone可以是一个硬盘、一个服务器、一个机架甚至一个数据中心。OpenStack官方要求一个Swift体系中至少配置5个Zone。
Replica:需要注意的是,每个副本要分隔于不同的Zone中。其默认值为3,这是因为NWR策略要求分布式系统中不允许存在数据单点。如果线上正常存在的Replica数量为1,一旦出错,就可能发生数据的永久性错误。如果设为2,只要有一个存储节点发生损坏,就会有单点存在,所以设置值必须大于2,但这个数值越高,维护成本和整体成本就越高,工业上一般都以3作为默认值。因此Swift符合NWR策略,非常可靠。
2.2.5 原理
Swift到底是如何通过Ring实现高效存储,和其他存储方式又有什么不同,下面就是Swift存储的相关原理。
[A] 普通哈希算法:假设有N台存储节点,为使服务器均衡负载,需要把对象均匀地映射到每台服务器上,通常会使用哈希算法来实现。分为两步,首先计算object的hash值Key,再计算Key mod N的结果,取得的余数即为数据存放的节点。
例如:有两台节点,N等于2,则值为0、1、2、3、4的Key按照余数分别存放在0、1、0、1、0号节点上。如果哈希算法是均匀的,数据就会平均分配在两个节点中。如果每个数据的访问量比较平均,负载也会平均分配到两个节点上。当然,这只是理想中的情况。在实际使用中,当数据量和访问量进一步增加,两个节点无法满足需求的时候,需要增加一个节点来服务客户端的请求。N增为3,映射关系变为Key mod (N+1),上述哈希值为2、3、4的数据需要重新分配。如果数据量很大,数据的迁移工作也会非常大,当N已为较大值时,再加入一个节点,会导致数据存储位置重新分配,代价巨大。有实验小组经过测试得出结论:在100个节点情况下,将107项数据使用md5 hash算法分配到每个node中,得出的结果为——“增加1%的存储能力=移动99%的数据”!
[B] 一致性哈希算法:Swift利用一致性哈希算法构建了一个冗余的可扩展的分布式对象存储集群。Swift采用一致性哈希的主要目的是在改变集群的Node数量时,能够尽可 能少地改变已存在Key和Node的映射关系。 该算法的思路分为以下三个步骤。 首先计算每个节点的哈希值,并将其分配到一个0~2的32次方的圆环区间上。其次使用相同方法计算存储对象的哈希值,也将其分配到这个圆环上。随后从数据映射到 的位置开始顺时针查找,将数据保存到找到的第一个节点上。如果超过2^32仍然找不到节点,就会保存到第一个节点上。 假设在这个环形哈希空间中存在5台服务器节点,若增加一台Server-6,根据算法得出Server-6被映射在Server-2和Server-3之间,那么受影响的将仅是沿 Server-6逆时针遍历到Server-2之间的对象(它们本来映射到Server-3上)。 具体过程如图三所示:增加一个Server-6以后仅对区间内部分数据产生影响,数据迁移量不大。
图三
实验小组在相同环境下,针对此种算法,得出的结果为——“增加1%的存储能力=移动50%的数据”。但是这样的结果还是无法令人满意,Swift在一致性哈希算法的基础上,引入虚节点的概念。
[C] 引入虚拟节点:哈希算法不能保证绝对的平衡,当节点较少的时候,需要存储的对象不能被均匀地映射到节点上,这样每个节点的存储压力不平衡。为了解决这个问题,在一致性哈希算法中加入了“虚拟节点(Partition)”概念:虚拟节点是实际节点在环形空间的复制品,一个实际节点对应了若干个虚拟节点,虚拟节点在哈希空间中以哈希值排列,结构如图四所示:
图四
由此,原来的映射关系反生了改变,由“object → node”转变为“object → vritual node → node”,逻辑上如图五所示:
图五
通过中间一层虚拟节点的存储再分配,数据可以比较均匀得分布在各个节点上了,同样的实验,在把100个节点细分成1000个虚拟节点之后,得出的结果为——“提升1%的能力=移动0.9%的数据”,这样的结果相对而言非常让人满意了!下面进行Swift组件的部署。
2.3 Swift部署
通过Keystone进行Swift的验证工作后,Swfit就精简到两个部分:代理和存储。正常来说,需要将二者分布在两台以上服务器上,这里为了快速搭建,完成实验,暂将Swift所有组件都装在一台电脑上,IP为10.2.20.90,模拟一个四节点的存储环境。着手安装之前需要先将OpenStack部署好,可以参考前面的文档,装好所需环境。在前文中都为Swift准备了一个分区,文件格式为XFS。
2.3.1 准备工作
1- 安装Swift存储服务所需软件包,其中xfsprogs用于XFS文件系统交互,curl用于Swift安装完毕后的测试工作,python-pastedeploy用于keystone访问:
# apt-get install swift swift-proxy swift-account swift-container \
> swift-object xfsprogs curl python-pastedeploy
2- 主节点已经提前建立了一个分区用于Swift,这里需要修改一下fstab文件
# vim /etc/fstab
UUID=……… /mnt/swift_backend xfs noatime,nodiratime,nobarrier,logbufs=8 0 0
3- 挂载存储目录并创建节点目录
# mount /mnt/storage
# cd /mnt/storage
# mkdir node1 node2 node3 node4
# chown swift.swift /mnt/storage
5- # vim /etc/default/rsync
RSYNC_ENABLE=true
6- # vim /etc/rsyncd.conf
# General stuff
uid = swift
gid = swift
log file = /var/log/rsyncd.log
pid file = /run/rsyncd.pid
address = 127.0.0.1
# Account Server replication settings
[account6012]
max connections = 25
path = /srv/node1/
read only = false
lock file = /run/lock/account6012.lock
[account6022]
max connections = 25
path = /srv/node2/
read only = false
lock file = /run/lock/account6022.lock
[account6032]
max connections = 25
path = /srv/node3/
read only = false
lock file = /run/lock/account6032.lock
[account6042]
max connections = 25
path = /srv/node4/
read only = false
lock file = /run/lock/account6042.lock
# Container server replication settings
[container6011]
max connections = 25
path = /srv/node1/
read only = false
lock file = /run/lock/container6011.lock
[container6021]
max connections = 25
path = /srv/node2/
read only = false
lock file = /run/lock/container6021.lock
[container6031]
max connections = 25
path = /srv/node3/
read only = false
lock file = /run/lock/container6031.lock
[container6041]
max connections = 25
path = /srv/node4/
read only = false
lock file = /run/lock/container6041.lock
# Object Server replication settings
[object6010]
max connections = 25
path = /srv/node1/
read only = false
lock file = /run/lock/object6010.lock
[object6020]
max connections = 25
path = /srv/node2/
read only = false
lock file = /run/lock/object6020.lock
[object6030]
max connections = 25
path = /srv/node3/
read only = false
lock file = /run/lock/object6030.lock
[object6040]
max connections = 25
path = /srv/node4/
read only = false
lock file = /run/lock/object6040.lock
7- 重启rsync服务
# service rsync restart
2.3.2 配置Swift各组件
8- 创建swift主配置文件
# vim /etc/swift/swift.conf
[swift-hash]
# random unique string that can never change (DO NOT LOSE)
swift_hash_path_suffix = `od -t x8 -N 8 -A n </dev/random`
9- 配置swift代理组件,需要建立配置文件,token和密码根据自己之前云环境的配置情况做相应改动
# vim /etc/swift/proxy-server.conf
[DEFAULT]
bind_port = 8080
user = swift
swift_dir = /etc/swift
[pipeline:main]
# Order of execution of modules defined below
pipeline = catch_errors healthcheck cache authtoken keystone proxy-server
[app:proxy-server]
use = egg:swift#proxy
allow_account_management = true
account_autocreate = true
set log_name = swift-proxy
set log_facility = LOG_LOCAL0
set log_level = INFO
set access_log_name = swift-proxy
set access_log_facility = SYSLOG
set access_log_level = INFO
set log_headers = True
account_autocreate = True
[filter:healthcheck]
use = egg:swift#healthcheck
[filter:catch_errors]
use = egg:swift#catch_errors
[filter:cache]
use = egg:swift#memcache
set log_name = cache
[filter:authtoken]
paste.filter_factory = keystone.middleware.auth_token:filter_factory
auth_protocol = http
auth_host = 127.0.0.1
auth_port = 35357
auth_token = CS2C
service_protocol = http
service_host = 127.0.0.1
service_port = 5000
admin_token = CS2C
admin_tenant_name = service
admin_user = swift
admin_password = zeastion
delay_auth_decision = 0
[filter:keystone]
paste.filter_factory = keystone.middleware.swift_auth:filter_factory
operator_roles = admin, swiftoperator
is_admin = true
10- 分为四个节点所以要分别创建配置文件
# vim /etc/swift/account-server/1.conf
[DEFAULT]
devices = /srv/node1
mount_check = false
bind_port = 6012
user = swift
log_facility = LOG_LOCAL2
[pipeline:main]
pipeline = account-server
[app:account-server]
use = egg:swift#account
[account-replicator]
vm_test_mode = no
[account-auditor]
[account-reaper]
11- 接下来将此文件为模板拷贝多份,分别对应每个节点修改参数:
# cp /etc/swift/account-server/1.conf /etc/swift/account-server/2.conf
# cp /etc/swift/account-server/1.conf /etc/swift/account-server/3.conf
# cp /etc/swift/account-server/1.conf /etc/swift/account-server/4.conf
# vim /etc/swift/account-server/2.conf
[DEFAULT]
devices = /srv/node2
mount_check = false
bind_port = 6022
user = swift
log_facility = LOG_LOCAL3
[pipeline:main]
pipeline = account-server
[app:account-server]
use = egg:swift#account
[account-replicator]
vm_test_mode = no
[account-auditor]
[account-reaper]
# vim /etc/swift/account-server/3.conf
[DEFAULT]
devices = /srv/node3
mount_check = false
bind_port = 6032
user = swift
log_facility = LOG_LOCAL4
[pipeline:main]
pipeline = account-server
[app:account-server]
use = egg:swift#account
[account-replicator]
vm_test_mode = no
[account-auditor]
[account-reaper]
# vim /etc/swift/account-server/4.conf
[DEFAULT]
devices = /srv/node4
mount_check = false
bind_port = 6042
user = swift
log_facility = LOG_LOCAL5
[pipeline:main]
pipeline = account-server
[app:account-server]
use = egg:swift#account
[account-replicator]
vm_test_mode = no
[account-auditor]
[account-reaper]
12- 这里需要修改一下主配置文件,加上一行sync
# vim /etc/swift/container-server.conf
[DEFAULT]
bind_ip = 0.0.0.0
workers = 2
[pipeline:main]
pipeline = container-server
[app:container-server]
use = egg:swift#container
[container-replicator]
[container-updater]
[container-auditor]
[container-sync]
13- 创建各节点的配置文件
# vim /etc/swift/container-server/1.conf
[DEFAULT]
devices = /srv/node1
mount_check = false
bind_port = 6011
user = swift
log_facility = LOG_LOCAL2
[pipeline:main]
pipeline = container-server
[app:container-server]
use = egg:swift#container
[container-replicator]
vm_test_mode = no
[container-updater]
[container-auditor]
[container-sync]
14- 同之前一样,将此文件拷贝多份,更改三个相关参数,过程略。
15- 配置一个节点
# vim /etc/swift/object-server/1.conf
[DEFAULT]
devices = /srv/node1
mount_check = false
bind_port = 6010
user = swift
log_facility = LOG_LOCAL2
[pipeline:main]
pipeline = object-server
[app:object-server]
use = egg:swift#object
[object-replicator]
vm_test_mode = no
[object-updater]
[object-auditor]
16- 也同样依照上面的方法创建多份。
17- 创建Ring。需要注意的是,下面命令中最后三个数字的意思分别为:虚节点(partition)数、副本(replica)数和延迟删除时间,以小时为单位。其中副本数不可大于node的数量,不然会报错。
# cd /etc/swift
# swift-ring-builder object.builder create 18 3 1
# swift-ring-builder container.builder create 18 3 1
# swift-ring-builder account.builder create 18 3 1
18- 向ring中添加已配置好的存储节点,权重为1
# swift-ring-builder object.builder add z1-127.0.0.1:6010/device 1
# swift-ring-builder object.builder add z2-127.0.0.1:6020/device 1
# swift-ring-builder object.builder add z3-127.0.0.1:6030/device 1
# swift-ring-builder object.builder add z4-127.0.0.1:6040/device 1
# swift-ring-builder container.builder add z1-127.0.0.1:6011/device 1
# swift-ring-builder container.builder add z2-127.0.0.1:6021/device 1
# swift-ring-builder container.builder add z3-127.0.0.1:6031/device 1
# swift-ring-builder container.builder add z4-127.0.0.1:6041/device 1
# swift-ring-builder account.builder add z1-127.0.0.1:6012/device 1
# swift-ring-builder account.builder add z2-127.0.0.1:6022/device 1
# swift-ring-builder account.builder add z3-127.0.0.1:6032/device 1
# swift-ring-builder account.builder add z4-127.0.0.1:6042/device 1
19- 检查Ring里的条目
# cd /etc/swift
# swift-ring-builder account.builder
# swift-ring-builder container.builder
# swift-ring-builder object.builder
20- 平衡Ring
# swift-ring-builder object.builder rebalance
# swift-ring-builder container.builder rebalance
# swift-ring-builder account.builder rebalance
21- 启动swift和REST API
# swift-init main start
# swift-init rest start
2.3.3 测试
22- # swift -v -V 2.0 -A http://127.0.0.1:5000/v2.0/ -U service:swift \
> -K zeasiton stat
显示了当前用户的存储情况,能够显示如上则配置无误。Swift的基本配置就结束了,后面还有Swift的实际应用和存储扩展,将在完成实际测试后归纳成文档。
http://blog.sina.com.cn/s/blog_6f2d2e31010123xf.html