在上一篇深入云存储系统Swift核心组件:Ring实现原理剖析中,我们分析了Ring的设计原理,深入探讨了Swift如何通过Ring组件来实现冗余的、可扩展的目的。本文旨在分析在实际swift的运行中,如何来构建Ring文件。
Ring数据结构
Ring 的数据结构由三个顶层域构成,其中:
- List of Devices,表示集群中设备的列表;
- Partition Assignment List,表示partition到device的指派;
- Partition Shift Value,表示计算数据hash的移位量。
1.List of Devices
设备列表在Ring类内部被称为devs。在设备列表中的每一项是带有以下键的字典:
表1:Devs键值说明
id | integer | 在device列表中的索引 |
zone | integer | 设备所在的zone |
weight | float | device与其他device的相对权重。这常常直接与device和其它device的磁盘空间数量的比有关 |
ip | string | device的服务器IP地址 |
port | int | 服务器进程所使用的TCP端口用来提供该设备的服务请求 |
device | string | 服务器上device的磁盘名称。例如:sdb1 |
meta | string | 存储设备额外信息的通用字段。该信息并不直接被服务器进程使用,但是在调试时会派上用场。例如,安装的日期和时间和硬件生产商可以存储在这。 |
2.Partition Assignment List
用于存放每个replica与device间映射关系。在Ring类内部被称为_replica2part2dev_id,列表中含有replica数量(3)的array(‘I’),array(‘I’)的长度等于ring的partition数量,在array(‘I’)中的每个值为List of Devices中的索引id。
3.Partition Shift Value
Partition Shift Value在Ring类内部称为_part_shift。该值用于转换一个MD5 hash值来计算对于该哈希值的数据所在的partition。使用hash值的前4个字节用于计算。例如,为了计算路径/account/container/object的虚节点,Python代码如下:
partition = unpack_from('>I',md5('/account/container/object').digest())[0] >> self._part_shift
其中>表示big-endian byte order,I表示长度为4 byte unsigned int
举例:我们以SAIO安装下的ring文件为例,使用python读取/etc/swift/object.ring.gz存放的数据,获得的是以devs、 part_shift、 replica2part2dev_id 为key的dict类数据,其中:
devs=
[{'device': 'sdb1',
'id': 0,
'ip': '127.0.0.1',
'meta': '',
'port': 6010,
'weight': 1.0,
'zone': 1},
{'device': 'sdb2',
'id': 1,
'ip': '127.0.0.1',
'meta': '',
'port': 6020,
'weight': 1.0,
'zone': 2},
{'device': 'sdb3',
'id': 2,
'ip': '127.0.0.1',
'meta': '',
'port': 6030,
'weight': 1.0,
'zone': 3},
{'device': 'sdb4',
'id': 3,
'ip': '127.0.0.1',
'meta': '',
'port': 6040,
'weight': 1.0,
'zone': 4}]
part_shift=4
replica2part2dev_id=[array(‘I’, [3, 2...]) array(‘I’, [0, 1...]), array(‘I’, [1, 3...])]
构建Ring文件
假设我们配置了一个4个node组成的集群,分别为node0、node1、node2、node3,在这个集群中,我们为4个存储节点配置了2^18个partition,平均每个存储节点分配65536个partition。
需要使用swift-ring-bulider命令来构建ring文件,关于swift-ring-builder命令的详细用法,在shell下直接敲该命令即可获得提示。这里只涉及构建新Ring文件的方法,需要用到两个相关选项:
- 构建相关的builder文件:swift-ring-builder <builder_file> create <part_power> <replicas><min_part_hours>
- 添加node到builder文件:swift-ring-builder <builder_file> add z<zone>-<ip>:<port>/<device_name>_<meta> <weight>
例如,该集群目前ring的配置如下:
swift-ring-builder account.builder create 18 3 1
swift-ring-builder account.builder add z1-192.168.1.50:6002/sdc 100
swift-ring-builder account.builder add z2-192.168.1.51:6002/sdc 100
swift-ring-builder account.builder add z3-192.168.1.52:6002/sdc 100
swift-ring-builder account.builder add z4-192.168.1.54:6002/sdc 100
swift-ring-builder account.builder rebalance
swift-ring-builder container.builder create 18 3 1
swift-ring-builder container.builder add z1-192.168.1.50:6001/sdc 100
swift-ring-builder container.builder add z2-192.168.1.51:6001/sdc 100
swift-ring-builder container.builder add z3-192.168.1.52:6001/sdc 100
swift-ring-builder container.builder add z4-192.168.1.54:6001/sdc 100
swift-ring-builder container.builder rebalance
swift-ring-builder object.builder create 18 3 1
swift-ring-builder object.builder add z1-192.168.1.50:6000/sdc 100
swift-ring-builder object.builder add z2-192.168.1.51:6000/sdc 100
swift-ring-builder object.builder add z3-192.168.1.52:6000/sdc 100
swift-ring-builder object.builder add z4-192.168.1.54:6000/sdc 100
swift-ring-builder object.builder rebalance
Ring的Rebalance机制
当集群中发生存储节点宕机、新增(删)存储节点、新增(删)zone等必须改变partition和node间的映射关系时,就需要对Ring文件进行更新,也就是在swift文档中见到的rebalance一词。
在基于原有的Ring来构造新Ring时,swift-ring-builder首先要重新计算每个设备所需的partition数量。然后,将需要重新分配的partition收集起来。取消分配给被移除设备上的partition并把这些partition添加到收集列表。从拥有比当前所需的partition数多的设备上随机地取消分配多出的partition并添加到收集列表中。最后,将收集列表中的partition使用与初始化分配时类似的方法重新分配。
在本地执行swift-ring-builder命令行来生成新的ring文件,然后把这些文件复制到集群的每个节点的/etc/swift目录中,所有需要使用ring的server进程会每15秒(默认值)检查一遍ring文件的修改时间mtime,如果发现和内存中的不一致,则重新加载ring文件到内存中去。
举例说明
现在再增加一台存储节点node4并作为zone5,使用相同权重的devcie。那么每个存储节点上的partition数是52428.8。需要从每台存储节点上随机地移除13107.2个partition到收集列表,然后再重新分配这些parttion到node4上。当有partition的replica被重分配时,重分配的时间将被记录。在RingBuilder类内使用min_part_hours来限制在规定时间内,同一个partition不会被移动两次。
由于收集用来重新分配的partition是基于随机的,rebalacne进程并不能一次就可以完美地重平衡ring。为了达到一个较为平衡的ring,rebalacne进程被重复执行直到接近完美(小于1%)或者当rebalacne的提升达不到最小值1%。
具体的操作如下,首先移除旧的ring文件:
rm -f account.builder account.ring.gz backups/account.builder backups/account.ring.gz
.......
然后,重新平衡ring文件:
swift-ring-builder account.builder create 18 3 1
swift-ring-builder account.builder add z1-192.168.1.50:6002/sdc 100
swift-ring-builder account.builder add z2-192.168.1.51:6002/sdc 100
swift-ring-builder account.builder add z3-192.168.1.52:6002/sdc 100
swift-ring-builder account.builder add z4-192.168.1.54:6002/sdc 100
swift-ring-builder account.builder add z5-192.168.1.53:6002/sdc 100
swift-ring-builder account.builder rebalance
swift-ring-builder container.builder create 18 3 1
swift-ring-builder container.builder add z1-192.168.1.50:6001/sdc 100
swift-ring-builder container.builder add z2-192.168.1.51:6001/sdc 100
swift-ring-builder container.builder add z3-192.168.1.52:6001/sdc 100
swift-ring-builder container.builder add z4-192.168.1.54:6001/sdc 100
swift-ring-builder container.builder add z5-192.168.1.53:6001/sdc 100
swift-ring-builder container.builder rebalance
swift-ring-builder object.builder create 18 3 1
swift-ring-builder object.builder add z1-192.168.1.50:6000/sdc 100
swift-ring-builder object.builder add z2-192.168.1.51:6000/sdc 100
swift-ring-builder object.builder add z3-192.168.1.52:6000/sdc 100
swift-ring-builder object.builder add z4-192.168.1.54:6000/sdc 100
swift-ring-builder object.builder add z5-192.168.1.53:6000/sdc 100
swift-ring-builder object.builder rebalance
最后,复制account.ring.gz、container.ring.gz、object.ring.gz到集群的各节点的/etc/swift目录下。这样我们就完成了Ring的重平衡(rebalance)。