基本概念

Pool

Pool是Object的逻辑组合,之所以划分成Pool,是因为在Ceph RADOS中,可以按照Pool来设置PG数量、副本策略等。

每个Pool下管辖多个PG,PG的数量可以通过更新Pool的属性来设定。

注意:

  1. Pool是RADOS层抽象的概念,至于映射成更上层的RBD、RGW什么概念,目前尚不清楚;
  2. Pool是逻辑概念,不同Pool可能的内容映射至底层相同的OSD;
  3. 实践中可以为每个pool在crushmap中定义rule,可以指定pool的一些个性化策略,比如可以为某个pool设置特定的存储介质ssd、设定pool的副本数等。

PG (Placement Group)

PG是Ceph Crush算法中的虚拟节点,在Ceph的架构中,其位于Pool和OSD之间充当中间层,将Pool和Object的映射解耦成Pool->PG 与PG->OSD。

PG可类比Nefs系统中的Partition。每个PG会存储与OSD Set,也就是副本集。如对于三副本存储,每个PG会被存储在三个OSD,其中一个OSD充当Primary,而另外两个则为Secondary。

OSD

OSD:Object Storage Device。代表了Ceph集群中的存储设备。在Ceph的实现中,每个OSD会启动一个Daemon。

在Ceph的推荐中,每个OSD上会被分配上百个PG。

Ceph资源模型

Ceph使用CRUSHMAP来描述所有的资源,CRUSHMAP主要包含Device、Bucket、Rule几个字段。

Device

Device字段记录了当前集群所有的OSD信息,格式如下:

#devices
device {num} {osd.name}

device 0 osd.0
device 1 osd.1
device 2 osd.2
device 3 osd.3
device 4 osd.4
device 5 osd.5
device 6 osd.6
device 7 osd.7

Bucket

在一个存储系统中,资源一般被抽象为以下几个部分:磁盘、服务器、机架、机房、数据中心。分布式存储系统由于其可靠性要求,可能对多副本的存储位置提出不同的需求,如资源充足时可能要求多副本位于不同的数据中心,而在资源紧张时只要求多副本位于不同的物理服务器即可。

Ceph定义了一种层次化资源描述语言,称之为Bucket,这种描述支持如下的资源类型,可以自定义新的资源类型:

# types
# type {num} {bucket-name}
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 region
type 10 root

每种Bucket由num和name组成,如上面的示例中定义了11种Bucket类型,其中root是最顶层资源类型,而这课资源树的最底层资源类型就是osd。

每种bucket的描述规则如下:

[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 ]
    hash [the hash type: 0 by default]
    item [item-name] weight [weight]
}

例如,如果某个资源类型为host(也即物理服务器),其示例Bucket定义如下:

host node1 {
    id -1
    alg straw
    hash 0
    item osd.0 weight 1.00
    item osd.1 weight 1.00
}

表示该Bucket类型为host,host名称为node1,其名下有两个osd,为osd.0和osd.1,且权重均为1,顺便说一句,这里的weight好像是根据osd的容量/1T计算得到的。

而下面是一个rack类型(也就是机架了)的bucket定义:

rack rack1 {
    id -3
    alg straw
    hash 0
    item node1 weight 2.00
    item node2 weight 2.00
}

这个rack1的的孩子bucket有node1和node2,类型为host(好理解啊,机架下面可不就是服务器嘛)。每个host的权重为host内osd的权重和,2。

现在我们大概就形成了下面这样一种资源树形结构:

Ceph科普

Rule

上面我们描述了整个Ceph的资源模型,Ceph根据现有的资源构建出整个的资源树。

而我们前面说到过,Ceph支持Pool粒度的存储策略设置,这里的存储策略包括PG数量、副本数、优先使用的存储介质等。这部分的配置被称为Rule,同样包含在CRUSHMAP中。

rule <rulename> {
    ruleset <ruleset>
    type [ replicated | erasure ]
    min_size <min-size>
    max_size <max-size>
    step take <bucket-name>
    step [choose|chooseleaf] [firstn|indep] <N> <bucket-type>
    step emit
}

type:该rule的复制策略,副本还是纠删码;
step take:该rule选择哪个bucket,一旦设置,以后绑定该rule的pool会从该bucket定义的资源树下选择osd。

有了Rule的抽象,我们就可以为每个pool定制个性策略,比如下面的定义就为两个Pool分别为其设置使用ssd存储和sata存储。

device 0 osd.0
device 1 osd.1
device 2 osd.2
device 3 osd.3
device 4 osd.4
device 5 osd.5
device 6 osd.6
device 7 osd.7

host ceph-osd-ssd-server-1 {
    id -1
    alg straw
    hash 0
    item osd.0 weight 1.00
    item osd.1 weight 1.00
}

host ceph-osd-ssd-server-2 {
    id -2
    alg straw
    hash 0
    item osd.2 weight 1.00
    item osd.3 weight 1.00
}

host ceph-osd-platter-server-1 {
    id -3
    alg straw
    hash 0
    item osd.4 weight 1.00
    item osd.5 weight 1.00
}

