ceph crush算法和crushmap淺析


1 什么是crushmap

crushmap就相當於是ceph集群的一張數據分布地圖,crush算法通過該地圖可以知道數據應該如何分布;找到數據存放位置從而直接與對應的osd進行數據訪問和寫入;故障域的設置和數據冗余選擇策略等。crushmap的靈活設置顯示出了ceph的軟件定義存儲方案。
這里可以引入raid相關的概念來對比下:
raid0:又稱為Stripe,中文名為條帶,它是所有RAID級別中存儲性能最高的,它的原理就是把連續的數據分散存儲到多個磁盤中,充分利用了每個磁盤的吞吐,突破了單個磁盤的吞吐限制。
raid1:又稱為Mirror,中文名為鏡像,它的主要宗旨是保證用戶的數據可用性和可修復性,它的原理是把用戶寫入硬盤的數據百分之百的自動復制到另外一個磁盤上。
raid10:高可靠性與高效磁盤結構,可以理解為raid0和raid1的互補結合,類似於ceph中的多副本策略。
raid5:是一種存儲性能、存儲成本和數據安全都兼顧的一種方案,它的原理是不對存儲的數據進行備份,而是把數據和存儲校驗信息存儲到組成raid5的各個磁盤上,當raid5的一個磁盤損壞時,可以根據剩下的數據和奇偶校驗信息來恢復損壞的數據,類似於ceph中的糾刪碼策略。

對導出的crushmap進行反編譯后得到的內容如下:

# begin crush map 選擇存放副本位置時的選擇算法策略中的變量配置
tunable choose_local_tries 0
tunable choose_local_fallback_tries 0
tunable choose_total_tries 50
tunable chooseleaf_descend_once 1
tunable straw_calc_version 1

# devices    一般指的是葉子節點osd
device 0 osd.0
device 1 osd.1
device 2 osd.2

# types    樹形下的多種類型
type 0 osd    # 一般一個osd對應一個磁盤
type 1 host    # 一般host表示是一個主機,即某一台服務器 
type 2 chassis # 系列
type 3 rack # 機架
type 4 row # 排
type 5 pdu # 
type 6 pod    # 
type 7 room # 機房
type 8 datacenter    # 數據中心
type 9 region    # 區域
type 10 root    # 根

# buckets    軀干部分 host一般是表示一個物理節點,root是樹形的根部
host thinstack-test0 {
id -2    # do not change unnecessarily
# weight 0.031
alg straw # ceph作者實現的選擇item的算法
hash 0    # rjenkins1 這代表的是使用哪個hash算法,0表示選擇rjenkins1這種hash算法
item osd.0 weight 0.031
}
host thinstack-test1 {
id -3    # do not change unnecessarily
# weight 0.031
alg straw
hash 0    # rjenkins1
item osd.1 weight 0.031
}
host thinstack-test2 {
id -4    # do not change unnecessarily
# weight 0.031
alg straw
hash 0    # rjenkins1
item osd.2 weight 0.031
}
root default {
id -1    # do not change unnecessarily
# weight 0.094
alg straw
hash 0    # rjenkins1
item thinstack-test0 weight 0.031
item thinstack-test1 weight 0.031
item thinstack-test2 weight 0.031
}

# rules    副本選取規則的設定
rule replicated_ruleset {
ruleset 0
type replicated
min_size 1
max_size 10
step take default    # 以default root為入口
step chooseleaf firstn 0 type host    # 看如下 解釋1
step emit    # 提交
}

# end crush map

解釋1:
step chooseleaf firstn {num} type {bucket-type}
chooseleaf表示選擇bucket-type的bucket並選擇其的葉子節點(osd)
當num等於0時:表示選擇副本數量個bucket
當num > 0 and num < 副本數量時:表示選擇num個bucket
當num < 0: 表示選擇 副本數量 - num 個bucket

 

2 crush的基本原理

常用的分布式數據分布算法有一致性Hash算法和Crush算法,Crush也稱為可擴展的偽隨機數據分布算法
一致性Hash算法原理:
對一個圓形(圓形可以是0-2的31次方-1)用n個虛擬節點將其划分成n個區域,每個虛擬節點管控一個范圍,如下圖所示:

