一生万物——RADOS导论 [ceph(一)]

一生万物——RADOS导论

文章目录

  • 一生万物——RADOS导论
    • 前言
    • 一. ceph简述
    • 二. 元数据
    • 三.ceph的核心组件
    • 四. ceph架构
    • 五. RADOS概述
    • 六. RODOS工作原理
      • 寻址流程
      • 数据的操作流程
    • 七. 对象演进与排序
      • 对象标识(object-id)
      • 对象排序
    • 八. stable_mod与客户端寻址
    • 九. PG分裂与扩容
    • 参考文献

前言

  已经有三个月(好像不止3个月)没怎么看书学习了,要重新开始好好做人TAT。要多看书才能进步~

一. ceph简述

  1. ceph是软件定义存储:ceph可以运行在几乎所有主流的linux发行版和其他UNIX操作系统。
  2. ceph是分布式存储:分布式基因可以使其可以轻易管理成百上千个节点,PB级及以上存储容量的大规模集群,基于计算寻址则让ceph客户端可以直接与服务端任意节点通信,从而避免因为存在访问热点而产生性能瓶颈。
  3. ceph是统一存储:ceph既支持传统的块,文件存储协议,也支持新兴的对象存储协议。

二. 元数据

  我一直以为元数据就是原始对象呢。今天才明白。。。
  元数据是描述数据的数据,主要描述数据属性的信息,如:存储位置,历史数据,资源查找,文件记录等功能。是用于描述一个文件的特征的系数数据,比如:访问权限,文件拥有者以及文件数据库的分布信息。在集群文件系统中,分布信息包括文件在磁盘上的位置以及磁盘在集群中的位置。用户需要操作一个文件就必须先得到它的元数据,才能定位到文件的位置并且得到文件的内容、相关属性。
在linux中使用stat命令,可以显示文件的元数据。

[root@ceph-node1 ~]# stat 1.txt 
  File: ‘1.txt’
  Size: 0             Blocks: 0          IO Block: 4096   regular empty file
Device: 802h/2050d    Inode: 33889728    Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Context: unconfined_u:object_r:admin_home_t:s0
Access: 2018-08-05 16:38:22.137566272 +0800
Modify: 2018-08-05 16:38:22.137566272 +0800
Change: 2018-08-05 16:38:22.137566272 +0800
 Birth: -

File:文件名

Size:文件大小(单位:B)

Blocks:文件所占扇区个数,为8的倍数(通常的Linux扇区大小为512B,连续八个扇区组成一个block)

IO Block:每个数据块的大小(单位:B)

regular file:普通文件(此处显示文件的类型)

Inode:文件的Inode号

Links:硬链接次数

Access:权限

Uid:属主id/属主名

Gid:属组id/属组名

Access:最近访问时间

Modify:数据改动时间

Change:元数据改动时间

三.ceph的核心组件

ceph核心组件有:OSD,Monitor,MDS
OSD:赋予底层物理设备(一般来说指得是磁盘)一些CPU,内存资源等,使之成为一个抽象对象存储设备(即OSD),它能够独立的完成一些低级别的文件系统操作(如:空间分配,磁盘IO调度等),以实现客户端IO操作(如:读、写)与系统调用(如:打开文件,关闭文件)之间的解耦。
Monitor:Monitor是集群的大脑,负责维护和分发集群的关键元数据。同时也是客户端与RADOS集群建立连接的桥梁。它是基于Paxos兼职议会算法构建的,具有分布式强一致性的小型集群,主要负责维护和传播集群表的权威副本。采用负荷分担的方式工作,因此任何时刻,任意类型的客户端或者OSD都可以通过和集群中任意一个Monitor进行交互,以索取或者请求更新集群表。基于Paxos的分布式一致性算法可以保证所有Monitor的行为自始至终都是正确和自治的。
MDS(我一直都不清楚它的作用。。。)全程是Ceph MetaData Server,主要保存文件系统服务的元数据,但对象存储和块存储设备是不需要使用该服务的。

四. ceph架构

RADOS是ceph的核心,基于RADOS及其派生的librados标准库可以开发任意类型的存储应用,典型如ceph的三大核心应用——RBD,RGW和cephFS。如图所示。
一生万物——RADOS导论 [ceph(一)]_第1张图片

