3.3 CRUSH关系分析
从本质上讲,CRUSH算法是通过存储设备的权重来计算数据对象的分布的。在计算过程中,通过Cluster Map(集群映射)、Data Distribution Policy(数据分布策略)和给出的一个随机数共同决定数据对象的最终位置。
1. Cluster Map
Cluster Map记录所有可用的存储资源及相互之间的空间层次结构(集群中有多少个机架、机架上有多少服务器、每个机器上有多少磁盘等信息)。所谓的Map,顾名思义,就是类似于我们生活中的地图。在Ceph存储里,数据的索引都是通过各种不同的Map来实现的。另一方面,Map 使得Ceph集群存储设备在物理层作了一层防护。例如,在多副本(常见的三副本)的结构上,通过设置合理的Map(故障域设置为Host级),可以保证在某一服务器死机的情况下,有其他副本保留在正常的存储节点上,能够继续提供服务,实现存储的高可用。设置更高的故障域级别(如Rack、Row等)能保证整机柜或同一排机柜在掉电情况下数据的可用性和完整性。
(1)Cluster Map的分层结构
Cluster Map由Device和Bucket构成。它们都有自己的ID和权重值,并且形成一个以Device为叶子节点、Bucket为躯干的树状结构,如图3-3所示。
图3-3 CRUSH架构图
Bucket拥有不同的类型,如Host、Row、Rack、Room等,通常我们默认把机架类型定义为Rack,主机类型定义为Host,数据中心(IDC机房)定义为Data Center。Bucket的类型都是虚拟结构,可以根据自己的喜好设计合适的类型。Device节点的权重值代表了存储设备的容量与性能。其中,磁盘容量是权重大小的关键因素。
OSD 的权重值越高,对应磁盘会被分配写入更多的数据。总体来看,数据会被均匀写入分布于群集所有磁盘,从而提高整体性能和可靠性。无论磁盘的规格容量,总能够均匀使用。
关于OSD权重值的大小值的配比,官方默认值设置为1TB容量的硬盘,对应权重值为1。
可以在/etc/init.d/ceph 原码里查看相关的内容。
…
363 get_conf osd_weight "" "osd crush initial weight"
364 defaultweight="$(df -P -k $osd_data/. | tail -1 | awk '{ print sprintf("%.2f",$2/1073741824) }')" ###此处就是ceph默认权重的设置值
get_conf osd_keyring "$osd_data/keyring" "keyring"
366 do_cmd_okfail "timeout 30 $BINDIR/ceph -c $conf --name=osd.$id --keyring=$osd_keyring osd crush create-or-move -- $id ${osd_weight:-${defaultweight:-1}} $osd_location"
…
(2)恢复与动态平衡
在默认设置下,当集群里有组件出现故障时(主要是OSD,也可能是磁盘或者网络等),Ceph会把OSD标记为down,如果在300s内未能回复,集群就会开始进行恢复状态。这个“300s”可以通过“mon osd down out interval”配置选项修改等待时间。PG(Placement Groups)是Ceph数据管理(包括复制、修复等动作)单元。当客户端把读写请求(对象单元)推送到Ceph时,通过CRUSH提供的Hash算法把对象映射到PG。PG在CRUSH策略的影响下,最终会被映射到OSD上。
2. Data Distribution Policy
Data Distribution Policy由Placement Rules组成。Rule决定了每个数据对象有多少个副本,这些副本存储的限制条件(比如3个副本放在不同的机架中)。一个典型的rule如下所示。
rule replicated_ruleset { ##rule名字
ruleset 0 # rule的Id
type replicated ## 类型为副本模式,另外一种模式为纠删码(EC)
min_size 1 ## 如果存储池的副本数大于这个值,此rule不会应用
max_size 10 ## 如要存储池的副本数大于这个值,此rule不会应用
step take default ## 以default root为入口
step chooseleaf firstn 0 type host ## 隔离域为host级,即不同副本在不同的主机上
step emit ## 提交
}
根据实际的设备环境,可以定制出符合自己需求的Rule,详见第10章。
3. CRUSH中的伪随机
先来看一下数据映射到具体OSD的函数表达形式。
CRUSH(x) -> (osd1, osd2, osd3.....osdn)
CRUSH使用了多参数的Hash函数在Hash之后,映射都是按既定规则选择的,这使得从x到OSD的集合是确定的和独立的。CRUSH只使用Cluster Map、Placement Rules、X。CRUSH是伪随机算法,相似输入的结果之间没有相关性。关于伪随机的确认性和独立性,以下我们以一个实例来展示。
[root@host-192-168-0-16 ~]# ceph osd tree
ID WEIGHT TYPE NAME UP/DOWN REWEIGHT PRIMARY-AFFINITY
-1 0.35999 root default
-3 0.09000 host host-192-168-0-17
2 0.09000 osd.2 up 1.00000 1.00000
-4 0.09000 host host-192-168-0-18
3 0.09000 osd.3 up 1.00000 1.00000
-2 0.17999 host host-192-168-0-16
0 0.09000 osd.0 up 1.00000 1.00000
1 0.09000 osd.1 up 1.00000 1.00000
以上是某个Ceph集群的存储的CRUSH结构。在此集群里我们手动创建一个pool,并指定为8个PG和PGP。
[root@host-192-168-0-16 ~]# ceph osd pool create crush 8 8
pool 'crush' created
PGP是PG的逻辑承载体,是CRUSH算法不可缺少的部分。在Ceph集群里,增加PG数量,PG到OSD的映射关系就会发生变化,但此时存储在PG里的数据并不会发生迁移,只有当PGP的数量也增加时,数据迁移才会真正开始。关于PG和PGP的关系,假如把PG比作参加宴会的人,那么PGP就是人坐的椅子,如果人员增加时,人的座位排序就会发生变化,只有增加椅子时,真正的座位排序变更才会落实。因此,人和椅子的数量一般都保持一致。所以,在Ceph里,通常把PGP和PG设置成一致的。
可以查看PG映射OSD的集合,即PG具体分配到OSD的归属。
[root@host-192-168-0-16 ~]# ceph pg dump | grep ^22\. | awk '{print $1 "\t" $17}' ## 22 表示 Pool id ##
dumped all in format plain
22.1 [1,2,3]
22.0 [1,2,3]
22.3 [3,1,2]
22.2 [3,2,0]
22.5 [1,3,2]
22.4 [3,1,2]
22.7 [2,1,3]
22.6 [3,0,2]
上面示例中,第一列是PG的编号(其中22表示的Pool的ID),共计8个,第二列是PG映射到了具体的OSD集合。如“22.1 [1,2,3]”表示PG 22.1分别在OSD1、OSD2和OSD3分别存储副本。
在Ceph集群里,当有数据对象要写入集群时,需要进行两次映射。第一次从object→PG,第二次是PG→OSD set。每一次的映射都是与其他对象无相关的。以上充分体现了CRUSH的独立性(充分分散)和确定性(可确定的存储位置)。