参考资料:
《Ceph 之 RADOS 设计原理与实现》
https://docs.ceph.com/en/latest/rados/operations/crush-map/
http://strugglesquirrel.com/2019/02/02/ceph%E8%BF%90%E7%BB%B4%E5%A4%A7%E5%AE%9D%E5%89%91%E4%B9%8B%E9%9B%86%E7%BE%A4osd%E4%B8%BAfull/
https://docs.ceph.com/en/latest/rados/operations/balancer/#modes
CRUSH
Crush 算法是用来计算对象分布在哪个 OSD 上的工具。其包含两个步骤:
-
计算出对象到 PG 的映射,使用哈希算法。如果 PG 数量不变,则此结果始终不变。
Hash(oid) = pgid
-
计算出 PG 到 具体 OSD 的映射,一般使用 straw2 算法(《Ceph 设计原理与实现》Crush 章节有详细介绍)。此结果可以通过调节 OSD 的权重来改变。
CRUSH(pgid) = OSDid
由此可见,对象被分布在一个个的 PG 当中,并且这个映射结果在 PG 数量不变的情况下始终保持稳定,这样通过管理 PG 的分布就相当于管理了整个集群中对象的分布。在 《Ceph 之Rados设计原理与实现》第一章中,详细介绍了PG分裂与扩容方式以及其高效简洁的原因。
编辑 CRUSH map
Crushmap 主要由两部分组成:cluster map 和 placement rule。前者描述整个集群设备的分布情况,后者规定了挑选 OSD 的步骤规则。
获取 CRUSH map
此命令获取的 CRUSH map 是经过编译的,需要经过反编译之后才能以文本的方式被编辑。
ceph osd getcrushmap -o {compiled-crushmap-filename}
[root@node-1 ~]# ceph osd getcrushmap -o crushmap
7
[root@node-1 ~]# ls -al crushmap
-rw-r--r-- 1 root root 845 6月 22 10:31 crushmap
反编译 CRUSH map
将 getcrushmap 获取的 CRUSH map 转化为可编辑可阅读的文本。
crushtool -d {compiled-crushmap-filename} -o {decompiled-crushmap-file}
[root@node-1 ~]# crushtool -d crushmap -o crushmap.txt
[root@node-1 ~]# cat crushmap.txt
# begin crush map
# 一般不更改
tunable choose_local_tries 0 # 已废弃,为做向后兼容设为0
tunable choose_local_fallback_tries 0 # 已废弃,为做向后兼容设为0
tunable choose_total_tries 50 # 选择 bucket 最大尝试次数,默认值 50
tunable chooseleaf_descend_once 1 # 已废弃,为做向后兼容设为1
tunable chooseleaf_vary_r 1 #
tunable chooseleaf_stable 1 # 避免一些不必要的 pg 迁移
tunable straw_calc_version 1 # starw 算法版本,为向后兼容设为1
tunable allowed_bucket_algs 54 # 允许使用的 bucket 选择算法,54 代表 straw2 算法
# devices
# 每一个最末端的的物理设备(即 OSD),也叫叶子节点,一般无需手动设置。
# id 一般为大于等于0。与后面的 bucket id 不同。
device 0 osd.0 class hdd
device 1 osd.1 class hdd
device 2 osd.2 class hdd
# types
# 定义的 bucket 类型,可自行定制(增删 type 类型),编号必须为正整数。
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 zone
type 10 region
type 11 root
# buckets
# 所有的中间节点就叫做bucket,bucket可以是一些devices的集合也可以是低一级的buckets的集合, 根节点称为root是整个集群的入口, bucket的id必须是负数且唯一,一个bucket在crush map 实际存储位置是 buckets[-1-(bucket id)]。
host node-1 {
id -3 # do not change unnecessarily
id -4 class hdd # do not change unnecessarily
# weight 0.010 # 此 bucket 权重,等于 item 权重之和
alg straw2 # 使用 straw2 算法
hash 0 # rjenkins1
item osd.0 weight 0.010 # 此 bucket 含有的 OSD 及其权重,权重一般根据容量来定,如 1T 等于 1 权重
}
host node-2 {
id -5 # do not change unnecessarily
id -6 class hdd # do not change unnecessarily
# weight 0.010
alg straw2
hash 0 # rjenkins1
item osd.1 weight 0.010
}
host node-3 {
id -7 # do not change unnecessarily
id -8 class hdd # do not change unnecessarily
# weight 0.010
alg straw2
hash 0 # rjenkins1
item osd.2 weight 0.010
}
# 根 bucket ,至少有一个,是 placement rule 的入口。
root default {
id -1 # do not change unnecessarily
id -2 class hdd # do not change unnecessarily
# weight 0.029
alg straw2
hash 0 # rjenkins1
item node-1 weight 0.010 # 此 bucket 含有3个子 bucket,每个权重0.01
item node-2 weight 0.010
item node-3 weight 0.010
}
# rules
# placement rule。注意:crushmap 只有一个,但可以定义多条 rule
rule replicated_rule {
id 0 # id
type replicated # 类型 [replicated|erasure]
min_size 1 # 如果池副本数小于这个数值,就不会应用这条rule
max_size 10 # 如果池副本数大于这个数值,就不会应用这条rule
step take default # crush规则的入口,一般是类型为root的bucket
step choose firstn 0 type osd # 分为choose和chooseleaf两种,num代表选择的数量,type是预期的bucket类型。
step emit # 输出结果
}
# end crush map
编译 CRUSH map
crushtool -c {decomplied-crush-map-filename} -o {complied-crush-map-filename}
模拟测试
可以对编译过的 CRUSH map 进行模拟测试。
min-x:最小输入值。输入对象名称模拟为[min, max]的数字。
max-x:最大输入值。
num-rep:副本数,输出 osd 个数等于副本数
ruleset:rule id。选择哪一条 replacement rule。
crushtool -i {complied-crush-map-filename} --test --min-x 0 --max-x 9 --num-rep 3 --ruleset 0 --show_mappings
[root@node-1 ~]# crushtool -i crushmap --test --min-x 0 --max-x 9 --num-rep 3 --ruleset 0 --show_mappings
CRUSH rule 0 x 0 [1,2,0]
CRUSH rule 0 x 1 [2,0,1]
CRUSH rule 0 x 2 [2,1,0]
CRUSH rule 0 x 3 [0,1,2]
CRUSH rule 0 x 4 [1,2,0]
CRUSH rule 0 x 5 [0,1,2]
CRUSH rule 0 x 6 [2,0,1]
CRUSH rule 0 x 7 [1,2,0]
CRUSH rule 0 x 8 [2,0,1]
CRUSH rule 0 x 9 [1,2,0]
或者仅统计结果分布。
crushtool -i {complied-crush-map-filename} --test --min-x 0 --max-x 10000 --num-rep 3 --ruleset 0 --show_utilization
[root@node-1 ~]# crushtool -i crushmap --test --min-x 0 --max-x 10000 --num-rep 3 --ruleset 0 --show_utilization
rule 0 (replicated_rule), x = 0..10000, numrep = 3..3
rule 0 (replicated_rule) num_rep 3 result size == 3: 10001/10001
device 0: stored : 10001 expected : 10001
device 1: stored : 10001 expected : 10001
device 2: stored : 10001 expected : 10001
注入集群
CRUSH map 需要注入集群后,才能生效.
ceph osd setcrushmap -i {complied-crushmap-filename}
[root@node-1 ~]# ceph osd setcrushmap -i crushmap
8
编写一个主副本在 SSD,其他副本在 HDD 的 CRUSH map
如何写出主副本始终分配到 SSD 设备,从副本分配到 HDD 设备呢?
首先要了解,主副本在 CRUSH map 中,实际上是第一个被计算出的 device,从副本是随后计算出的 device。只要保证每次第一个 emit 中输出的为 SSD 类型,就可以始终让主副本在 SSD 设备上。
接下来以一个例子介绍。
这里给出设备分布图,有三台主机,每台主机上有1个 SSD 和2个 HDD 类型的 OSD。
root
_ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _
| | |
node-1 node-2 node-3
_ _ _ _|_ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _|_ _ _ _
| | | | | | | | |
osd.0 osd.1 osd.2 osd.3 osd.4 osd.5 osd.6 osd.7 osd.8
SSD HDD HDD SSD HDD HDD SSD HDD HDD
编写
首先修改 device。
# 修改 device
device 0 osd.0 class SSD
device 1 osd.1 class HDD
device 2 osd.2 class HDD
device 3 osd.3 class SSD
device 4 osd.4 class HDD
device 5 osd.5 class HDD
device 6 osd.6 class SSD
device 7 osd.7 class HDD
device 8 osd.8 class HDD
然后,修改cluester map,把 SSD 归为一组。考虑到物理隔离域,把 HDD 根据主机不同分为三组。
# buckets
# SSD 化为一组
host node-SSD {
id -1 # do not change unnecessarily
alg straw2
hash 0 # rjenkins1
item osd.0 weight 0.010 # 权重可以根据硬盘容量调整,这里认为
item osd.3 weight 0.010
item osd.6 weight 0.010
}
# HDD 根据主机化为三组
host node-1-HDD {
id -2 # do not change unnecessarily
alg straw2
hash 0 # rjenkins1
item osd.1 weight 0.1
item osd.2 weight 0.1
}
host node-2-HDD {
id -3 # do not change unnecessarily
alg straw2
hash 0 # rjenkins1
item osd.4 weight 0.1
item osd.5 weight 0.1
}
host node-3-HDD {
id -4 # do not change unnecessarily
alg straw2
hash 0 # rjenkins1
item osd.7 weight 0.1
item osd.8 weight 0.1
}
接着,给出入口。这里有两个入口:SSD 节点和 HDD 节点。
注:root 和 host 并无本质区别,都属于 type。host 也可做为 入口 。
# root bucket
root root-SSD {
id -5 # do not change unnecessarily
alg straw2
hash 0 # rjenkins1
item node-SSD weight 0.030 # 注意了,权重等于 node-SSD 内三个 item 权重之和
}
root root-HDD {
id -6 # do not change unnecessarily
alg straw2
hash 0 # rjenkins1
item node-1-HDD weight 0.2 # 注意了,权重等于 node-x-HDD 内三个 item 权重之和
item node-2-HDD weight 0.2
item node-3-HDD weight 0.2
}
placement rule,策略应该注意先输出 SSD 在输出 HDD。
# rules
rule ssd-primary {
id 1
type replicated
min_size 1
max_size 10
step take root-SSD
step chooseleaf firstn 1 type host
step emit
step take root-HDD
step chooseleaf firstn -1 type host # -1 表示选出(副本数-1)个host,3副本的话,这里就是2个host
step emit
}
测试
[root@node-1 ~]# vi mycrushmap.txt
[root@node-1 ~]# crushtool -c mycrushmap.txt -o mycrushmap
[root@node-1 ~]# crushtool -i mycrushmap --test --min-x 0 --max-x 9 --num-rep 3 --ruleset 1 --s
how_mappings
CRUSH rule 1 x 0 [0,7,1]
CRUSH rule 1 x 1 [3,2,5]
CRUSH rule 1 x 2 [0,8,4]
CRUSH rule 1 x 3 [0,8,4]
CRUSH rule 1 x 4 [3,1,8]
CRUSH rule 1 x 5 [3,7,4]
CRUSH rule 1 x 6 [6,7,4]
CRUSH rule 1 x 7 [6,1,8]
CRUSH rule 1 x 8 [6,2,5]
CRUSH rule 1 x 9 [6,8,1]
测试结果显示所有的主 OSD,即第一个 OSD 都为 SSD 。说明此 CRUSH map 符合预设。
附录
给出完整的编译后的 CRUSH map。
[root@node-1 ~]# vi mycrushmap.txt
device 0 osd.0 class SSD
device 1 osd.1 class HDD
device 2 osd.2 class HDD
device 3 osd.3 class SSD
device 4 osd.4 class HDD
device 5 osd.5 class HDD
device 6 osd.6 class SSD
device 7 osd.7 class HDD
device 8 osd.8 class HDD
type 0 osd
type 1 host
type 11 root
host node-SSD {
id -1 # do not change unnecessarily
alg straw2
hash 0 # rjenkins1
item osd.0 weight 0.010
item osd.3 weight 0.010
item osd.6 weight 0.010
}
host node-1-HDD {
id -2 # do not change unnecessarily
alg straw2
hash 0 # rjenkins1
item osd.1 weight 0.1
item osd.2 weight 0.1
}
host node-2-HDD {
id -3 # do not change unnecessarily
alg straw2
hash 0 # rjenkins1
item osd.4 weight 0.1
item osd.5 weight 0.1
}
host node-3-HDD {
id -4 # do not change unnecessarily
alg straw2
hash 0 # rjenkins1
item osd.7 weight 0.1
item osd.8 weight 0.1
}
root root-SSD {
id -5 # do not change unnecessarily
alg straw2
hash 0 # rjenkins1
item node-SSD weight 0.030
}
root root-HDD {
id -6 # do not change unnecessarily
alg straw2
hash 0 # rjenkins1
item node-1-HDD weight 0.2
item node-2-HDD weight 0.2
item node-3-HDD weight 0.2
}
rule ssd-primary {
id 1
type replicated
min_size 1
max_size 10
step take root-SSD
step chooseleaf firstn 1 type host
step emit
step take root-HDD
step chooseleaf firstn -1 type host
step emit
}
CRUSH 其他命令
查看 device map
将 devie map 树形结构以深度优先遍历的方式全部打印出来。
ceph osd tree
[root@node-1 ~]# ceph osd tree
ID CLASS WEIGHT TYPE NAME STATUS REWEIGHT PRI-AFF
-1 0.03918 root default
-3 0.01959 host node-1
0 hdd 0.00980 osd.0 up 1.00000 1.00000
3 hdd 0.00980 osd.3 up 1.00000 1.00000
-5 0.00980 host node-2
1 hdd 0.00980 osd.1 up 1.00000 1.00000
-7 0.00980 host node-3
2 hdd 0.00980 osd.2 up 1.00000 1.00000
查看 placement rule
查看 CLUSH map 中有哪些 rule,以及每个 rule 的具体步骤。
# 查询所有的 rule
ceph osd crush rule ls
# 查询指定 rule 的具体步骤
ceph osd crush rule dump
[root@node-1 ~]# ceph osd crush rule ls
replicated_rule
[root@node-1 ~]# ceph osd crush rule dump
[
{
"rule_id": 0,
"rule_name": "replicated_rule",
"ruleset": 0,
"type": 1,
"min_size": 1,
"max_size": 10,
"steps": [
{
"op": "take",
"item": -1,
"item_name": "default"
},
{
"op": "chooseleaf_firstn",
"num": 0,
"type": "host"
},
{
"op": "emit"
}
]
}
]
数据重平衡
尽管 CRUSH 算法在设计上尽可能的注意到数据的平衡分布,但是在实际生产中,随着集群存储数据的不断增加和集群设备的变动,这种平衡将必定被打破。
针对这类问题,Ceph 提供了一系列的工具来使得数据重新分配,回到相对平衡的位置。
查看集群空间使用率
ceph df 可以大致查看集群的设备和存储池的空间使用情况。
RAW STORAGE类型下,可以看到 USED 比 RAW USED 小,USED 为用户实际数据存储量,RAW USED 为总的使用量(包括集群自生元数据)。
而 POOLS中,STORED是用户认为的使用量,USED 是集群实际的空间使用量,后者大约是前者的3倍。这是因为 Ceph 使用三副本存储数据,每一份数据要使用三份的存储空间。
[root@node-1 ceph-deploy]# ceph df
RAW STORAGE:
类型 大小 可用空间 已用空间 总使用空间 总使用百分比
CLASS SIZE AVAIL USED RAW USED %RAW USED
hdd 40 GiB 35 GiB 1.3 GiB 5.3 GiB 13.17
TOTAL 40 GiB 35 GiB 1.3 GiB 5.3 GiB 13.17
POOLS:
池名称 id 数据量 对象数量
POOL ID STORED OBJECTS USED %USED MAX AVAIL
pool-1 1 376 MiB 95 1.1 GiB 3.30 11 GiB
rbd-pool 2 22 MiB 17 67 MiB 0.20 11 GiB
ceph osd df tree 可以详细查看每个设备的空间使用情况。
VAR = 当前 OSD 使用率 / 集群平均空间使用率。
通过最后一行的 “MIN/MAX VAR: 0.88/1.07”,可以得知 VAR 最大和最小的偏移量,从而判断整个集群分布是否均衡。
# 深度优先遍历,输出 CRUSH map 中所有 bucket 以及 device 的详细信息
[root@node-1 ceph-deploy]# ceph osd df tree
权重 再权重 大小 总使用量 数据总量 元数据总量 剩余量 使用率 比重 pg数
ID CLASS WEIGHT REWEIGHT SIZE RAW USE DATA OMAP META AVAIL %USE VAR PGS STATUS TYPE NAME
-1 0.03918 - 40 GiB 5.3 GiB 1.3 GiB 112 KiB 4.0 GiB 35 GiB 13.17 1.00 - root default
-3 0.01959 - 20 GiB 2.4 GiB 449 MiB 32 KiB 2.0 GiB 18 GiB 12.20 0.93 - host node-1
0 hdd 0.00980 1.00000 10 GiB 1.3 GiB 282 MiB 32 KiB 1024 MiB 8.7 GiB 12.76 0.97 92 up osd.0
3 hdd 0.00980 1.00000 10 GiB 1.2 GiB 167 MiB 0 B 1 GiB 8.8 GiB 11.64 0.88 100 up osd.3
-5 0.00980 - 10 GiB 1.4 GiB 424 MiB 48 KiB 1024 MiB 8.6 GiB 14.14 1.07 - host node-2
1 hdd 0.00980 1.00000 10 GiB 1.4 GiB 424 MiB 48 KiB 1024 MiB 8.6 GiB 14.14 1.07 192 up osd.1
-7 0.00980 - 10 GiB 1.4 GiB 424 MiB 32 KiB 1024 MiB 8.6 GiB 14.14 1.07 - host node-3
2 hdd 0.00980 1.00000 10 GiB 1.4 GiB 424 MiB 32 KiB 1024 MiB 8.6 GiB 14.14 1.07 192 up osd.2
TOTAL 40 GiB 5.3 GiB 1.3 GiB 113 KiB 4.0 GiB 35 GiB 13.17
MIN/MAX VAR: 0.88/1.07 STDDEV: 1.05
# 仅输出 osd 的详细信息
[root@node-1 ~]# ceph osd df
ID CLASS WEIGHT REWEIGHT SIZE RAW USE DATA OMAP META AVAIL %USE VAR PGS STATUS TYPE NAME
-1 0.03918 - 40 GiB 5.3 GiB 1.3 GiB 96 KiB 4.0 GiB 35 GiB 13.18 1.00 - root default
-3 0.01959 - 20 GiB 2.4 GiB 452 MiB 64 KiB 2.0 GiB 18 GiB 12.21 0.93 - host node-1
0 hdd 0.00980 1.00000 10 GiB 1.3 GiB 283 MiB 48 KiB 1024 MiB 8.7 GiB 12.77 0.97 92 up osd.0
3 hdd 0.00980 1.00000 10 GiB 1.2 GiB 169 MiB 16 KiB 1024 MiB 8.8 GiB 11.65 0.88 100 up osd.3
-5 0.00980 - 10 GiB 1.4 GiB 425 MiB 16 KiB 1024 MiB 8.6 GiB 14.16 1.07 - host node-2
1 hdd 0.00980 1.00000 10 GiB 1.4 GiB 425 MiB 16 KiB 1024 MiB 8.6 GiB 14.16 1.07 192 up osd.1
-7 0.00980 - 10 GiB 1.4 GiB 425 MiB 16 KiB 1024 MiB 8.6 GiB 14.16 1.07 - host node-3
2 hdd 0.00980 1.00000 10 GiB 1.4 GiB 425 MiB 16 KiB 1024 MiB 8.6 GiB 14.16 1.07 192 up osd.2
TOTAL 40 GiB 5.3 GiB 1.3 GiB 97 KiB 4.0 GiB 35 GiB 13.18
MIN/MAX VAR: 0.88/1.07 STDDEV: 1.05
Ceph 的三副本的空间利用率在实际中比 33% 还要小,因为还需要预留出一部分空间用于 Ceph 集群的正常工作,所以其空间利用率的均值大约在 23%【《Ceph rados 设计原理与实现》】。replicated 策略实在是太糟糕了,吃了一碗粉,却要付三碗的钱。
可以通过 mon osd full ratio 和 mon osd nearfull ratio 来设置集群空间使用量。mon osd full ratio 默认0.95,在达到95%的数据写入量后,此 OSD 将禁止读写数据(注意,读写都不行)。mon osd nearfull ratio 默认0.85,达到85%的空间使用率后,将产生警告。
需要注意的是这两个设置只适合写在配置文件中,用于一开始创建 Ceph 集群的时候。
[global]
mon_osd_full_ratio = .80
mon_osd_backfillfull_ratio = .75
mon_osd_nearfull_ratio = .70
可以通过 ceph osd set-nearfull-ratio 和 set-full-ratio 命令来在运行过程中修改这两个比率值。
[root@node-1 ceph-deploy]# ceph osd set-full-ratio 0.98
osd set-full-ratio 0.98
[root@node-1 ceph-deploy]# ceph osd dump | grep full
full_ratio 0.98
backfillfull_ratio 0.9
nearfull_ratio 0.85
[root@node-1 ceph-deploy]# ceph osd set-nearfull-ratio 0.95
osd set-nearfull-ratio 0.95
[root@node-1 ceph-deploy]# ceph osd dump | grep full
full_ratio 0.98
backfillfull_ratio 0.9
nearfull_ratio 0.95
# 注意:在调整参数时,要注意和其他参数的限制关系。
[root@node-1 ~]# ceph osd dump | grep full
full_ratio 0.98
backfillfull_ratio 0.9
nearfull_ratio 0.95
[root@node-1 ~]# ceph health detail
HEALTH_ERR full ratio(s) out of order
OSD_OUT_OF_ORDER_FULL full ratio(s) out of order
backfillfull_ratio (0.9) < nearfull_ratio (0.95), increased
osd_failsafe_full_ratio (0.97) < full_ratio (0.98), increased
此命令通常用于集群满状态后,临时修改配置文件,使得 OSD 可以读写,然后通过扩容、删除、重平衡等方式使得集群恢复健康状态。这里贴出一个相关的运维链接:
http://strugglesquirrel.com/2019/02/02/ceph%E8%BF%90%E7%BB%B4%E5%A4%A7%E5%AE%9D%E5%89%91%E4%B9%8B%E9%9B%86%E7%BE%A4osd%E4%B8%BAfull/
reweight
CRUSH 算法在理论上平衡效果很好,但实际生产环境情况复杂多变,因此它的平衡效果不尽如人意。而 reweight 就是专门为 CRUSH 设计的补偿措施,通过调整 reweight ,可以在 straw2 计算完成后,再进行一次过载测试,只有通过了过载测试,才算真正被选中。reweight 的值越大,通过测试的概率越高,最大值为1,默认值为1。并且,它还支持对已经分配的 pg 进行重新计算,如果发现 osd 映射结果发生了变化,会动态迁移到新的 osd,实现集群运行时的数据重平衡。
《Ceph rados 设计原理与实现》 中还提到了过载测试的另一个好处:“可以对 OSD 暂时失效和 OSD 永久被删除的场景进行区分。区分这两者的意义在于:如果 OSD 暂时失效,可以通过将其 reweight 调整为0,从而利用过载测试将其从候选条目中淘汰,进而将其承载的数据迁移至其他 OSD,这样后续该 OSD 正常回归时,将其 reweight 重新调整为原来的数值,即可使得原本属于该 OSD 的数据全部回归。而删除操作则会使得 OSD 在 cluster map 中的唯一编号发生改变,所以可能承载不同的数据。这样数据迁移量更大”。
可以使用下列命令,调整每个 OSD 的 reweight。reweight 在程序中会被放大10000倍,即[0-10000]。
ceph osd reweight
# 通过调整 osd.3 的reweight 把它的所有 pg 都迁移到别的 osd 上。
[root@node-1 ~]# ceph osd reweight osd.3 0
reweighted osd.3 to 0 (0)
[root@node-1 ~]# ceph osd df
ID CLASS WEIGHT REWEIGHT SIZE RAW USE DATA OMAP META AVAIL %USE VAR PGS STATUS
0 hdd 0.00980 1.00000 10 GiB 1.3 GiB 319 MiB 48 KiB 1024 MiB 8.7 GiB 13.12 0.95 192 up
3 hdd 0.00980 0 0 B 0 B 0 B 0 B 0 B 0 B 0 0 0 up
1 hdd 0.00980 1.00000 10 GiB 1.4 GiB 425 MiB 16 KiB 1024 MiB 8.6 GiB 14.16 1.03 192 up
2 hdd 0.00980 1.00000 10 GiB 1.4 GiB 425 MiB 16 KiB 1024 MiB 8.6 GiB 14.16 1.03 192 up
TOTAL 40 GiB 5.3 GiB 1.3 GiB 97 KiB 4.0 GiB 35 GiB 13.81
MIN/MAX VAR: 0.95/1.03 STDDEV: 0.49
# 通过调整 osd.3 reweight 回到原本数值,pg 又迁移回去
[root@node-1 ~]# ceph osd reweight osd.3 1
reweighted osd.3 to 1 (10000)
[root@node-1 ~]# ceph osd df
ID CLASS WEIGHT REWEIGHT SIZE RAW USE DATA OMAP META AVAIL %USE VAR PGS STATUS
0 hdd 0.00980 1.00000 10 GiB 1.3 GiB 283 MiB 48 KiB 1024 MiB 8.7 GiB 12.80 0.97 92 up
3 hdd 0.00980 1.00000 10 GiB 1.2 GiB 169 MiB 16 KiB 1024 MiB 8.8 GiB 11.66 0.88 100 up
1 hdd 0.00980 1.00000 10 GiB 1.4 GiB 425 MiB 16 KiB 1024 MiB 8.6 GiB 14.16 1.07 192 up
2 hdd 0.00980 1.00000 10 GiB 1.4 GiB 425 MiB 16 KiB 1024 MiB 8.6 GiB 14.16 1.07 192 up
TOTAL 40 GiB 5.3 GiB 1.3 GiB 97 KiB 4.0 GiB 35 GiB 13.19
MIN/MAX VAR: 0.88/1.07 STDDEV: 1.05
也可以批量调整,目前有两种模式:1. 按 OSD 当前空间使用率(reweight-by-utilization),2. 按 PG 在 OSD 之间的分布(reweight-by-pg)。可以在这两条命令前加上“test-”,测试执行该命令会产生的结果。
# 参数说明
# overload:当且仅当某个 OSD 的空间使用率大于等于集群平均空间使用率的 overload/100 时,调整reweight。取值范围:>100,默认120
# max_change:每次调整reweight 的最大幅度。取值范围:[0,1],默认0.05
# max_osds:每次最多调整 OSD 数量。默认4
# --no-increasing:表示不允许 reweitght 上调,只允许下调。注意 reweight范围 [0,1]
ceph osd {overload} {max_change} {max_osds} {--no-increasing}
[root@node-1 ~]# ceph osd test-reweight-by-utilization 101
no change
moved 22 / 576 (3.81944%)
avg 144
stddev 48.0833 -> 42.9535 (expected baseline 10.3923)
min osd.0 with 92 -> 92 pgs (0.638889 -> 0.638889 * mean)
max osd.1 with 192 -> 182 pgs (1.33333 -> 1.26389 * mean)
oload 101
max_change 0.05
max_change_osds 4
average_utilization 0.1319
overload_utilization 0.1333
osd.2 weight 1.0000 -> 0.9500
osd.1 weight 1.0000 -> 0.9500
[root@node-1 ~]# ceph osd test-reweight-by-pg 101
no change
moved 22 / 576 (3.81944%)
avg 144
stddev 48.0833 -> 42.9535 (expected baseline 10.3923)
min osd.0 with 92 -> 92 pgs (0.638889 -> 0.638889 * mean)
max osd.1 with 192 -> 182 pgs (1.33333 -> 1.26389 * mean)
oload 101
max_change 0.05
max_change_osds 4
average_utilization 14699.6636
overload_utilization 14846.6602
osd.2 weight 1.0000 -> 0.9500
osd.1 weight 1.0000 -> 0.9500
需要注意的是,如果 OSD 的 reweight 已经为1了,那么他将不能调的更大,只能通过调小所有其他的 OSD 的 reweight 来凸显它的值变大。这很不方便,在调整过程中,也会导致集群迁移频繁。因此需要在刚刚建立好集群的时候,就适当调小 OSD 的 reweight,为以后的运维留下裕度空间。
weight set
straw2 算法中一个关键的参数是输入变量的权重,对于 Ceph 来说,每次 CRUSH 算法计算出的是一组 OSD(如果副本数为 3,则是 3 个 OSD),这一组 OSD 是有先后顺序的。因此,通过 CRUSH 选择不同位置的 OSD 时,每个 OSD 呈现的概率都应该有所不同。weight set 就是针对每个 OSD 在不同位置时,呈现出不同的权重特性。
支持两种模式:
- 兼容模式:和原本的 weight 相同,只有一个数字表示自身权重。
- 非兼容模式:weight-set 和具体的存储池绑定,可以为每个位置设置权重。
# 创建一个兼容模式的 weight set
ceph osd crush weight-set create-compat
# 单独调整每个 OSD 的权重
ceph osd crush weight-set reweight-compat {name} {weight}
# 删除兼容模式的 weight set
ceph osd crush weight-set rm-compat
# 创建非兼容模式的 weight set
# flat:效果和兼容模式相同,每个 OSD 的 weight-set 只有1个参数
# positional:根据副本个数以及副本当前所处的位置,为每个 OSD 设置一组权重。
ceph osd crush weight-set create flat|positional
# 删除
ceph osd crush weight-set rm
[root@node-1 ~]# ceph osd set-require-min-compat-client luminous
set require_min_compat_client to luminous
[root@node-1 ~]# ceph osd crush weight-set create pool-1 positional
[root@node-1 ~]# ceph osd crush weight-set reweight pool-1 osd.0 0.3 0.4 0.5
[root@node-1 ~]# ceph osd crush dump
...
{
"bucket_id": -4,
"weight_set": [
[
0.29998779296875,
0.009796142578125
],
[
0.399993896484375,
0.009796142578125
],
[
0.5,
0.009796142578125
]
]
},
...
upmap
前面介绍的 reweight 和 weight-set 都只能提高或者降低 OSD 被选中的概率,而 upmap 可以直接选中某个 OSD 作为输出结果。默认 upmap 等于 activemap(计算出的结果)。
有两种替换方式:
-
指定某个 PG 的计算结果
ceph osd pg-upmap
[ ...] # 查看 pg 映射 [root@node-1 ~]# ceph pg dump | awk '{print $1, $17}' ... PG_STAT UP_PRIMARY 1.7f [2,3,1] 1.7e [1,0,2] 1.7d [3,2,1] 1.7c [0,2,1] 1.7b [1,0,2] ... # 设置的副本个数必须大于等于 pool min size [root@node-1 ~]# ceph osd pg-upmap 1.7f 1 Error EINVAL: num of osds (1) < pool min size (2) [root@node-1 ~]# ceph osd pg-upmap 1.7f 0 0 0 osd.0 already exists, osd.0 already exists, set 1.7f pg_upmap mapping to [0] [root@node-1 ~]# ceph pg dump | awk '{print $1,$17}' | grep 1.7f dumped all 1.7f [0] -
替换 PG 计算结果中的某个 OSD,需要同时指定源 OSD 和目标 OSD
ceph osd pg-upmap-items
[ ...] # 在实际操作过程中,发现 primer osd 无法替换,而且 upmap 会自动恢复成原本数组。 [root@node-1 ~]# ceph osd pg-upmap-items 1.7f 3 0 set 1.7f pg_upmap_items mapping to [3->0] [root@node-1 ~]# ceph pg dump | awk '{print $1,$17}' | grep 1.7f dumped all 1.7f [2,0,1] # 过一会,再查看,发现变回原来数组,balancer自动调整 [root@node-1 ~]# ceph pg dump | awk '{print $1,$17}' | grep 1.7f dumped all 1.7f [2,3,1]
删除 upmap
ceph osd rm-pg-upmap
ceph osd rm-pg-upmap-items
balancer
balancer 是 Ceph 自动性的重平衡工具,其主要依赖 reweigth、weight set 和 upmap 工具实现。
检查状态
ceph balancer status
# 参数说明
# last_optimize_duration:上一次优化过程持续时间
# plans:优化任务
# mode:指执行计划时,默认选择的工具和手段,当前支持 crush-compat、upmap、none
# active:是否启用 balancer
# optimize_result:优化结果
# last_optimize_started:上一次优化时间
[root@node-1 ~]# ceph balancer status
{
"last_optimize_duration": "0:00:00.000220",
"plans": [],
"mode": "none",
"active": true,
"optimize_result": "Please do \"ceph balancer mode\" to choose a valid mode first",
"last_optimize_started": "Wed Jun 23 15:58:21 2021"
}
开启|关闭
ceph balancer on
ceph balancer off
通过 target_max_misplaced_ratio 参数调整平衡操作时,每次 PG 移动的比率
ceph config set mgr target_max_misplaced_ratio .07 # 7%
设置 balancer 自动调整的间隔时间,默认 60s,注意斜杠不要省略,不要分成3条命令输入
ceph config set mgr mgr/balancer/sleep_interval 60
设置 balancer 一天中的开始和结束时间,避开正常业务的高峰时段,默认全天都进行,时间格式为 HHMM
ceph config set mgr mgr/balancer/begin_time 0000
ceph config set mgr mgr/balancer/end_time 2400
设置 balancer 一周中的开始和结束时间,默认一整周,注意 0 或 7 为周日,1为周一,以此类推
ceph config set mgr mgr/balancer/begin_weekday 0
ceph config set mgr mgr/balancer/end_weekday 7
设置 balancer 平衡操作针对的存储池
ceph config set mgr mgr/balancer/pool_ids 1,2,3
调整 mode
ceph balancer mode crush-compat
ceph balancer mode upmap
ceph balancer mode none # 相当于关闭 balancer
生成一个 plan,注意:没有 plan 的 balancer 是不会执行任何操作的
ceph balancer optimize { [...]}
执行 plan
ceph balancer execute
查询所有的 plan
ceph balancer ls
删除 plan
ceph balancer rm
对当前集群的平衡状态进行评估,数字越小越好
ceph balancer eval {pool}
查看评估的详细信息
ceph balancer eval-verbose {pool}
对一个 plan 进行评估
ceph balancer eval
如何为集群创建一个 blancer 计划
一般不需要手动建立计划,开启 balancer 并选择 mode后,balancer 会定时自动的完成优化工作。
# 首先关闭 balancer
[root@node-1 ~]# ceph balancer off
# 为 balancer 选择一个模式
[root@node-1 ~]# ceph balancer mode upmap
# 实验的集群已经很平衡了,直接告诉我不能继续优化
[root@node-1 ~]# ceph balancer optimize myplan
Error EALREADY: Unable to find further optimization, or pool(s) pg_num is decreasing, or distribution is already perfect
# 手动调整 reweight,构建一个不平衡的集群,注意 0 是指停用,这里reweight 如若调整为0,集群依然是完美平衡。
[root@node-1 ~]# ceph osd reweight osd.3 0.1
reweighted osd.3 to 0.1 (1999)
[root@node-1 ~]# ceph osd df
ID CLASS WEIGHT REWEIGHT SIZE RAW USE DATA OMAP META AVAIL %USE VAR PGS STATUS
0 hdd 0.00980 1.00000 10 GiB 1.4 GiB 427 MiB 48 KiB 1024 MiB 8.6 GiB 14.18 1.07 179 up
3 hdd 0.00980 0.09999 10 GiB 1.0 GiB 33 MiB 16 KiB 1024 MiB 9.0 GiB 10.35 0.78 13 up
1 hdd 0.00980 1.00000 10 GiB 1.4 GiB 431 MiB 16 KiB 1024 MiB 8.6 GiB 14.22 1.07 192 up
2 hdd 0.00980 1.00000 10 GiB 1.4 GiB 431 MiB 16 KiB 1024 MiB 8.6 GiB 14.22 1.07 192 up
TOTAL 40 GiB 5.3 GiB 1.3 GiB 97 KiB 4.0 GiB 35 GiB 13.24
MIN/MAX VAR: 0.78/1.07 STDDEV: 1.08
# 新建计划
[root@node-1 ~]# ceph balancer optimize myplan
# 评估计划
[root@node-1 ~]# ceph balancer eval myplan
plan myplan final score 0.017154 (lower is better)
# 查看集群当前分数,发现优化有一点点效果,我们这里选择执行计划
[root@node-1 ~]# ceph balancer eval
current cluster score 0.017850 (lower is better)
# 执行计划
[root@node-1 ~]# ceph balancer execute myplan
[root@node-1 ~]# ceph balancer eval
current cluster score 0.017154 (lower is better)
[root@node-1 ~]# ceph osd df
ID CLASS WEIGHT REWEIGHT SIZE RAW USE DATA OMAP META AVAIL %USE VAR PGS STATUS
0 hdd 0.00980 1.00000 10 GiB 1.4 GiB 408 MiB 48 KiB 1024 MiB 8.6 GiB 13.99 1.06 180 up
3 hdd 0.00980 0.09999 10 GiB 1.1 GiB 57 MiB 16 KiB 1024 MiB 8.9 GiB 10.59 0.80 12 up
1 hdd 0.00980 1.00000 10 GiB 1.4 GiB 432 MiB 16 KiB 1024 MiB 8.6 GiB 14.22 1.07 192 up
2 hdd 0.00980 1.00000 10 GiB 1.4 GiB 432 MiB 16 KiB 1024 MiB 8.6 GiB 14.22 1.07 192 up
TOTAL 40 GiB 5.3 GiB 1.3 GiB 97 KiB 4.0 GiB 35 GiB 13.25
MIN/MAX VAR: 0.80/1.07 STDDEV: 1.00
PG 相关命令
获取 PG 详细信息
[root@node-1 ~]# ceph pg dump_json sum
dumped all
version 406
stamp 2021-06-24 09:24:46.705278
last_osdmap_epoch 0
last_pg_scan 0
PG_STAT OBJECTS MISSING_ON_PRIMARY DEGRADED MISPLACED UNFOUND BYTES OMAP_BYTES* OMAP_KEYS* LOG DISK_LOG STATE STATE_STAMP VERSION REPORTED UP UP_PRIMARY ACTING ACTING_PRIMARY LAST_SCRUB SCRUB_STAMP LAST_DEEP_SCRUB DEEP_SCRUB_STAMP SNAPTRIMQ_LEN
1.7f 0 0 0 0 0 0 0 0 0 0 active+clean 2021-06-24 09:11:24.543912 0'0 275:687 [2,1,0] 2 [2,1,0] 2 0'0 2021-06-23 14:51:08.406526 0'0 2021-06-22 09:31:39.906375 0
1.7e 1 0 0 0 0 4194304 0 0 2 2 active+clean 2021-06-24 09:11:24.318962 50'2 275:606 [1,0,2] 1 [1,0,2] 1 50'2 2021-06-23 14:37:05.824343 50'2 2021-06-23 14:37:05.824343 0
1.7d 0 0 0 0 0 0 0 0 0 0 active+clean 2021-06-24 09:11:22.895867 0'0 275:36 [0,2,1] 0 [0,2,1] 0 0'0 2021-06-23 12:21:07.406368 0'0 2021-06-22 09:31:09.128962 0
...
查询每个 OSD 上 PG 总数
[root@node-1 ~]# ceph pg dump osds
dumped osds
OSD_STAT USED AVAIL USED_RAW TOTAL HB_PEERS PG_SUM PRIMARY_PG_SUM
3 59 MiB 8.9 GiB 1.1 GiB 10 GiB [0,1,2] 12 4
2 433 MiB 8.6 GiB 1.4 GiB 10 GiB [0,1,3] 192 75
1 433 MiB 8.6 GiB 1.4 GiB 10 GiB [0,2,3] 192 62
0 409 MiB 8.6 GiB 1.4 GiB 10 GiB [1,2,3] 180 51
sum 1.3 GiB 35 GiB 5.3 GiB 40 GiB
查询指定 OSD 上的 PG 的详细信息
[root@node-1 ~]# ceph pg ls-by-osd 0
PG OBJECTS DEGRADED MISPLACED UNFOUND BYTES OMAP_BYTES* OMAP_KEYS* LOG STATE SINCE VERSION REPORTED UP ACTING SCRUB_STAMP DEEP_SCRUB_STAMP
1.1 0 0 0 0 0 0 0 0 active+clean 19m 0'0 275:573 [2,0,1]p2 [2,0,1]p2 2021-06-23 14:37:36.380990 2021-06-22 09:28:39.643688
1.2 1 0 0 0 4194304 0 0 2 active+clean 19m 50'2 275:606 [1,0,2]p1 [1,0,2]p1 2021-06-23 14:44:23.268353 2021-06-23 14:44:23.268353
1.3 0 0 0 0 0 0 0 0 active+clean 19m 0'0 275:472 [1,2,0]p1 [1,2,0]p1 2021-06-23 10:20:09.588889 2021-06-23 10:20:09.588889
...
查询指定 pool 上的 PG 的详细信息
[root@node-1 ~]# ceph pg ls-by-pool pool-1
PG OBJECTS DEGRADED MISPLACED UNFOUND BYTES OMAP_BYTES* OMAP_KEYS* LOG STATE SINCE VERSION REPORTED UP ACTING SCRUB_STAMP DEEP_SCRUB_STAMP
1.0 1 0 0 0 4194304 0 0 2 active+clean 22m 50'2 275:715 [1,2,3]p1 [1,2,3]p1 2021-06-23 12:56:25.914554 2021-06-22 09:29:38.155739
1.1 0 0 0 0 0 0 0 0 active+clean 22m 0'0 275:573 [2,0,1]p2 [2,0,1]p2 2021-06-23 14:37:36.380990 2021-06-22 09:28:39.643688
1.2 1 0 0 0 4194304 0 0 2 active+clean 22m 50'2 275:606 [1,0,2]p1 [1,0,2]p1 2021-06-23 14:44:23.268353 2021-06-23 14:44:23.268353
1.3 0 0 0 0 0 0 0 0 active+clean 22m 0'0 275:472 [1,2,0]p1 [1,2,0]p1 2021-06-23 10:20:09.588889 2021-06-23 10:20:09.588889
...
查询 PG 上对象数量
[root@node-1 ~]# ceph pg dump | awk '{print $1, $2}'
dumped all
version 760
stamp 2021-06-24
last_osdmap_epoch 0
last_pg_scan 0
PG_STAT OBJECTS
1.7f 0
1.7e 1
1.7d 0
1.7c 1
1.7b 2
1.7a 1
1.79 1
1.78 2
1.77 1
查询指定 OSD 上对象数量
[root@node-1 ~]# ceph pg ls-by-osd 0 | awk 'BEGIN{sum = 0;}{sum+=$2}END {print "objects: " sum}'
objects: 106
查询指定 pool 上对象数量
注意啦,这个是包含了副本对象的,实际对象可能只有 1/3 不到。
[root@node-1 ~]# ceph df
RAW STORAGE:
CLASS SIZE AVAIL USED RAW USED %RAW USED
hdd 40 GiB 35 GiB 1.3 GiB 5.3 GiB 13.26
TOTAL 40 GiB 35 GiB 1.3 GiB 5.3 GiB 13.26
POOLS:
POOL ID STORED OBJECTS USED %USED MAX AVAIL
pool-1 1 376 MiB 95 1.1 GiB 3.30 11 GiB
rbd-pool 2 22 MiB 17 67 MiB 0.20 11 GiB
附录
Monitor commands:
=================
pg cancel-force-backfill [...] restore normal backfill priority of
pg cancel-force-recovery [...] restore normal recovery priority of
pg debug unfound_objects_exist|degraded_pgs_exist show debug info about pgs
pg deep-scrub start deep-scrub on
pg dump {all|summary|sum|delta|pools|osds|pgs|pgs_ show human-readable versions of pg map (only
brief [all|summary|sum|delta|pools|osds|pgs|pgs_brief. valid with plain)
..]}
pg dump_json {all|summary|sum|pools|osds|pgs [all| show human-readable version of pg map in json only
summary|sum|pools|osds|pgs...]}
pg dump_pools_json show pg pools info in json only
pg dump_stuck {inactive|unclean|stale|undersized| show information about stuck pgs
degraded [inactive|unclean|stale|undersized|degraded..
.]} {}
pg force-backfill [...] force backfill of first
pg force-recovery [...] force recovery of first
pg getmap get binary pg map to -o/stdout
pg ls {} { [...]} list pg with specific pool, osd, state
pg ls-by-osd {} { list pg on osd [osd]
[...]}
pg ls-by-pool { [...]} list pg with pool = [poolname]
pg ls-by-primary {} list pg with primary = [osd]
{ [...]}
pg map show mapping of pg to osds
pg repair start repair on
pg repeer force a PG to repeer
pg scrub start scrub on
pg stat show placement group status.