五. RADOS概述

  一个RADOS集群由大量OSD和少量的Monitor组成。
  RADOS使用一张紧凑的集群表对集群拓扑结构和数据映射规则进行描述。任何时刻,任何持有该表的合法客户端都可以独立地与位于任意位置的OSD直接通信。
  当集群拓扑结构发生变化时,RAODS确保这些变化能够及时地被Monitor捕获,并通过集群表高效传递至所有受影响的OSD和客户端,以保证对外提供不中断的业务和服务。
数据复制、故障检测和数据恢复都是由每个OSD自动进行,因此不存在明显的调度和处理瓶颈。
  RADOS另辟蹊径地以基于可扩展哈希的受控副本分布算法——CRUSH作为衔接客户端和OSD的桥梁。说白了CRUSH是利用计算代替查表。CRUSH包含了获取集群当前数据分布形态所需的全部信息,所以OSD之间通过交互即可智能地完成诸如故障、扩容等引起的数据重新分布。
一生万物——RADOS导论 [ceph(一)]_第2张图片

六. RODOS工作原理

寻址流程

  RADOS并不是直接将数据写入OSD的本地存储设备,而是引入了一个中间结构,称为PG,执行两次映射。如图:
一生万物——RADOS导论 [ceph(一)]_第3张图片
第一次File -> object
将客户端数据按照固定的大小进行切割,编号

  • ino:待操作file的元数据,可以简单理解为该file的唯一id
  • ono:file切分产生的object的序号。
  • oid:将ino、ono连接起来得到的。
    举例而言,如果一个id为filename的file被切分成了三个object,则其object序号依次为0、1和2,而最终得到的oid就依次为filename0、filename1和filename2。

第二次object -> PG映射

  • hash(oid) & mask -> pgid
    使用Ceph系统指定的一个静态哈希函数计算oid的哈希值,将oid映射成为一个近似均匀分布的伪随机值。然后,将这个伪随机值和mask按位相与,得到最终的PG序号(pgid)。根据RADOS的设计,给定PG的总数为m(m应该为2的整数幂),则mask的值为m-1。因此,哈希值计算和按位与操作的整体结果事实上是从所有m个PG中近似均匀地随机选择一个。基于这一机制,当有大量object和大量PG时,RADOS能够保证object和PG之间的近似均匀映射。又因为object是由file切分而来,大部分object的size相同,因而,这一映射最终保证了,各个PG中存储的object的总数据量近似均匀。

第三次PG -> OSD 映射

  • 实现PG到OSD的映射,仍然采用伪随机哈希函数(以保证PG在OSD之间分布的均匀性),输入除了全局唯一的PG身份标识外(pid),还引入了集群拓扑,并且使用CRUSH规则对映射过程进行调整,以帮助PG在不同OSD之间灵活迁移,进而实现数据可靠性、自动平衡等高级特性。

数据的操作流程

以file写入过程为例,对数据操作流程进行说明。

为简化说明,便于理解,此处进行若干假定。首先,假定待写入的file较小,无需切分,仅被映射为一个object。其次,假定系统中一个PG被映射到3个OSD上。

基于上述假定,则file写入流程可以被下图表示:
一生万物——RADOS导论 [ceph(一)]_第4张图片
  如图所示,当某个client需要向Ceph集群写入一个file时,首先需要在本地完成上面叙述的寻址流程,将file变为一个object,然后找出存储该object的一组三个OSD。这三个OSD具有各自不同的序号,序号最靠前的那个OSD就是这一组中的Primary OSD,而后两个则依次是Secondary OSD和Tertiary OSD。
  找出三个OSD后,client将直接和Primary OSD通信,发起写入操作(步骤1)。Primary OSD收到请求后,分别向Secondary OSD和Tertiary OSD发起写入操作(步骤2、3)。当Secondary OSD和Tertiary OSD各自完成写入操作后,将分别向Primary OSD发送确认信息(步骤4、5)。当Primary OSD确信其他两个OSD的写入完成后,则自己也完成数据写入,并向client确认object写入操作完成(步骤6)。
  之所以采用这样的写入流程,本质上是为了保证写入过程中的可靠性,尽可能避免造成数据丢失。同时,由于client只需要向Primary OSD发送数据,因此,在Internet使用场景下的外网带宽和整体访问延迟又得到了一定程度的优化。
  这种可靠性机制必然导致较长的延迟,特别是,如果等到所有的OSD都将数据写入磁盘后再向client发送确认信号,则整体延迟可能难以忍受。因此,Ceph可以分两次向client进行确认。当各个OSD都将数据写入内存缓冲区后,就先向client发送一次确认,此时client即可以向下执行。待各个OSD都将数据写入磁盘后,会向client发送一个最终确认信号,此时client可以根据需要删除本地数据。

