一直以来都在使用openstack的swift来做分布式存储,至于为什么使用其来做存储在这里就不说明(网上搜搜其的优点就知道了),
随着项目的步步深入,越来越多的问题也随之而来。在项目进行的过程中对swift的理解也越来越多,swift的核心代码行并不是太多,而且也十分容易理解。下面通过实验来理解swift Object的副本修复功能(swift版本为1.9.1)。
一. swift的简单配置
为了能更好的理解swift的分布式存储功能,在此将swift默认的认证功能去掉。proxy-server.conf的相关配置如下:
通过命令swift-ring-builder /etc/swift/object.builder查看swift集群配置如下:
从图中可以知道,有三个Zone、三个副本、三块存储设备分别是sdb1 sdb2 sdb3,这样一来每个存储设备中就会存储一个副本
二. 场景设计及验证记录
在此假设所有的配置已经真确配置并能正常运行,那么我们来开启相关服务,为了能够更好的理解各个服务在此采用单独开启的方法:
swift-init proxy start
swift-init account start
swift-init container start
在多个终端中分别按如下方式去开启object相关的服务,便于查看对应的操作输出
swift-init object start -n -v
swift-object-auditor /etc/swift/object-server.conf -v
swift-object-replicator /etc/swift/object-server.conf -v
swift-object-updater /etc/swift/object-server.conf -v
swift-object-expirer /etc/swift/object-expirer.conf -v
在这里主要关注swift-object-auditor和swift-object-replicator服务的相关信息输出
1. 使用curl分别创建对应的账户、容器和对象(9022是proxy服务监听的端口号)
curl -X PUT -I http://127.0.0.1:9022/v1.0/a # 创建账户a
curl -X PUT -I http://127.0.0.1:9022/v1.0/a/a #在账户a中创建容器a
curl -X PUT -H 'Content-Length: 0' -I http://127.0.0.1:9022/v1.0/a/a/a #在容器a中创建一个空的对象a
2. 为了知道对象a在物理磁盘的具体位置我们可以使用swift-get-nodes /etc/swift/object.ring.gz a a a命令来查找,在我的机器中a在磁盘中的位置信息如下
由上图可知道对象a的三个副本分别存储在如下目录中
/srv/node/sdb1/objects/245255/226/ef81c496a61fca1e2ee8f1c377bb0226/
/srv/node/sdb2/objects/245255/226/ef81c496a61fca1e2ee8f1c377bb0226/
/srv/node/sdb3/objects/245255/226/ef81c496a61fca1e2ee8f1c377bb0226/
查看三个目录的信息内容分别如下图所示:
由上图可知每个副本的大小都为0,跟之前创建的大小是一致的,每个副本所存储的名字也是一致的,连存储路径除了设备号不一致外其他都是一致的。
我们还可以知道文件所在目录的上一级目录的名字是文件所在目录的名字后三位。关于245255这个目录是swift中Partition这个概念对应的目录名,在
Partition目录中存储了如下图所示的内容:
在hashes.pkl中记录了该Partition下的所有对象的相关hash内容,
如果该文件被他人手动更改那么object-replicator服务则会重新计算hash并重写该文件,
如果该目录下的内容被被恶意删除
object-replicator服务也会从其他副本中拷贝一份过来,如果用于保存对象内容的文件被恶意修改则object-auditor服务
会将该文件放到隔离区,然后object-replicator服务从其他副本中拷贝一份过来。
看到这里,我们对swift的对象存储方式有了一定的了解了,接下来我们设计一些破坏对象副本的场景来达到本文的主要目的。
3. 场景模拟
1. 恶意破坏或者删除所有副本的hashes.pkl文件
/srv/node/sdb1/objects/245255/hashes.pkl文件原始内容如
我们将其更改为
,在经过数秒后其就
会变成和原始的一样,那在这个过程中object-replicator服务做了什么动作呢?具体的大概步骤是object-replicator服务从object.ring.gz文件中获取到本机
各个存储设备,本例中的是sdb1、sdb2、sdb3,然后随机排序三个设备按排序后的顺序逐个扫描其下的objects目录,此时如果发现某个Partition下的hashes.pkl
不存在或被修改则会从新计算该副本的hash,然后向其他副本所在的节点object-server服务发出一个REPLICATE请求,请求对应副本的hash值(如果其他副本的hash
值也被修改或者删除,它们也会重新计算,如果没有变更则直接读出里面的数据,之后再回复请求),最后使用本地副本的hash值与请求回来的hash值进行比较,
如果相等则不作任何处理,如果不等则做对象数据同步,这样被修改hashes.pkl文件又回复称原来的样子了。
2. 强制删除一个或者两个副本
rm -rf /srv/node/sdb1/objects/245255/*
rm -rf /srv/node/sdb2/objects/245255/*
执行以上两个命令则将对象a所处的sdb1,sdb2上的副本删除,并且连hashes.pkl也删除了,几秒钟过后我们可以从
执行swift-object-replicator /etc/swift/object-server.conf -v的终端中看到如下内容:
修复后的/srv/node/sdb1/objects/245255/ 和 /srv/node/sdb2/objects/245255/ 目录内容如下:
从以上两张图我们可以知道object-replicator服务通过rsync把sdb3中副本同步到了sdb1和sdb2中(其实就是将Partition 245255下的内容进行同步),这样就到达了
副本修复的功能。最终要的效果是达到了,但是其内部做了什么动作呢?大概步骤是: 刚开始与第一种场景的步骤是一样的,先比较hash,如果发现hash不一样则进行
数据同步。那么swift是如何计算hash的呢?其实很简单就是找对象副本所存储的文件的名字和其它文件名串联起来计算出md5值,具体算法可以阅读源码来理解。如果
找不到对应的文件,就无法计算出hash值,那么就认为该副本出了问题,于是就将完整的副本同步到该Partition的目录中,最终达到副本修复功能。
3. 修改副本文件的内容
原来的/srv/node/sdb1/objects/245255/226/ef81c496a61fca1e2ee8f1c377bb0226/1381912898.96369.data文件为空,现在我们往该文件中写入一些数据
echo "abcdefg" >> /srv/node/sdb1/objects/245255/226/ef81c496a61fca1e2ee8f1c377bb0226/1381912898.96369.data
过一段时间我们会在执行命令swift-object-auditor /etc/swift/object-server.conf -v和swift-object-replicator /etc/swift/object-server.conf -v的终端
中分别看到如下输出:
&
由上两幅图可以看出swift-object-auditor服务报出了一个关于文件/srv/node/sdb1/objects/245255/226/ef81c496a61fca1e2ee8f1c377bb0226/1381912898.96369.data的错误,
而swift-object-replicator服务很快就把该文件给修复了,那么这里具体做了哪些步骤呢?大概步骤是:swift-object-auditor服务是一个审计服务,其作用就是扫描各个存储设备
中的objects目录,计算对象副本文件内容的md5值,判断该值是否与最初写入时元数据中Etag的值相等,如果不等则认为该对象出了问题,于是就将该对象移动到该存储设备(sdb1)
中的quarantined目录中(我们称之为隔离区),之后就会去更改对应Partition目录下的哈希记录文件(hashes.pkl)。到这swift-object-auditor对该副本的审计工作已经完成,
接下修复副本的工作就交给了swift-object-replicator服务,swift-object-replicator在扫描存储设备的时候发现hashes.pkl文件被修改过,则会重新计算副本的hash,但此时副本
文件不存在,计算hash失败,那么就向副本正常的节点发起同步该副本的请求,最终达到副本修复的功能。
如上图所示被恶意修改过的对象副本文件被移动到了隔离区当中。
4. 直接删除一份或者两份存储对象副本的文件
rm -rf /srv/node/sdb1/objects/245255/226/ef81c496a61fca1e2ee8f1c377bb0226/1381912898.96369.data
rm -rf /srv/node/sdb2/objects/245255/226/ef81c496a61fca1e2ee8f1c377bb0226/1381912898.96369.data
以上命令删除了对象a两个副本的存储文件,在过了一段时间之后副本并没有修复,那么这是为什么呢?根据之前的几个场景分析可以初步总结出副本修复的一个必要条件:
hashes.pkl被更改。如果hashes.pkl与其他副本的还保持一致则不会重新就算hash,因此它也不会发现真正的数据已经不见了。到这里你可能会问swift-object-auditor服务
不会发现文件不存在了吗?根据代码分析swift-object-auditor服务所做的事情很简单,就是对存储设备中objects目录下
存在的文件进行审计,它是不会知道数据在
swift中的关系的。
三. 总结
1. 对象副本修复功能由swift-object-auditor、swift-object-replicator、object-server以及rsync这几个服务共同完成
2. 当集群中只剩下一个正确的对象副本时,副本也能真确修复
3. 当集群中有对象副本被恶意修改,只要还要一个副本是正确的也能修复
4. 当所有对象副本都被破坏时,所有对象副本就会被动到隔离区,那么该数据就从集群中彻底的损坏了
5. 对象副本的修复只有hashes.pkl文件和存储对象副本内容的文件有变更时才会进行rsync数据同步
注:本文只是简单的对swift对象修复的过程做简单介绍,其内部实现还有很多实现细节(比如一致性处理等)在这里没有提到,更多内容还需要我们对其进一步的理解。