host ceph-osd-platter-server-2 {
    id -4
    alg straw
    hash 0
    item osd.6 weight 1.00
    item osd.7 weight 1.00
}

root platter {
    id -5
    alg straw
    hash 0
    item ceph-osd-platter-server-1 weight 2.00
    item ceph-osd-platter-server-2 weight 2.00
} 

root ssd {
    id -6
    alg straw
    hash 0
    item ceph-osd-ssd-server-1 weight 2.00
    item ceph-osd-ssd-server-2 weight 2.00
}

rule platter {
    ruleset 3
    type replicated
    min_size 0
    max_size 10
    # 指定从bucket platter下选择osd
    step take platter
    step chooseleaf firstn 0 type host
    step emit
}

rule ssd {
    ruleset 4
    type replicated
    min_size 0
    max_size 4
    # 指定从bucket ssd下选择osd
    step take ssd
    step chooseleaf firstn 0 type host
    step emit
}

rule ssd-primary {
    ruleset 5
    type replicated
    min_size 5
    max_size 10
    step take ssd
    step chooseleaf firstn 1 type host
    step emit
    step take platter
    step chooseleaf firstn -1 type host
    step emit
}

下面这个命令就设置test-pool使用ssd rule:

ceph osd pool set test-pool crush_ruleset 4

CRUSH算法

Ceph使用Crush算法来直接将object映射到OSD。CRUSH算法参与者有以下:

  1. Object:所有在RADOS中存储的数据被划分为object,如大文件可能按照固定大小切割成object,方便管理;
  2. Pool:所有的Object在写入RADOS时必须指定Pool,因此Pool也必须参与CRUSH定位算法;
  3. PG:CRUSH算法的第一步是将Object定位到Pool下的某个PG;
  4. OSD:CRUSH算法的第二步是将PG定位到具体的OSD set。

Object –> PG

客户端输入 pool ID 和 object ID (比如 pool = “liverpool” and object-id = “john”)

  1. ceph 对 object ID 做哈希
  2. ceph 对该 hash 值取 PG 总数的模,得到 PG 编号 (比如 58)(第2和第3步基本保证了一个 pool 的所有 PG 将会被均匀地使用)
  3. ceph 对 pool ID 取 hash (比如 “liverpool” = 4)
  4. ceph 将 pool ID 和 PG ID 组合在一起(比如 4.58)得到 PG 的完整ID。
  5. 也就是:PG-id = hash(pool-id). hash(objet-id) % PG-number

Ceph科普

PG –> OSD SET

如何将PG映射为OSD SET则依赖于Ceph复杂的资源定义以及资源分配算法了。因此,想搞清楚PG –> OSD SET的映射法则,首先得必须搞清楚Ceph的资源定义。

PG –> OSD SET 定位算法

CRUSH算法的目的是,为给定的PG(即分区)分配一组存储数据的OSD节点。选择OSD节点的过程,要考虑以下几个因素:

  1. PG在OSD间均匀分布。假设每个OSD的磁盘容量都相同,那么我们希望PG在每个OSD节点上是均匀分布的,也就是说每个OSD节点包含相同数目的PG。假如节点的磁盘容量不等,那么容量大的磁盘的节点能够处理更多数量的PG。
  2. PG的OSD分布在不同的故障域。因为PG的OSD列表用于保存数据的不同副本,副本分布在不同的OSD中可以降低数据损坏的风险。

CRUSH算法的输入是(Pool,PGID)。CRUSH通过重复执行Tack(bucketID)和Select(n, bucketType)两个操作选取副本位置,其中:

  • Tack(bucketID)指定从给定的bucketID中选取副本位置,例如可以指定从某台机架上选取副本位置,以实现将不同的副本隔离在不同的故障域;
  • Select(n, bucketType)则在给定的Bucket下选取n个类型为bucketType的Bucket,它选取Bucket主要考虑层级结构中节点的容量,以及当节点离线或者加入时的数据迁移量。

说明:

  1. Tack()参数为bucketID,因为CRUSH算法输入中包含Pool,找到Pool绑定的Rule,即可获得bucketID,没问题;

下图详细描述了这个过程:

Ceph科普

  • bucket: Take操作指定的bucket;
  • type: Select操作指定的Bucket的类型;
  • repnum: Select操作指定的副本数目;
  • rep:当前选择的副本编号;
  • x: 当前选择的PG编号;
  • item: 代表当前被选中的Bucket;
  • c(r, x, in): 代表从Bucket @in中为PG x选取第r个副本;
  • collide: 代表当前选中的副本位置item已经被选中,即出现了冲突;
  • reject: 代表当前选中的副本位置item被拒绝,例如,在item已经处于out状态的情况下;
  • ftotal: 在Descent域中选择的失败次数,即选择一个副本位置的总共的失败次数;
  • flocal: 在Local域中选择的失败次数;
  • local_retries: 在Local域选择冲突时的尝试次数;
  • local_fallback_retries: 允许在Local域的总共尝试次数为bucket.size + local_fallback_retires次,以保证遍历完Buckt的所有子节点;
  • tries: 在Descent的最大尝试次数,超过这个次数则放弃这个副本。

参考