在上一篇深入雲存儲系統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)。