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
posted @ 2019-03-04 19:51  luohaixian  阅读(7066)  评论(0编辑  收藏  举报