T0負責[A, B],T1負責[B, C],T2負責[C,D],T3負責[D,A]
由於分區是固定的,所以我們很容易知道哪些數據要遷移,哪些數據不需要遷移。
在每個節點的存儲容量相等且虛擬節點跟物理節點個數一致時,就會是每個節點對應一段相同大小的區段,從而可以達到最佳的數據分布。

Crush算法原理:
Crush算法跟一致性Hash算法原理很類似,其中的pg就是充當上面所說的虛擬節點的作用,用以分割區域,每個pg管理的數據區間相同,因而數據能夠均勻的分布到pg上。

crush分布數據過程:
數據x經過hash函數得到一個值,將該值與pg數量取模得到數值,該數值即是pg的編號,然后通過crush算法知道pg是對應哪些osd的,最后是將數據存放到pg對應的osd上。其中經過了兩次的映射,一次是數據到pg的映射,一次是pg到osd的映射,由於pg是抽象的存儲節點,一般情況下,pg的數量是保持的不變的,不隨着節點的增加或減少而改變,因此數據到pg的映射是穩定的。

 

3 利用crushmap可以做哪些事

(1)故障域的設置
ceph中已經定義的故障域有:
# types
type 0 osd # 一般一個osd對應一個磁盤
type 1 host # 一般host表示是一個主機,即某一台服務器
type 2 chassis # 系列
type 3 rack # 機架
type 4 row # 排
type 5 pdu #
type 6 pod #
type 7 room # 機房
type 8 datacenter # 數據中心
type 9 region # 區域
type 10 root # 根
當然也可以自定義類型
比如有3台服務器,設置的故障域為host,數據冗余策略為多副本的2副本策略,則數據會保存到其中的兩台服務器上,這樣當其中一台服務器掛掉時,確保還有冗余數據在另外一台服務器上,保證了數據的可靠性。


(2)指定數據存放到指定位置上
比如對於一些私有雲來說,可以划分ssd的磁盤用以存放鏡像等數據,這樣虛擬機啟動會比較快,對於快照和備份等冷數據則可存放在sata盤中。


(3)保證數據盡可能的均衡
可以設定osd的權重,如果只考慮容量,則一般1T容量權重設為1.0


(4)盡可能保證數據不會大量遷移引起性能問題
當有節點故障時需要進行數據均衡時,可以設定合理的crushmap讓數據的遷移只發生在一個小范圍內

4 crushmap實踐

(1)獲取當前crushmap並修改和應用
獲取當前集群中應用的crushmap文件到一個文件中:ceph osd getcrushmap -o crushmap.txt
上面導出的crushmap.txt是亂碼的,需要進行反編譯來查看:crushtool -d crushmap.txt -o crushmap-decompile
此時就是文本形式了,可以在這個文件上修改,修改后進行編譯:crushtool -c crushmap-decompile -o crushmap-compiled
然后應用到集群中:ceph osd setcrushmap -i crushmap-compiled
注意:這樣修改如果想要服務重啟后保持原來的,需要在配置文件的[osd]上加上:osd crush update on start = false,否則啟動時會校驗當前位置是否是正確的位置,如果不是則會自動移動到之前的位置

如果只是想查看下當前的crush map分布,可以用命令行查看:ceph osd crush tree

(2)命令行構建crush map
<1>添加bucket:ceph osd crush add-bucket {bucket-name} {bucket-type}
比如添加一個root類型的名為default的bucket:ceph osd crush add-bucket default root
添加一個host類型的名為test1的bucket:ceph osd crush add-bucket test1 host

<2>移動一個bucket:ceph osd crush move {bucket-name} {bucket-type}={bucket-name}, [...]
比如我想將bucket1 test1移動到root類型的名為default2下:ceph osd crush move test1 root=default2

move操作是針對host及host以上的bucket的,如果是想把osd移動到某個bucket上,則可以先ceph osd crush remove 掉osd,然后再ceph osd crush add重新指定到想要指定的bucket下去

<3>移除一個bucket:ceph osd crush remove {bucket-name}
比如:ceph osd crush remove test1

