局部自修复纠删码插件

jerasure 插件时,纠删码编码的对象存储在多个 OSD 上,丢失一个 OSD 的恢复过程需读取所有其他的 OSD 。比如 jerasure 的配置为 k=8m=4 ,丢失一个 OSD 后需读取其他 11 个 OSD 才能恢复。

lrc 纠删码插件创建的是局部校验块,这样只需较少的 OSD 即可恢复。比如 lrc 的配置为 k=8m=4l=4 ,它将为每四个 OSD 创建额外的校验块,当一个 OSD 丢失时,它只需四个 OSD 即可恢复,而不需要十一个。

纠删码配置实例

降低主机间的恢复带宽

虽然当所有主机都接入同一交换机时,这不会是诱人的用法,但是带宽利用率确实降低了。

$ ceph osd erasure-code-profile set LRCprofile \
     plugin=lrc \
     k=4 m=2 l=3 \
     crush-failure-domain=host
$ ceph osd pool create lrcpool 12 12 erasure LRCprofile

降低机架间的恢复带宽

在 Firefly 版中,只有主 OSD 与丢失块位于同一机架时所需带宽才能降低。

$ ceph osd erasure-code-profile set LRCprofile \
     plugin=lrc \
     k=4 m=2 l=3 \
     crush-locality=rack \
     crush-failure-domain=host
$ ceph osd pool create lrcpool 12 12 erasure LRCprofile

创建 lrc 配置

要新建 lrc 纠删码配置:

ceph osd erasure-code-profile set {name} \
     plugin=lrc \
     k={data-chunks} \
     m={coding-chunks} \
     l={locality} \
     [crush-root={root}] \
     [crush-locality={bucket-type}] \
     [crush-failure-domain={bucket-type}] \
     [crush-device-class={device-class}] \
     [directory={directory}] \
     [--force]

其中:

k={data chunks}

描述

各对象都被分割为数据块,分别存储于不同 OSD 。

类型

Integer

是否必需

Yes.

实例

4

m={coding-chunks}

描述

计算各对象的编码块、并存储于不同 OSD 。编码块的数量等同于在不丢数据的前提下允许同时失效的 OSD 数量。

类型

Integer

是否必需

Yes.

实例

2

l={locality}

描述

把编码块和数据块分组为大小为 locality 的集合。比如, k=4m=2 时,若设置 locality=3 ,将会分组为大小为三的两组,这样各组都能自行恢复,无需从另一组读数据块。

类型

Integer

是否必需

Yes.

实例

3

crush-root={root}

描述

CRUSH 规则第一步所指向的 CRUSH 桶之名,如 step take default

类型

String

是否必需

No.

默认值

default

crush-locality={bucket-type}

描述

l 定义的块集合将按哪种 crush 桶类型存储。比如,若设置为 rack ,大小为 l 块的各组将被存入不同的机架,此值会被用于创建类似 step choose rack 的CRUSH 规则。如果没设置,就不会这样分组。

类型

String

是否必需

No.

crush-failure-domain={bucket-type}

描述

确保两个编码块不会存在于同一故障域的桶里面。比如,假设故障域是 host ,就不会有两个编码块存储到同一主机;此值用于在 CRUSH 规则中创建类似 step chooseleaf host 的步骤。

类型

String

是否必需

No.

默认值

host

crush-device-class={device-class}

描述

使归置限于指定的设备类(比如 ssdhdd )之内,在 CRUSH 图内用的是 crush 设备类的名字。

类型

String

是否必需

No.

directory={directory}

描述

设置纠删码插件的路径,需是目录

类型

String

是否必需

No.

默认值

/usr/lib/ceph/erasure-code

--force

描述

覆盖同名配置。

类型

String

是否必需

No.

低级插件配置

km 之和必须是 l 参数的整数倍。低级配置参数没有强加这样的限制,并且在某些场合下更方便。因此有可能配置两个组,一组 4 块、另一组 3 块;也有可能递归地定义局部集合,如数据中心和机架再组合为数据中心。 k/m/l 可通过生成低级配置来实现。

lrc 纠删码插件递归地使用纠删码技术,这样一些块丢失的恢复大多只需少部分数据块的子集。

比如,三步编码描述为如下:

chunk nr    01234567
step 1      _cDD_cDD
step 2      cDDD____
step 3      ____cDDD

其中, c 是从数据块 D 计算出的编码块,块 7 丢失后能从后四个块恢复,块 2 丢失后能从前四个块恢复。

使用低级配置的纠删码配置实例

最小测试

此例其实等价于默认纠删码配置, DD 其实就是 K=2c 就是 m=1 并且默认使用 jerasure 插件。

$ ceph osd erasure-code-profile set LRCprofile \
     plugin=lrc \
     mapping=DD_ \
     layers='[ [ "DDc", "" ] ]'
$ ceph osd pool create lrcpool 12 12 erasure LRCprofile

降低主机间的恢复带宽

