注意
本文档适用于 Ceph 的开发版本。
手动编辑 CRUSH Map
注意
手动编辑 CRUSH map 是一项高级管理员操作。对于大多数安装,CRUSH 更改可以通过 Ceph CLI 实现,不需要手动编辑 CRUSH map。如果您确定在最近的 Ceph 版本中手动编辑是必要的用例,请考虑联系 Ceph 开发者 dev@ceph.io,以便未来的 Ceph 版本不再有此问题。
要编辑现有的 CRUSH map,请执行以下步骤
有关为特定池设置 CRUSH map 规则的详细信息,请参阅设置池值。
获取 CRUSH Map
要获取集群的 CRUSH map,请运行以下形式的命令
ceph osd getcrushmap -o {compiled-crushmap-filename}
Ceph 将编译后的 CRUSH map 输出 (-o) 到您指定的文件名。因为 CRUSH map 是编译形式,所以必须先反编译才能编辑它。
反编译 CRUSH Map
要反编译 CRUSH map,请运行以下形式的命令
crushtool -d {compiled-crushmap-filename} -o {decompiled-crushmap-filename}
重新编译 CRUSH Map
要编译 CRUSH map,请运行以下形式的命令
crushtool -c {decompiled-crushmap-filename} -o {compiled-crushmap-filename}
设置 CRUSH Map
要设置集群的 CRUSH map,请运行以下形式的命令
ceph osd setcrushmap -i {compiled-crushmap-filename}
Ceph 从您指定的文件名加载 (-i) 编译后的 CRUSH map。
章节
CRUSH map 有六个主要部分
tunables: map 顶部的序言描述了任何不属于传统 CRUSH 行为的 tunables。这些可调参数纠正了多年来为改善 CRUSH 行为而进行的旧错误、优化或其他更改。
devices: 设备是存储数据的单个 OSD。
types: 桶
types定义了 CRUSH 层级结构中使用的桶类型。buckets: 桶由存储位置(例如,行、机架、机箱、主机)的层级结构聚合及其分配的权重组成。定义桶
types后,CRUSH map 定义层级结构中的每个节点、其类型以及它包含的设备或其他节点。rules: 规则定义了数据如何在层级结构中的设备上分布的策略。
choose_args:
choose_args是与层级结构相关的替代权重,已调整以优化数据放置。单个choose_argsmap 可以用于整个集群,也可以创建多个choose_argsmap,以便每个 map 都针对特定池进行定制。
CRUSH Map 设备
设备是存储数据的单个 OSD。在此部分中,通常为集群中的每个 OSD 守护程序定义一个设备。设备由 id(一个非负整数)和 name(通常为 osd.N,其中 N 是设备的 id)标识。
设备还可以关联一个 device class:例如,hdd 或 ssd。设备类使得 CRUSH 规则可以定位设备。这意味着设备类允许 CRUSH 规则仅选择与特定特征匹配的 OSD。例如,您可能希望一个 RBD 池仅与 SSD 相关联,而另一个 RBD 池仅与 HDD 相关联。
要查看设备列表,请运行以下命令
ceph device ls
此命令的输出采用以下形式
device {num} {osd.name} [class {class}]
例如
ceph device ls
device 0 osd.0 class ssd
device 1 osd.1 class hdd
device 2 osd.2
device 3 osd.3
在大多数情况下,每个设备都映射到一个相应的 ceph-osd 守护程序。此守护程序可能映射到单个存储设备、一对设备(例如,一个用于数据,一个用于日志或元数据),或者在某些情况下映射到小型 RAID 设备或较大存储设备的分区。
CRUSH Map 桶类型
CRUSH map 中的第二个列表定义了“桶”类型。桶有助于创建节点和叶子的层级结构。节点桶(也称为非叶子桶)通常代表层级结构中的物理位置。节点聚合其他节点或叶子。叶子桶代表 ceph-osd 守护程序及其相应的存储介质。
提示
在 CRUSH 的上下文中,“桶”一词用于指代层级结构中的节点(即位置或物理硬件)。然而,在 RADOS 网关 API 的上下文中,“桶”一词具有不同的含义。
要将桶类型添加到 CRUSH map,请在桶类型列表下创建新行。输入 type,后跟一个唯一的数字 ID 和一个桶名称。按照惯例,只有一个叶子桶类型,它是 type 0;但是,您可以给叶子桶起任何您喜欢的名称(例如:osd、disk、drive、storage)。
# types
type {num} {bucket-name}
例如
# 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 zone
type 10 region
type 11 root
CRUSH Map 桶层级结构
CRUSH 算法根据每设备的权重值在存储设备之间分配数据对象,近似于均匀概率分布。CRUSH 根据您定义的层级集群 map 分布对象及其副本。CRUSH map 代表可用的存储设备和包含它们的逻辑元素。
为了将放置组(PG)映射到跨故障域的 OSD,CRUSH map 在生成的 CRUSH map 的 #types 下定义了桶类型的层级列表。创建桶层级结构的目的 是根据故障域(例如:主机、机箱、机架、配电单元、pod、行、房间和数据中心)来隔离叶子节点。除了代表 OSD 的叶子节点外,层级结构是任意的,您可以根据自己的需求进行定义。
我们建议根据您首选的硬件命名约定调整 CRUSH map,并使用清晰反映物理硬件的桶名称。清晰的命名实践可以使集群管理更容易,并且在 OSD 发生故障(或其他硬件故障)且管理员需要访问物理硬件时,更容易排除故障。
在以下示例中,CRUSH 层级结构具有 osd 叶子桶和两个 host 级别的节点桶,这些节点桶又是 rack 桶的子节点
注意
较高类型编号的 rack 桶聚合较低类型编号的 host 桶,而 host 桶又聚合基本类型 0 的 osd 桶。
由于叶子节点反映了 CRUSH map 开头 #devices 列表下已声明的存储设备,因此无需将它们声明为桶实例。层级结构中倒数第二低的桶类型通常用于聚合设备:通常是容纳存储驱动器的主机。在高密度环境中,单个机箱中通常有多个主机或节点:刀片或双子星。预测机箱故障的潜在后果非常重要。例如,在节点故障时更换机箱期间,机箱的所有主机及其关联的 OSD 都将处于 down 状态,因此不可用。重要的是要避免将数据的多个副本或分片放置在单个此类机箱中,因为在这种情况下,机箱是一个 failure domain。
要声明桶实例,请执行以下操作:指定其类型,为其指定一个唯一的名称(字母数字字符串),为其分配一个表示为负整数的唯一 ID(这是可选的),为其分配一个相对于桶中项目总容量和能力的权重,为其分配一个桶算法(通常是 straw2),并指定桶算法的哈希值(通常是 0,此设置反映了哈希算法 rjenkins1)。一个桶可以有一个或多个项目。项目可能包含节点桶或叶子。项目可能具有反映项目相对权重的权重。
要声明节点桶,请使用以下语法
[bucket-type] [bucket-name] {
id [a unique negative numeric ID]
weight [the relative capacity/capability of the item(s)]
alg [the bucket type: uniform | list | tree | straw | straw2 ]
hash [the hash type: 0 by default]
item [item-name] weight [weight]
}
例如,在上图中,定义了两个主机桶(在下面的声明中称为 node1 和 node2)和一个机架桶(在下面的声明中称为 rack1)。OSD 被声明为主机桶中的项目
host node1 {
id -1
alg straw2
hash 0
item osd.0 weight 1.00
item osd.1 weight 1.00
}
host node2 {
id -2
alg straw2
hash 0
item osd.2 weight 1.00
item osd.3 weight 1.00
}
rack rack1 {
id -3
alg straw2
hash 0
item node1 weight 2.00
item node2 weight 2.00
}
注意
在此示例中,机架桶不包含任何 OSD。相反,它包含较低级别的主机桶,并在项目条目中包含它们的权重总和。
CRUSH Map 规则
CRUSH map 包含用于池的数据放置规则:这些被称为“CRUSH 规则”。默认 CRUSH map 为每个池包含一个规则。如果您运行的是大型集群,您可能会创建许多池,并且每个池都可能有自己的非默认 CRUSH 规则。
注意
在大多数情况下,无需修改默认规则。当创建一个新池时,默认情况下规则将设置为值 0(表示默认 CRUSH 规则,其数字 ID 为 0)。
CRUSH 规则定义了管理数据如何在层级结构中的设备上分布的策略。规则定义了放置以及复制策略或分布策略,允许您精确指定 CRUSH 如何放置数据副本。例如,您可以创建一条规则来选择一对目标进行双向镜像,另一条规则选择两个不同数据中心中的三个目标进行三向复制,还有一条规则用于跨六个存储设备进行纠删码。有关 CRUSH 规则的详细讨论,请参阅 CRUSH - Controlled, Scalable, Decentralized Placement of Replicated Data 的 **第 3.2 节**。
正常的 CRUSH 规则采用以下形式
rule <rulename> {
id [a unique integer ID]
type [replicated|erasure]
step take <bucket-name> [class <device-class>]
step [choose|chooseleaf] [firstn|indep] <N> type <bucket-type>
step emit
}
CRUSH MSR (Multi-Step Retry) 规则是一种独特的 CRUSH 规则类型,它支持重试步骤,并为每个故障域需要多个 OSD 的配置提供更好的支持。MSR 规则采用以下形式
rule <rulename> {
id [a unique integer ID]
type [msr_indep|msr_firstn]
step take <bucket-name> [class <device-class>]
step choosemsr <N> type <bucket-type>
step emit
}
id- 描述:
用于标识规则的唯一整数。
- Purpose:
规则掩码的一个组成部分。
- 类型:
整数
- 必需:
是
- 默认值:
0
type- 描述:
表示规则要强制执行的复制策略类型。msr_firstn 和 msr_indep 是一种独特的下降算法,它支持在规则内重试步骤,因此每个故障域有多个 OSD。
- Purpose:
规则掩码的一个组成部分。
- 类型:
String
- 必需:
是
- 默认值:
replicated- Valid Values:
replicated,erasure,msr_firstn,msr_indep
step take <bucket-name> [class <device-class>]- 描述:
获取一个桶名称并遍历树。如果指定了
device-class参数,则该参数必须与分配给集群中 OSD 的类匹配。只包含属于该类的设备。- Purpose:
规则的一个组成部分。
- 必需:
是
- 示例:
step take data
step choose firstn {num} type {bucket-type}- 描述:
从当前桶中选择给定类型的
num个桶。{num}通常是池中的副本数(换句话说,池大小)。如果
{num} == 0,选择pool-num-replicas个桶(有多少可用的桶就选择多少)。如果
pool-num-replicas > {num} > 0,选择这么多桶。如果
{num} < 0,选择pool-num-replicas - {num}个桶。
- Purpose:
规则的一个组成部分。
- Prerequisite:
跟随
step take或step choose。- 示例:
step choose firstn 1 type row
step chooseleaf firstn {num} type {bucket-type}- 描述:
选择给定类型的一组桶,并从这组桶中的每个桶的子树中选择一个叶子节点(即 OSD)。集合中的桶数通常是池中的副本数(换句话说,池大小)。
如果
{num} == 0,选择pool-num-replicas个桶(有多少可用的桶就选择多少)。如果
pool-num-replicas > {num} > 0,选择这么多桶。如果
{num} < 0,选择pool-num-replicas - {num}个桶。
- Purpose:
规则的一个组成部分。使用
chooseleaf消除了在单独步骤中选择设备的需要。- Prerequisite:
跟随
step take或step choose。- 示例:
step chooseleaf firstn 0 type row
step emit- 描述:
输出堆栈顶部的当前值并清空堆栈。通常用于规则的末尾,但也可以用于在同一规则中从不同的树中选择。
- Purpose:
规则的一个组成部分。
- Prerequisite:
跟随
step choose。- 示例:
step emit
重要
一个 CRUSH 规则可以分配给多个池,但一个池不能有多个 CRUSH 规则。
firstn or indep
- 描述:
确定当 CRUSH map 中项目(OSD)被标记为
down时 CRUSH 使用哪种替换策略。当此规则用于复制池时,使用firstn。当此规则用于纠删码池时,使用indep。假设一个 PG 存储在 OSD 1、2、3、4 和 5 上,然后 OSD 3 发生故障。
在
firstn模式下,CRUSH 只需调整计算以选择 OSD 1 和 2,然后选择 3 并发现 3 已关闭,重试并选择 4 和 5,最后继续选择新的 OSD:OSD 6。最终的 CRUSH 映射转换是 1, 2, 3, 4, 5 → 1, 2, 4, 5, 6。但是,如果您存储的是纠删码池,则上述序列会更改映射到 OSD 4、5 和 6 的数据。
indep模式试图避免这种不必要的后果。在indep模式下,CRUSH 可以预期选择 3,发现 3 已关闭,重试,然后选择 6。最终的 CRUSH 映射转换是 1, 2, 3, 4, 5 → 1, 2, 6, 4, 5。
step choosemsr {num} type {bucket-type}- 描述:
选择 num 个 type bucket-type 的桶。msr_firstn 和 msr_indep 必须使用 choosemsr 而不是 choose 或 chooseleaf。
如果
{num} == 0,选择pool-num-replicas个桶(有多少可用的桶就选择多少)。如果
pool-num-replicas > {num} > 0,选择这么多桶。
- Purpose:
msr_firstn 和 msr_indep 规则所需的选择步骤。
- Prerequisite:
跟随
step take并在step emit之前- 示例:
step choosemsr 3 type host
从传统 SSD 规则迁移到设备类
在 Luminous 版本引入 device class 功能之前,为了编写适用于专用设备类型(例如 SSD)的规则,有必要手动编辑 CRUSH map 并为每种设备类型维护一个并行层级结构。设备类功能提供了一种更透明的方法来实现此目的。
但是,如果您的集群从现有的手动定制的每设备 map 迁移到新的基于设备类的规则,系统中的所有数据都将被重新洗牌。
crushtool 实用程序有几个命令可以转换传统规则和层级结构,并允许您开始使用新的设备类规则。有三种可能的转换类型
--reclassify-root <root-name> <device-class>此命令检查层级结构中
root-name下的所有内容,并重写任何引用指定根且形式为take <root-name>的规则,使其改为具有take <root-name> class <device-class>的形式。该命令还以一种方式重新编号桶,使得旧 ID 用于指定类的“影子树”,因此不会发生数据移动。例如,假设您有以下现有规则
rule replicated_rule { id 0 type replicated step take default step chooseleaf firstn 0 type rack step emit }
如果根
default被重新分类为类hdd,则新规则将如下所示rule replicated_rule { id 0 type replicated step take default class hdd step chooseleaf firstn 0 type rack step emit }
--set-subtree-class <bucket-name> <device-class>此命令将以 bucket-name 为根的子树中的每个设备标记为指定的设备类。
此命令通常与
--reclassify-root选项结合使用,以确保该根中的所有设备都标记有正确的类。然而,在某些情况下,其中一些设备已正确标记为不同的类,并且不得重新标记。为了解决这个困难,可以排除--set-subtree-class选项。重新映射过程不会完美,因为以前的规则对多个类的设备产生了影响,而调整后的规则将仅映射到指定设备类的设备。然而,当没有太多异常设备时,由此产生的数据移动水平通常在可容忍的限制内。--reclassify-bucket <match-pattern> <device-class> <default-parent>此命令允许您将并行类型特定层级结构与正常层级结构合并。例如,许多用户有类似于以下内容的 map
host node1 { id -2 # do not change unnecessarily # weight 109.152 alg straw2 hash 0 # rjenkins1 item osd.0 weight 9.096 item osd.1 weight 9.096 item osd.2 weight 9.096 item osd.3 weight 9.096 item osd.4 weight 9.096 item osd.5 weight 9.096 ... } host node1-ssd { id -10 # do not change unnecessarily # weight 2.000 alg straw2 hash 0 # rjenkins1 item osd.80 weight 2.000 ... } root default { id -1 # do not change unnecessarily alg straw2 hash 0 # rjenkins1 item node1 weight 110.967 ... } root ssd { id -18 # do not change unnecessarily # weight 16.000 alg straw2 hash 0 # rjenkins1 item node1-ssd weight 2.000 ... }
此命令重新分类与特定模式匹配的每个桶。模式可以采用
%suffix或prefix%的形式。例如,在上面的示例中,我们将使用模式%-ssd。对于每个匹配的桶,名称的剩余部分(对应于%通配符)指定 base bucket。匹配桶中的所有设备都标记有指定的设备类,然后移动到 base bucket。如果 base bucket 不存在(例如,node12-ssd存在但node12不存在),则创建它并将其链接到指定的 default parent 桶下。在每种情况下,都会小心保留新影子桶的旧桶 ID,以防止数据移动。引用旧桶的take步骤的任何规则都会相应调整。--reclassify-bucket <bucket-name> <device-class> <base-bucket>相同的命令也可以在没有通配符的情况下使用,以映射单个桶。例如,在前面的示例中,我们希望将
ssd桶映射到default桶。用于转换包含上述片段的 map 的最终命令类似于以下内容
ceph osd getcrushmap -o original crushtool -i original --reclassify \ --set-subtree-class default hdd \ --reclassify-root default hdd \ --reclassify-bucket %-ssd ssd default \ --reclassify-bucket ssd ssd default \ -o adjusted
--compare 标志
可以使用 --compare 标志来确保 从传统 SSD 规则迁移到设备类 中执行的转换是正确的。此标志针对 CRUSH map 测试大量输入样本,并检查是否输出了预期结果。控制这些输入的选项与适用于 --test 命令的选项相同。有关此 --compare 命令如何应用于上述示例的说明,请参阅以下内容
crushtool -i original --compare adjusted
rule 0 had 0/10240 mismatched mappings (0)
rule 1 had 0/10240 mismatched mappings (0)
maps appear equivalent
如果命令发现任何差异,则会在括号中报告重新映射的输入比例。
当您对调整后的 map 感到满意时,通过运行以下命令将其应用于集群
ceph osd setcrushmap -i adjusted
手动调优 CRUSH
如果您已验证所有客户端都运行的是最新代码,则可以通过提取 CRUSH map、修改值并将 map 重新注入集群来调整 CRUSH 可调参数。该过程按如下方式进行
提取最新的 CRUSH map
ceph osd getcrushmap -o /tmp/crush调整可调参数。在我们的测试中,以下值似乎在大型和小型集群中都产生了最佳行为。该过程要求您在
crushtool命令中指定--enable-unsafe-tunables标志。使用此选项时请极其小心crushtool -i /tmp/crush --set-choose-local-tries 0 --set-choose-local-fallback-tries 0 --set-choose-total-tries 50 -o /tmp/crush.new重新注入修改后的 map
ceph osd setcrushmap -i /tmp/crush.new
传统值
要设置 CRUSH 可调参数的传统值,请运行以下命令
crushtool -i /tmp/crush --set-choose-local-tries 2 --set-choose-local-fallback-tries 5 --set-choose-total-tries 19 --set-chooseleaf-descend-once 0 --set-chooseleaf-vary-r 0 -o /tmp/crush.legacy
需要特殊的 --enable-unsafe-tunables 标志。在恢复到传统值后运行旧版本的 ceph-osd 守护程序时要小心,因为功能位没有完美强制执行。