本章主要对Ceph的工作原理进行介绍。
寻址过程
如果Client来了一个请求,不管个请求是读还是写都需要先寻址,才能找到数据应该放哪里或者说需要从哪里去。
之前我们说过Ceph的寻址方式是基于计算的,这样就避免的查表,也避免了使用一个单独的元数据服务器。
概述
对于Client传来的一个File,为了方便处理,我们可以将其分割为若干大小相同的小块Object。
然后可以将这些Object映射到OSD上,如果使用一种固定的映射算法,则一个Object每次都会固定的映射到一组OSD上,那么如果这个OSD坏了呢?则无法迁移到其他的OSD上。
同时一个Object可以给会映射到若干的OSD上,所以这个OSD会定期与其他的OSD进行信息的交互,而每个OSD上承载的Object可能达数百万个,那么如果OSD要进行的信息交互将暴涨至数千万次,维护成本较高。
那么为了实现Object与OSD之间的动态映射,以及降低OSD与Object之间的信息维护开销,则可以引入一个中间层PG(Placement Group)
PG首先会对Object进行组织。一个Object只能映射到一个PG里面。而一个PG可以组织多个Object。也就是说PG与Object是“一对多”的映射关系
另外PG还会与OSD进行映射。每个PG一般会复制成3副本放到3个OSD上。而每个OSD上也会承载多个PG。
映射过程
那么file——Object——PG——OSD之间的映射是如何完成的呢?
-
首先是file —— Object的映射
这个比较简单,就是按照一定的大小对file进行切割即可,相当于RAID中的条带化处理
切割以后,我们可以对object进行编号,也即oid(Object ID)
每个file都有一个唯一的元数据ino,然后再把file切分后产生的序号连缀在一起,即可构成oid
比如元数据为fileName的文件成分为三个Object,oid即为fileName0,fileName1,fileName2。
需要注意的是ino必须唯一
-
然后是Object—— PG
每个Object都要独立的映射到一个PG里面。
我们之前提供,数据块最好能均匀在底层,这样即可以充分利用底层硬件,有可以平摊风险。那么怎么均匀分布呢?
首先可以将oid进行哈希,这样会得到一个近似均匀分布的伪随机值,然后我们要考虑的是把Object放到PG里面去了。假设PG数为m,那么最简单的就是在m个PG中随机的选一个,那么可以设定一个长度为m-1的掩码,然后将HASH得到的伪随机值于mask按位相与,最终得到PG序号(pgid)
现在我们保证了Object与PG之间近似均匀的映射,然后Object的size相同,这样就可以保证PG中存储的Object的总数据量近似均匀。
不过需要注意的是Object与PG的数量较多的时候,这种伪随机关系才近似均匀性才能成立。
-
最后是PG——OSD映射
之前讲的Object与PG的映射其实是静态的,也就是一个Object通过这个运算一定会映射到某个PG里面。
但是我们加上PG层的目的是可以实现动态迁移,也就是说"Object——PG"的结构不是绝对不变的,而是受到其他因素的影响,比如
当前系统的状态,也就是Cluster Map。当系统中的OSD状态、数量发生变化的时候,ClusterMap可能发生变化。因此映射关系也会变化。
预先配置的存储策略。
最开始的时候管理员可以配置一些策略,比如说一个PG分到的3个OSD需要位于不同的服务器或者说机架上,这样就算一个服务器宕掉了,还有其他的副本可用。
所以说这一层的映射公式需要满足动态特性,可以让PG根据需要动态迁移到不同的OSD组合上。
此次映射中使用的算法就叫CRUSH算法
写数据的流程
Ceph的写与读的流程大体相同,本章主要介绍写过程。
当Client要向Ceph集群写入一个file时,首先要在Client本地将file映射为若干Object,然后使用HASH和CRUSH算法映射成3个OSD。序号最靠前的OSD就是Primary OSD,后两个为Secondary OSD和Tertiary OSD。
现在Client已经知道数据要写到哪些OSD里面了,首先向Primary OSD发起写请求,然后由Primary OSD同步复制两个副本到其余的OSD中。
当Secondary 和Tertiary完成写入了以后,返回ACK给 Primary,最后由Primary向Client确认Object的写入。
很显然这种做法延迟比较长,必须等所有的OSD都落到磁盘上了以后才会真正返回ACK。可以进行一些优化。当各个OSD将数据写入内存缓冲区后,先向Client发送 一次确认,当各个OSD都将数据落到磁盘后,再向Client发送一个最终的ACK。
总之,不需要依赖其他的系统模块,只需要Client即可完成OSD的寻址。所以Client可以与OSD进行并行操作,这样工作压力可以尽可能均匀分担,从而避免单个OSD成为性能瓶颈。
如何同步信息
下面两章我们介绍了如何寻址,如何写入,他们都需要使用到cluster map这个元数据,那么如何在所有节点上同步cluster map呢?
由若干monitor共同监控所有OSD的状态,然后会形成cluster map的master版本。再将这个版本同步到其他的OSD和client中。
不过monitor不是主动询问所有OSD的状态的,而是由OSD主动上报自己的状态信息,比如说OSD加入或者异常的时候,都需要主动通知到monitor
那么Cluster Map需要包含哪几个方面的信息呢?
首先是OSD的网络地址,这样才可以找到OSD在那里。
然后当然是OSD的状态,也就是OSD是否正常工作,是否在至少一个PG里面。
另外还需要包含CRUSH算法的一些配置参数。
还有个问题,Cluster Map在所有的OSD之间进行同步,极有可能两个OSD持有的Cluster Map的版本不同,那么怎么进行区分了?可以使用 版本号,时间越靠后的版本号越大,所以monitor手上的版本号一定最大。当任意两方在通信的时候发现彼此的版本号不相同,则需要将高版本的同步到另外的节点上。
OSD上线
当一个新的OSD上线后,首先会根据配置信息主动向monitor进行报告。
然后Monitor会把它加入Cluster Map中,然后更新状态,最后把最新版的Cluster Map发给这个OSD。
接下来,这个OSD会计算出自己对应的PG,以及这个PG还会放到哪些其他的OSD。然后这个新的OSD会与这些OSD取得联系,如果
-
此时发现这个PG只放了2个副本,而预先配置的副本数应该是3。说明之前肯定有OSD出现了故障,所以其他OSD把这个PG的内容复制给新的OSD,最后再更新一次状态,说明OSD已经可以承载PG了。
这就是一个failure recovery的过程
如果此时发现OSD一切正常,则用新的OSD替换到现在的一个OSD,并承担数据,重新更新cluster map的内容。这就是一个re-balancing的过程。
那么failture detection的过程又是什么呢?如果一个OSD发现自己与共同承载一个PG的另一个OSD无法通信,它会上报给monitor。或者说,OSD的守护进程发现自己的状态异常,同样会上报给monitor。如果在一段时间以后,OSD仍然无法恢复正常,则状态会设置为down and out,更新cluster map并扩散。
更新cluster map会不会有广播风暴
之前我们了解了cluster map数据结构并不大,也就是说即使这个集群中有上千个OSD,cluster map的扩散也不会占用太大的带宽。
而且cluster map也不会频繁的更新。
最关键的是cluster map是以增量的形式扩散,只会把两个版本的差异发送给另一方。
同时monitor不是map的版本一更新就广播给大家。它只会在有OSD上报信息的时候才会同步一次,所以这种方式是异步且lazy的。
所以因为map更新而导致广播防暴的几率并不高。