七. 对象演进与排序

对象标识(object-id)

  与文件类似,ceph也使用字符串对对象进行标识,使用命名空间对每个对象归属的作用域进行限制和隔离。因为对象必须通过PG间接地 归属于某个存储池,所以还必须记忆所归属的存储池 标识。
  ceph具有数据备份功能和数据回滚功能。

  • 数据备份:要区分原始对象和由其产生的一些克隆对象,必须向对象添加全局唯一的快照标识(snap-id)加以区分
  • 数据回滚:区分某个纠删码对象的当前版本和由其衍生的一众历史版本,因此需要向对象中添加分片标识(shard-id)和对象版本号(generation)加以区分。仅在纠删码存储池有效。
      至此,我们得到基于对象名命名空间对象归属存储池标识快照标识分片标识版本号,区分集群中各种对象的方法,基于这些特征值构造的数据结构被称为对象标识(object-id),其在集群内唯一定义一个ceph对象。

对象排序

   ceph引以为傲的自动数据恢复和平衡功能,用于守护数据正确性与一致性的Scrub机制,都依赖于“可以通过某种手段不重复地遍历PG中所有对象”。一般典型的应用中,为了保证对象的唯一性一般会使用比较长的文件名(例如:rbd_data.75b86e238e1f29.00000000000050),所以若采用字符串比较来实现对象排序效率是十分低下的。
   RADOS采用的是32位(根据内存消耗和比较效率选出)的哈希,使用命名空间加上对象名作为哈希输入。
  至此,我们得到对象标识完整的特征值:对象名,键(作用与对象名类型,目前暂未使用),命名空间,对象归属存储池标识,哈希,快照标识,分片标识,版本号
  基于对象的特征值可以定义对象排序所依赖的比较算法如下:

  • 比较分片标识
  • 比较存储池标识
  • 比较32位哈希值
  • 比较命名空间
  • 比较键
  • 比较对象名
  • 比较快照标识
  • 比较版本号
  • 判定两个对象相等
    基于上述排序算法可以对PG中所有对象执行快速排序,这是实现Backfill,Scrub等复杂功能的理论基础。

八. stable_mod与客户端寻址

  Ceph通过C/S模式实现外部应用程序与RADOS集群之间的交互。应用程序通过客户端访问集群时,首先由客户端负责生成一个字符串形式的对象名,然后基于对象名(命名空间)生成32位的哈希值。针对此哈希值,通过简单的数学运算,例如对存储池的PG数(pg_num)取模,可以得到一个PG在存储池内部的编号,加上对象本身已经记录的存储池编号,即可得到负责承载该对象的PG。
  为什么要引入stable_mod呢? --主要是为PG分裂做铺垫,当PG分裂时,老PG上的对象需要向新PG上迁移,我们需要尽可能的减少对象的移动,以避免长时间的业务停顿。此时就需要保证“同一个PG内的对象的哈希值有尽可能的多,相同的低位”。
  假定标识为1的存储池中的某个对象,哈希值为0x4979FA12并且该存储池的pg_num为256。

	0x4979FA12 mod 256 = 18

  因此该对象由存储池1中的18号PG负责承载,并且完整的PGID为1.18。以下对象也会映射到1.18PG上。

0x4979FB12 mod 256 = 18
0x4979FC12 mod 256 = 18
0x4979FD12 mod 256 = 18

  在上面这个例子中,我们仅仅使用了32位哈希的后8位(二进制),如果我们将pg_num写成2n的形式(256=28),我们很容易验证**此时(pg_num是2的整数幂时)归属同一PG的对象的低n位都是相同的。**我们将2n-1称为pg_num的掩码,其中n为掩码的位数。
  如果pg_num不是2的整数次幂,此时其最高位n,此时普通的取模无法保证“归属同一PG的对象的低n位都是相同的”这一特性,因此我们对普通取模进行改进,以保证该特性。一种方案是通过掩码代替取模操作,例如pg_num的最高位为n,其掩码为2n-1。映射时执行hash & (2n-1)即可。但是这种方式会产生空穴,即将一些对象映射到了不存在的PG上面。如下图,此时pg_num=12,n=4,执行hash & (2n-1)会产生0-15共计16中结果。实际12-15并不存在。