虽然当所有主机都接入同一交换机时,这不会是诱人的用法,但是带宽利用率确实降低了。它等价于 k=4m=2l=3 ,尽管数据块的布局不同:

$ ceph osd erasure-code-profile set LRCprofile \
     plugin=lrc \
     mapping=__DD__DD \
     layers='[
               [ "_cDD_cDD", "" ],
               [ "cDDD____", "" ],
               [ "____cDDD", "" ],
             ]'
$ ceph osd pool create lrcpool 12 12 erasure LRCprofile

降低机架间的恢复带宽

在 Firefly 版中,只有主 OSD 与丢失块位于同一机架时所需带宽才能降低。

$ ceph osd erasure-code-profile set LRCprofile \
     plugin=lrc \
     mapping=__DD__DD \
     layers='[
               [ "_cDD_cDD", "" ],
               [ "cDDD____", "" ],
               [ "____cDDD", "" ],
             ]' \
     crush-steps='[
                     [ "choose", "rack", 2 ],
                     [ "chooseleaf", "host", 4 ],
                    ]'
$ ceph osd pool create lrcpool 12 12 erasure LRCprofile

不同纠删码后端测试

LRC 当前用 jerasure 作为默认 EC 后端。使用低级配置时,你可以为每一级分别指定 EC 后端、算法。 layers=’[ [ “DDc”, “” ] ]’ 里的第二个参数其实是用于本级的纠删码配置。下面的例子为 lrcpool 存储池配置了 cauchy 技术的 ISA 后端。

$ ceph osd erasure-code-profile set LRCprofile \
     plugin=lrc \
     mapping=DD_ \
     layers='[ [ "DDc", "plugin=isa technique=cauchy" ] ]'
$ ceph osd pool create lrcpool 12 12 erasure LRCprofile

你也可以为各级分别使用不同的纠删码配置。

$ ceph osd erasure-code-profile set LRCprofile \
     plugin=lrc \
     mapping=__DD__DD \
     layers='[
               [ "_cDD_cDD", "plugin=isa technique=cauchy" ],
               [ "cDDD____", "plugin=isa" ],
               [ "____cDDD", "plugin=jerasure" ],
             ]'
$ ceph osd pool create lrcpool 12 12 erasure LRCprofile

纠删编码和解码算法

在层描述中找出的步骤:

chunk nr    01234567

step 1      _cDD_cDD
step 2      cDDD____
step 3      ____cDDD

将被依次应用。比如一个 4K 的对象要被编码,它要先通过 step 1 被分割为四个 1K 的块(四个大写的 D ),分别依次存储于 2 、 3 、 6 和 7 。这些数据产生了两个编码块(两个小写 c ),它们分别存储于 1 和 5 。

step 2 以相似的方式重用 step 1 创建的内容,并把单个编码块 c 存储于位置 0 。最后四个下划线( _ )标记是为提高可读性的,被忽略了。

step 3 把单个编码块存储到了位置 4 , step 1 创建的三个块被用于计算此编码块,也就是 step 1 产生的编码块成了 step 3 的数据块。

如果 2 块丢失了:

chunk nr    01234567

step 1      _c D_cDD
step 2      cD D____
step 3      __ _cDDD

将通过解码来恢复它,反向依次执行: step 3 然后 step 2 最后是 step 1

step 32 一无所知(即它是下划线),所以跳过此步。

step 2 里的编码块存储在 0 块中,可用来恢复 2 块的内容。没有需要恢复的数据块了,不再考虑 step 1 ,进程终止。

恢复块 2 需读取块 0, 1, 3 并写回块 2

如果块 2, 3, 6 丢失:

chunk nr    01234567

step 1      _c  _c D
step 2      cD  __ _
step 3      __  cD D

step 3 可恢复块 6 的内容:

chunk nr    01234567

step 1      _c  _cDD
step 2      cD  ____
step 3      __  cDDD

step 2 未能恢复被跳过了,因为丢失了两块( 2, 3 ),它只能恢复一个块的丢失。

step 1 中的编码块位于块 1, 5 ,因此能恢复块 2, 3 的内容。

chunk nr    01234567

step 1      _cDD_cDD
step 2      cDDD____
step 3      ____cDDD

CRUSH 归置的控制

默认的 CRUSH 规则会选择位于不同主机的 OSD ,例如:

chunk nr    01234567

step 1      _cDD_cDD
step 2      cDDD____
step 3      ____cDDD

需要整整 8 个 OSD ,分别存储 8 个块。如果这些主机分别位于相邻的机架,前四块可放到第一个机架,后四块可放到第二个机架,这样丢失单个 OSD 恢复时就不会用到机架间的带宽。

例如:

crush-steps='[ [ "choose", "rack", 2 ], [ "chooseleaf", "host", 4 ] ]'

此配置会创建这样的规则,选定类型为 rack 的两个 crush 桶、并在各桶中再选四个 OSD ,这几个 OSD 分别位于类型为 host 的不同桶中。

此 CRUSH 规则还可以手工雕琢一下,使其更精细。