注意

本文档适用于 Ceph 的开发版本。

手动编辑 CRUSH Map

注意

手动编辑 CRUSH map 是一项高级管理员操作。对于大多数安装,CRUSH 更改可以通过 Ceph CLI 实现,不需要手动编辑 CRUSH map。如果您确定在最近的 Ceph 版本中手动编辑必要的用例,请考虑联系 Ceph 开发者 dev@ceph.io,以便未来的 Ceph 版本不再有此问题。

要编辑现有的 CRUSH map,请执行以下步骤

  1. 获取 CRUSH map.

  2. 反编译 CRUSH map。

  3. 编辑以下至少一个部分:设备规则。使用文本编辑器执行此任务。

  4. 重新编译 CRUSH map。

  5. 设置 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 有六个主要部分

  1. tunables: map 顶部的序言描述了任何不属于传统 CRUSH 行为的 tunables。这些可调参数纠正了多年来为改善 CRUSH 行为而进行的旧错误、优化或其他更改。

  2. devices: 设备是存储数据的单个 OSD。

  3. types: 桶 types 定义了 CRUSH 层级结构中使用的桶类型。

  4. buckets: 桶由存储位置(例如,行、机架、机箱、主机)的层级结构聚合及其分配的权重组成。定义桶 types 后,CRUSH map 定义层级结构中的每个节点、其类型以及它包含的设备或其他节点。

  5. rules: 规则定义了数据如何在层级结构中的设备上分布的策略。

  6. choose_args: choose_args 是与层级结构相关的替代权重,已调整以优化数据放置。单个 choose_args map 可以用于整个集群,也可以创建多个 choose_args map,以便每个 map 都针对特定池进行定制。

CRUSH Map 设备

设备是存储数据的单个 OSD。在此部分中,通常为集群中的每个 OSD 守护程序定义一个设备。设备由 id(一个非负整数)和 name(通常为 osd.N,其中 N 是设备的 id)标识。

设备还可以关联一个 device class:例如,hddssd。设备类使得 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;但是,您可以给叶子桶起任何您喜欢的名称(例如:osddiskdrivestorage)。

# 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]
}

例如,在上图中,定义了两个主机桶(在下面的声明中称为 node1node2)和一个机架桶(在下面的声明中称为 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 takestep 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 takestep 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 实用程序有几个命令可以转换传统规则和层级结构,并允许您开始使用新的设备类规则。有三种可能的转换类型

  1. --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
    }
    
  2. --set-subtree-class <bucket-name> <device-class>

    此命令将以 bucket-name 为根的子树中的每个设备标记为指定的设备类。

    此命令通常与 --reclassify-root 选项结合使用,以确保该根中的所有设备都标记有正确的类。然而,在某些情况下,其中一些设备已正确标记为不同的类,并且不得重新标记。为了解决这个困难,可以排除 --set-subtree-class 选项。重新映射过程不会完美,因为以前的规则对多个类的设备产生了影响,而调整后的规则将仅映射到指定设备类的设备。然而,当没有太多异常设备时,由此产生的数据移动水平通常在可容忍的限制内。

  3. --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
    ...
     }
    

    此命令重新分类与特定模式匹配的每个桶。模式可以采用 %suffixprefix% 的形式。例如,在上面的示例中,我们将使用模式 %-ssd。对于每个匹配的桶,名称的剩余部分(对应于 % 通配符)指定 base bucket。匹配桶中的所有设备都标记有指定的设备类,然后移动到 base bucket。如果 base bucket 不存在(例如,node12-ssd 存在但 node12 不存在),则创建它并将其链接到指定的 default parent 桶下。在每种情况下,都会小心保留新影子桶的旧桶 ID,以防止数据移动。引用旧桶的 take 步骤的任何规则都会相应调整。

  4. --reclassify-bucket <bucket-name> <device-class> <base-bucket>

    相同的命令也可以在没有通配符的情况下使用,以映射单个桶。例如,在前面的示例中,我们希望将 ssd 桶映射到 default 桶。

  5. 用于转换包含上述片段的 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 可调参数。该过程按如下方式进行

  1. 提取最新的 CRUSH map

    ceph osd getcrushmap -o /tmp/crush
    
  2. 调整可调参数。在我们的测试中,以下值似乎在大型和小型集群中都产生了最佳行为。该过程要求您在 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
    
  3. 重新注入修改后的 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 守护程序时要小心,因为功能位没有完美强制执行。

由 Ceph 基金会为您呈现

Ceph 文档是由非营利性 Ceph 基金会 资助和托管的社区资源。如果您希望支持这项工作和我们的其他努力,请考虑 立即加入