使用掩码的方式进行对象至PG映射
  因此我们退而其次,**如果pg_num不是2的整数次幂,我们只要保证同一PG上面的对象的低n-1位时相同的即可。**改进后的映射方法称为stable_mod:

if ((hash & (2^n - 1)) < pg_num)
	return (hash & (2^n -1));
else
	return  (hash & (2^(n-1) -1))

利用stable_mod映射结果如下:
一生万物——RADOS导论 [ceph(一)]_第5张图片
  无论pg_num是否为2的整数次幂,采用stable_mod的方法都可以产生一个相对有规律的对象到PG的映射结果,这是PG分裂的重要理论基础。

九. PG分裂与扩容

  PG增加之后带来两个问题:1. 对象需要重新映射,老PG上的对象向新PG上迁移。
2. 由上面学习我们知道,RADOS会使用CRUSH规则根据pid为PG分配OSD,若pid发生变化,那么集群会大规模的迁移。
  我们以pg_num=24 -> pg_num=26为例,老PG中的对象的哈希低4位一定是相同的。扩容之后n=6,所以我们只需要关注对象哈希值的低6位。我们关注其中一个PG,假定此PGID=Y.X,其中X=0bX3X2X1X0,那么此PG内的对象可以分为4类:

0 0 X3 X2 X1 X0
0 1 X3 X2 X1 X0
1 0 X3 X2 X1 X0
1 1 X3 X2 X1 X0

依次针对这4种类型的对象PG(Y.X)使用新的pg_num(26)执行stable_mod(只写了6位)

  • stable_mod(00X3X2X1X0) = X
  • stable_mod(01X3X2X1X0) = X + 1 × 16
  • stable_mod(10X3X2X1X0) = X + 2 × 16
  • stable_mod(11X3X2X1X0) = X + 3 × 16

  可见,由于存储池的pg_num发生了变化,仅有第一种的对象(X5X4=00)使用stable_mod仍然映射到老PG上,其他的三种类型并无实际的PG对应,我们创建3个新的PG来转移来自老PG中的对象。
  针对所有的老PG重复上述过程,最终可以将存储池的PG数量调整为原来的4倍(24 -> 26)。又因为在调整PG数量的过程中,我们总是基于老PG(称为祖先PG)产生新的孩子PG,并且新的孩子PG中的最初的对象全部来源于laoPG,所以这个过程被形象的称为PG的分裂。
  在FileStore的实现中,由于PG对应一个文件目录,其下的对象全部使用文件保存,并对目录下的文件进行分层管理,使用对象哈希值逆序之后作文目录分层的依据。(目录结构详情见《ceph之RADOS设计原理与实现》P15)
  此时可以不用移动对象,而是直接修改文件夹的归属,即可完成对象在新老PG之间的迁移。 – 解决了问题1。
  引入PG分裂之后,如果仍然使用PGID作为CRUSH输入,据此计算新增孩子PG在OSD之间的映射结果,由于此时每个PG的PGID都不相同,必然触发大量新增孩子PG在OSD之间的迁移。一个好的办法是让同一个祖先诞生的所有孩子PG与其保持相同的副本分布,这样分裂之后集群的PG分布仍然是均衡的。为此,每个存储池除了记录当前的PG数量之外,为了应对PG分裂,还需要额外记录分裂之前祖先PG的数量,我们称它为PGP数量(pgp_num)。
  最终,利用CRUSH执行PG映射时,我们不是直接使用PGID,而是先PGID对pgp_num执行stable_mod,再与存储池标识一起哈希之后作为CRUSH的特征输入,即可保证每个孩子PG与祖先产生相同的CRUSH计算结果(因为他们两个的输入完全相同),进而保证两者产生相同的副本分布。
  Luminous将默认的本地对象存储引擎切换为BlueStore,此时同一个OSD下所有对象都共享一个扁平寻址空间,所以PG分裂时,甚至不存在上述对象在新老文件夹之间转移的过程,因而更加高效。

参考文献

  1. 《ceph之RADOS设计原理与实现》谢型果 严军 著

你可能感兴趣的:(ceph)