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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!