每一个不曾起舞的日子,都是对生命的一种辜负。-- 尼采

首先看下 Ceph 整体架构图简图:

默认每个主 OSD 都携带两个副本。有效存储比例为 3:1 。 在写入的时候也是先写入到主分区(Primary OSD),然后在自动同步到副本(Replicate OSD)。

1. 正常 IO 流程图

写入数据步骤:

  1. client 创建cluster handler。
  2. client 读取配置文件。
  3. client 连接上monitor,获取集群map信息。
  4. client 读写io 根据 crshmap 算法请求对应的主 OSD 数据节点。
  5. 主 osd 数据节点同时写入另外两个副本节点数据。
  6. 等待主节点以及另外两个副本节点写完数据状态。
  7. 主节点及副本节点写入状态都成功后,返回给client,io写入完成。

如果是读取数据的话,步骤 4 直接读取数据返回。后面的步骤都不执行了。

2. 新 Primary IO 流程图

如果新加入的 OSD1 取代了原有的 OSD4 成为 Primary OSD, 由于 OSD1 上未创建 PG , 不存在数据,那么 PG 上的 I/O 无法进行, 如下图所示:

此时的步骤如下:

  1. client连接monitor获取集群map信息。
  2. 同时新主osd1由于没有pg数据会主动上报monitor告知让 OSD2 临时接替为主节点。
  3. 临时主 OSD2 会把数据全量同步给新主节点 OSD1。
  4. client IO读写直接连接临时主 OSD2 进行读写。
  5. OSD2 收到读写io,同时写入另外两副本节点。
  6. 等待 OSD2 以及另外两副本写入成功。
  7. OSD2 三份数据都写入成功返回给client, 此时client io读写完毕。
  8. 如果 OSD1 数据同步完毕,此时 OSD1 会正式替换 OSD2 成为主节点,OSD2 变为副本。

3. Ceph IO 算法流程

  1. 计算 File -> Object 映射(假设 File 为用户要读写的文件)得到 oid(object id) [oid = ino + ono]
    • ino : File 的元数据序列号,File的唯一id。
    • ono : File 切分产生的某个 object 的序号,默认以4M切分一个块大小。
  2. Object 是 RADOS需要的对象。Ceph指定一个静态hash函数计算oid的值,将oid映射成一个近似均匀分布的伪随机值, 然后和mask按位相与,得到pgid。Object -> PG 映射:
    • hash(oid) & mask-> pgid 。
    • mask = PG总数m(m为2的整数幂)-1 。
  3. PG 用途是对object的存储进行组织和位置映射。一个 PG 里面会有很多 Object。采用CRUSH算法,将pgid代入其中, 然后得到一组OSD。PG -> OSD 映射:[CRUSH(pgid)->(osd1,osd2,osd3)]
Ceph IO伪代码流程:
locator = object_name
obj_hash =  hash(locator)
pg = obj_hash % num_pg
osds_for_pg = crush(pg)  # returns a list of osds
primary = osds_for_pg[0]
replicas = osds_for_pg[1:]

4. Ceph RBD IO流程

步骤:

  1. 客户端创建一个pool,需要为这个pool指定pg的数量。
  2. 创建pool/image rbd设备进行挂载。
  3. 用户写入的数据进行切块,每个块的大小默认为4M,并且每个块都有一个名字,名字就是object+序号。
  4. 将每个object通过pg进行副本位置的分配。
  5. pg根据cursh算法会寻找3个osd,把这个object分别保存在这三个osd上。
  6. osd上实际是把底层的disk进行了格式化操作,一般部署工具会将它格式化为xfs文件系统。
  7. bject的存储就变成了存储一个文rbd0.object1.file。

5. Ceph RBD IO框架图

客户端写数据osd过程:

  1. 采用的是librbd的形式,使用librbd创建一个块设备,向这个块设备中写入数据。
  2. 在客户端本地同过调用librados接口,然后经过pool,rbd,object、pg进行层层映射,在PG这一层中, 可以知道数据保存在哪3个OSD上,这3个OSD分为主从的关系。
  3. 客户端与primay OSD 建立socket 通信,将要写入的数据传给primary OSD,由primary OSD再将数据发送给其他replica OSD数据节点。

6. Ceph Pool 和 PG 分布情况

  1. pool是ceph存储数据时的逻辑分区,它起到 namespace 的作用。
  2. Ceph 集群搭建成之后,会有一个默认的 RBD pool。
  3. 每个pool包含一定数量(可配置)的PG。
  4. PG里的对象被映射到不同的Object上。
  5. pool 是分布到整个集群的,RBD pool 和 CephFS 的 pool 并没有隔离。
  6. pool可以做故障隔离域,根据不同的用户场景不一进行隔离。

7. Ceph 数据扩容 PG 分布

场景模拟:现状3个OSD, 4个PG, 扩容到4个OSD, 4个PG

扩容前:

扩容后:

扩容后,`PGa1`,`PGb2`和`PGc3` 迁移到了 OSD4 上了。
每个OSD上分布很多PG, 并且每个PG会自动散落在不同的OSD上。
如果扩容那么相应的PG会进行迁移到新的OSD上,保证PG数量的均衡。

参考文献