<4>添加或移動osd的crush map位置:ceph osd crush set {name} {weight} root={root} [{bucket-type}={bucket-name} ...]
比如我將osd.1設置test1下:ceph osd crush set osd.1 1.0 root=default host=test1

或者用ceph osd crush add osd.1 host=test1

<5>調整osd的權重:ceph osd crush reweight {name} {weight}
比如:ceph osd crush reweight osd.1 2.0

<6>移除osd crush map:ceph osd crush remove {name}
比如:ceph osd crush remove osd.1

<7>創建一個副本策略:ceph osd crush rule create-replicated {name} {root} {failure-domain-type} [{class}] # 低版本沒有這個,比如0.94.10就沒有
比如創建一個故障域級別為host的副本策略:ceph osd crush rule create-replicated rep_test default host
自己項目中用的創建命令:ceph osd crush rule create-simple rep_test default host firstn # 原型是osd crush rule create-simple <name> <root> <type> {firstn|indep}

<8>給pool指定副本策略:ceph osd pool set <pool-name> crush_rule <rule-name>
比如:ceph osd pool set testpool crush_rule test_rule

(3)創建指定主副本放在host A上,其它副本放在host B上

# rules
rule replicated_ruleset {
ruleset 0
type replicated
min_size 1
max_size 10
step take A
step chooseleaf firstn 1 type osd
step emit

step take B
step chooseleaf firstn -1 type osd
step emit
}

這時可以用ceph pg dump命令看pg的分布是否是如我們所想的那樣分布。
或者測試數據是否真的落到我們想要的osd上:
put一個文件命名對象名為uuu到testpool中去:rados put uuu kk.txt -p volumes
查看該對象是在哪個osd上:ceph osd map testpool uuu

還有個簡單的方法來設置某個osd作為主的,某個為副osd:
假設只有osd.0和osd.1,要將osd.0作為副本osd,1作為主osd,則可以將osd.0的主親和力設置為0,這樣osd.0就只能做副本osd
ceph tell mon.\* injectargs '--mon_osd_allow_primary_affinity=true'
ceph osd primary-affinity osd.0 0

(4)將ssd磁盤的osd用來放主副本,hdd磁盤的osd用來放副本
思路:可以創建兩個root類型,一個名為ssd,一個名為hdd,然后ssd磁盤的osd移到root=sdd下,hdd磁盤的osd移到root=hdd下,crushmap內容如下:

# begin crush map
tunable choose_local_tries 0
tunable choose_local_fallback_tries 0
tunable choose_total_tries 50
tunable chooseleaf_descend_once 1
tunable straw_calc_version 1

# devices
device 0 osd.0
device 1 osd.1

# types
type 0 osd
type 1 host
type 2 chassis
type 3 rack
type 4 row
type 5 pdu
type 6 pod
type 7 room
type 8 datacenter
type 9 region
type 10 root

# buckets
host A {
id -2    # do not change unnecessarily
# weight 0.031
alg straw
hash 0    # rjenkins1
item osd.0 weight 0.031
}
host B {
id -3    # do not change unnecessarily
# weight 0.031
alg straw
hash 0    # rjenkins1
item osd.1 weight 0.031
}
root ssd {
id -5    # do not change unnecessarily
# weight 0.031
alg straw
hash 0    # rjenkins1
item A weight 0.031
}
root hdd {
id -6    # do not change unnecessarily
# weight 0.031
alg straw
hash 0    # rjenkins1
item B weight 0.031
}

# rules
rule replicated_ruleset {
ruleset 0
type replicated
min_size 1
max_size 10
step take ssd
step chooseleaf firstn 1 type host
step emit

step take hdd
step chooseleaf firstn -1 type host
step emit
}

# end crush map

ceph osd tree的命令執行如下:

[root@test ~]# ceph osd tree
ID WEIGHT  TYPE NAME                UP/DOWN REWEIGHT PRIMARY-AFFINITY 
-6 0.03099 root hdd                                                   
-3 0.03099     host thinstack-test1                                   
 1 0.03099         osd.1                 up  1.00000          1.00000 
-5 0.03099 root ssd                                                   
-2 0.03099     host thinstack-test0                                   
 0 0.03099         osd.0                 up  1.00000          1.00000


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM