Ceph 是一个开源项目,它提供软件定义的、统一的存储解决方案 。Ceph 是一个具有高性能、高度可伸缩性、可大规模扩展并且无单点故障的分布式存储系统 。
Ceph 是软件定义存储解决方案
Ceph 是统一存储解决方案
Ceph 是云存储解决方案
Ceph 官方文档:http://docs.ceph.com/docs/mimic/
Ceph 监视器服务进程,简称 MON。负责监控集群的监控状态,所有的集群节点都向 MON 节点报告状态以及每个状态变化的信息。MON 通过收集这些信息并维护一个 Cluster Map 来完成它的任务。Cluster Map 下属包含每个组件的 Map,例如:MON Map、OSD Map、PG Map、CRUSH Map 和 MDS Map。一个典型的 Cepb 集群中通常存在多个 MON 节点,它们之间启用了仲 裁(Quorum)这种分布式决策机制,应用 Paxos 算法保持各节点 Cluster Map 的一致性。所以集群中 MON 节点数应该是一个奇数,开始仲裁操作时至少要保证有一半以上的 MON 处于可用状态,这样才可以防止传统存储系统中常见的脑裂问题。OSD 只有在一些特殊情况(e.g. 添加新的 OSD、OSD 发现自身或他人存在异常)才会上报自己的信息,平常只会简单的发送心跳。MON 在收到这些上报信息时,则会更新 Cluster Map 并加以扩散。
Ceph 对象存储设备服务进程,简称 OSD。一个 OSD 守护进程与集群中的一个物理磁盘绑定,OSD 负责将数据以 Object 的形式存储在这些物理磁盘中,并在客户端发起数据请求时提供相同的数据。一般来说,物理磁盘的 总数与 Ceph 集群中负责存储用户数据到每个物理磁盘的 OSD 守护进程数是相等的。OSD 同时还负责处理数据复制、数据恢复、数据再平衡以及通过心跳机制监测其它 OSD 状况并报告给 MON。对于任何 R/W 操作,客户端首先向 MON 请求集群的 Map,然后客户端就可以直接与 OSD 进行 I/O 操作。正是因为客户端能够直接与 OSD 进行操作而不需要额外的数据处理层,才使得 Ceph 的数据事务处理速度如此的快。
Ceph 元数据服务器服务进程,简称 MDS。只有在启用了 Ceph 文件存储(CephFS)的集群中才需要启用 MDS,它负责跟踪文件层次结构,存储和管理 CephFS 的元数据。MDS 的元数据也是以 Obejct 的形式存储在 OSD 上。除此之外,MDS 提供了一个带智能缓存层的共享型连续文件系统,可以大大减少 OSD 读写操作频率。


可靠、自动、分布式对象存储系统,简称 Ceph 存储集群。Ceph 的所有优秀特性都是由 RADOS 提供的,包括分布式对象存储的数据一致性、高可用性、高可靠性、没有单点故障、向我修复以及自我管理等,Ceph 的数据访问方式(e.g. RBD, CephFS, RADOSGW 和 librados)都建立在 RADOS 之上。Cepb 中的一切都以对象的形式存储,而 RADOS 就负责存储这些对象。对于分布式存储的数据一致性,RADOS 通过执行数据复制、故障检测与恢复,还包括数据在集群节点间的迁移与再平衡来实现。
是一个 C 语言库,简称 Ceph 基础库。对 RADOS 的功能进行了抽象和封装,并提供北向 API。通过 librados,应用程序可以直接访问 RADOS 原生功能,以此来提高了应用程序的性能、可靠性和效率。因为 RADOS 本质是一个对象存储系统,所以 librados 提供的 API 都是面向对象存储的。librados 原生接口的优点是它直接与应用代码集成,操作非常方便,但也不会主动对上传的数据进行分片。
librados 支持具有异步通信能力的对象存储接口:
Ceph 块存储,简称 RBD,是基于 librados 之上的块存储服务接口。RBD 的驱动程序已经被集成到 Linux 内核(2.6.39 或更高版本)中,也已经被 QEMU/KVM Hypervisor 支持,它们都能够无缝地访问 Ceph 块设备。Linux 内核 RBD(KRBD)通过 librados 映射 Ceph 块设备,然后 RADOS 将 Ceph 块设备的数据对象以分布式的方式存储在集群节点中。
Ceph 对象网关,简称 RGW,是基于 librados 之上的对象存储服务接口。本质是一个代理,可以将 HTTP 请求转交给 RADOS,同时也可以把 RADOS 请求转换为 HTTP 请求,以此来提供 RESTful 对象存储服务接口,并兼容 S3 和 Swift。

Ceph 文件系统,简称 CephFS。在 RADOS 层之上提供了一个任意大小且兼容 POSIX 的分布式文件系统。CephFS 依赖 MDS 来管理其元数据(文件层次结果),以此将元数据与原始数据分离,MDS 不直接向客户端提供数据,因此可以避免单点故障,有助于降低软件复杂性并提高可靠性。

Ceph 的客户端是指使用 Ceph 存储服务的一切实体对象。可能是一个 Client 软件、一台主机/虚拟机或者是一个 App。客户端会根据 Ceph 提供的不同接口类型(服务接口、原生接口、C 库)来连接并 Ceph 存储服务器并使用相应的 Ceph 存储服务。

当用户使用 RBD、RGW、CephFS 类型客户端接口来储存数据时,会经历一个透明的、将数据转化为 RADOS 统一处理对象的过程,这个过程就称之为数据条带化或分片处理。
熟悉存储系统的你不会对条带化感到陌生,它是一种提升存储性能和吞吐能力的手段,通过将有序的数据分割成多个区段并分别储存到多个存储设备上,最常见的条带化就是 RAID0。而 Ceph 的条带化处理就类似于 RAID0,如果想发挥 Ceph 的并行 I/O 处理能力,就应该充分利用客户端的条带化功能。需要注意的是,librados 原生接口并不具有条带化功能,比如:使用 librados 接口上传 1G 的文件,那么落到存储磁盘上的就是 1G 大小的文件。存储集群中的 Objects 也同样不具备条带化功能,实际上是由上述三种类型的客户端将数据条带化之后再储存到集群 Objects 之中的。
条带化处理过程:
假设 Object 存储上限为 4M,每一个条带单元块占 1M。此时我们储存一个 8M 大小的文件,那么前 4M 就储存在 Object0 中,后 4M 就创建 Object1 来继续储存。

随着储存文件 Size 的增加,可以通过将客户端数据条带化分割储存到多个 Objects 中,同时由于 Object 映射到不同的 PG 上进而会映射到不同的 OSD 上,这样就能够充分利用每个 OSD 对应的物理磁盘设备的 IO 性能,以此实现每个并行的写操作都以最大化的速率进行。随着条带数的增加对写性能的提升也是相当可观的。如下图,数据被分割储存到两个 Object Set 中,条带单元块储存的顺序为 stripe unit 0~31。

Ceph 有 3 个重要参数会对条带化产生影响:
NOTE:由于客户端会指定单个 Pool 进行写入,所以条带化到 Objects 中的所有数据都会被映射在同一个 Pool 包含的 PGs 内。
客户端可以向 Object 注册持久的监听,并保持与 Primary OSD 的会话,这就是客户端对 Object 的监视与通知性。该特性使得监听同一个 Object 的客户端之间可以使用 Object 来作为通信的渠道。

客户端的独占锁特性提供一种可以对 RBD 块设备进行 “排它性的” 锁定,这有助于解决多个客户端对同一个 RBD 块设备进行操作时,多个客户端尝试同时向同一个 Object 写入数据导致的冲突。独占锁特性需要依赖客户端对 Object 的监视与通知特性,在数据写入时,如果一个客户端首先在 Object 上放置了独占锁,则能够被其它客户端在写入数据前检查到,并放弃数据写入。设置了这一特性的话,同一时刻只有一个客户端能够对 RBD 块设备进行修改,常被应用到快照创建、快照删除这种改变块设备内部结构的操作。强制的独占锁功能特性默认是不开启的,需要在创建镜象时显式的通过选项 --image-features 启用。
客户端可以在数据写入到 RBD Image 时会跟踪这些 Object(建立映射索引),从而让客户端可以在数据读写时就能知道相应的 Objects 是否存在,省去了遍历 OSD 以确定 Objects 是否存在的开销。Object 的映射索引保存在 librbd 客户端的内存中。该特性对一些 RBD Image 操作比较有利:
Object 映射索引特性默认也是不开启的,同样需要在创建镜象时显式的通过选项 --image-features 启用。
可控的、可扩展的、分布式的副本数据放置算法。本质是一种伪随机数据分布算法,类似一致性哈希,是 Ceph 的智能数据分发机制,管理着 PG 在整个 OSD 集群的分布。CRUSH 的目的很明确, 就是一个 PG 如何与 OSD 建立关系,它是 Ceph 皇冠上的宝石。
传统物理存储设备的存储机制都涉及存储原始数据及其元数据,元数据存储了原始数据存储在存储节点和磁盘阵列的地址信息。每一次有新数据写入存储系统时,元数据最先更新,更新的内容就是原始数据将会存放的物理地址,在此之后才是原始数据的写入。Ceph 则抛弃了这种存储机制,使用 CRUSH 算法动态计算用于存储 Object 的 PG,同时也用于计算出 PG 的 OSD Acting Set(Acting Set 即为活跃的 OSD 集合,集合中首编号的 OSD 即为 Primary OSD)。CRUSH 按需计算元数据,而不是存储元数据,所以 Ceph 消除了传统的元数据存储方法中的所有限制,具有更好的容量、高可用性和可扩展性。
除此之外,CRUSH 还具有独特的基础设施感知能力,可以识别整个基础设施中的物理组件拓扑(故障域和性能域),包括磁盘、节点、机架(Rack)、行(Row)、开关 、电源电路、房间、数据中心以及存储介质类型等多种 CRUSH bucke 类型,bucket 表明了设备的具体物理位置。这种感知能力使得 CRUSH 可以让客户端进行跨故障域写入数据,以此来保证数据的安全性。CRUSH bucke 包含在 CRUSH Map 中,CRUSH Map 还保存了一系列可定义的规则(CRUSH Rules),告诉 CRUSH 如何为不同的 Pool 复制数据。CRUSH 使得 Ceph 能够自我管理和自我疗愈。当故障区域中的组件故障时,CRUSH 能够感知哪个组件故障了,并自动执行相应的数据迁移、恢复等动作。使用 CRUSH,我们能够设计一个没有单点故障的高度可靠的存储基础设施。CRUSH 也可使客户端能够将数据写入特定类型的存储介质中,例如:SSD、SATA。CRUSH Rules 决定了故障域以及性能域的范围。

对象。是 Ceph 的最小存储单元,每个 Object 都包含了在集群范围内唯一的标识、二进制数据、以及由一组键值对组成的元数据。绑定在一起的原始数据与元数据,并且具有 RADOS 全局唯一的标识符 OID。无论上层应用的是 RBD、CephFS 还是 RADOSGW,最终都会以 Object 的方式存储在 OSD 上。当 RADOS 接收到来向客户端的数据写请求时,它将接收到的数据转换为 Object,然后 OSD 守护进程将数据写入 OSD 文件系统上的一个文件中。

归置组,简称 PG。PG 是一组 Objects 的逻辑集合。一个 Ceph 存储池可能会存储数百万或更多的数据对象。因为 Ceph 必须处理数据持久化(副本或纠删码数据块)、清理校验、复制、再平衡以及数据恢复,因此以 Object 作为管理对象就会出现扩展性和性能上的瓶颈。Ceph 通过引入 PG 层来解决这个问题。CRUSH 分配每个 Object 到指定的 PG 中,每个 PG 再映射到一组 OSD 中。PG 是保障 Ceph 可伸缩性和性能必不可少的部分。当数据要写入 Ceph 时,首先会将将数据分解成一组 Objects,然后根据 Object 名称、复制级别和系统中的总 PG 数等信息执行散列操作并生成 PG ID,最后依据 PG ID 将 Objects 数据分散到各个 PG 中。没有 PG,在成千上万个 OSD 上管理和跟踪数以百万计的 Objects 的复制和传播是相当困难的。将包含大量 Objects 的 PG 作为管理和复制的对象,可以有效缩减计算资源的损耗。每个 PG 都会消耗一定的计算资源(CPU、RAM),所以存储管理员应该
精心计算集群中的 PG 数量。
上文中我们提到过,PG 数会在一定程度上影响存储性能,一个 OSD 的 PG 数可以被动态修改,但建议在部署规划时期就能够对 PG 数有一定的把握。常见的,有以下几种 PG 数计算方式:
结果取最接近 2 的 N 次幂,比如:集群有 160 个 OSD 且副本数为 3,那么根据公式计算得到的 PG 总数是 5333.3,再舍入这个值到最接近的 2 的 N 次幕,最终结果为 8192 个 PG。
结果同样取最接近 2 的 N 次幂。
PG - Placement Group
PGP - Placement Group for Placement purpose
pg_num - number of placement groups mapped to an OSD
When pgnum is increased for any pool, every PG of this pool splits into half, but they all remain mapped to their parent OSD.Until this time, Ceph does not start rebalancing. Now, when you increase the pgpnum value for the same pool, PGs start to migrate from the parent to some other OSD, and cluster rebalancing starts. This is how PGP plays an important role.
顾名思义,PGP 是为了实现定位而设置的 PG。PGP 的数量应该与 PG 总数保持一致。对于一个 Pool 而言,当你增加了 PG 的数量,这个 Pool 的 PG 同时还应该修改 PGP 的数量,让两者保持一致,这样集群才能够触发再平衡动作。
OSD 守护进程会为每个 PG 的副本执行 Peering 操作,确保 PG 对应的主从 OSDs 之间的 PG 副本是一致的。这些主从 OSDs 以 Acting Set 的形式组织起来,Acting Set 的第一个元素就是 Primary OSD,保存着 PG 的主副本。其余为 Replica OSDs 中保存的是 PG 的第二/第三副本(假设副本数为 3)。Primary OSD 负责 Replica OSDs 之间的 PG 的 Peering 操作。当 Primary OSD 状态为 down 时,首先会从 Up Set 中移除,然后由第二 OSD 晋升为 Primary OSD,故障 OSD 中的 PG 会以同步到其他 OSD 的方式还原,并将新的 OSD 加入到 Actin Set 和 Up Set 中,以确保整个集群的高可用性。

Pool
存储池。是一个面向管理员的、用来隔离 PGs 和 Objects 的逻辑分区。简单来说,Pool 就是一个管理员自定义的命名空间,不同的 Pool 可以具有完全不同的数据处理方式,例如:Replica Szie(副本数)、PG Num、CRUSH Rules、快照、所属者及其授权等。可以为特定类型的数据创建存储池,比如:块设备、对象网关,亦或仅仅是为了多用户隔离而创建 Pool。
管理员可以为 Pool 设置所有者和访问权限,还支持快照功能。当用户通过 API 来存储数据时,需要指定 Objects 要存储到哪一个 Pool 中。管理员可以对不同的 Pool 设置相关的优化策略,比如 PG 副本数、数据清洗次数、数据块及 Object 的 Size 等等。Pool 提供了一个有组织的存储管理方式。每个 Pool 都会交叉分布在集群节点的 OSDs 上,这样能够提供足够的弹性。除了 PG 副本数,也可以通过指定纠删码的规则集来提供与副本数同等级别的可靠性,而且只消耗更少的空间。
当把数据写人到一个 Pool 时,首先会在 CRUSH Map 找到与 Pool 对应的规则集,这个规则集描述了 Pool 的副本数信息。在部署 Ceph 集群时会创建一些默认的存储池(e.g. data、metadata、rbd)。
Pool 也存在容量的概念,但 Pool Size 只在限制最大容量或者 QoS 的场景中有用,并不是真实的容量,因为 Pools 与 OSDs 之间的隐射是交叉的,Pools 的总容量大于 Ceph Cluster GLOBAL Size。所以 Pools 的总容量是没有意义的。


此处不妨小结一下 Ceph 每个内部构件的设计含义及其存在的意义。以 Object 作为 Ceph 的最小存储单元充分地展示了 Ceph 宏大的野心 —— 要成为新一代(或者成为云时代)的存储架构。众所周知,元数据是关于原始数据的信息,它决定了原始数据将往哪里存储,从哪里读取。传统的存储系统通过维护一张集中式的查找表来跟踪它们的元数据,这直接导致了传统存储系统查找性能低(容量有限)、单点故障(可靠性低)、数据一致性(可扩展性低)等问题。为了实现这一目标,Ceph 就必须打破传统存储系统的桎梏,采用更加智能的元数据管理方法,以离散的方式来存储原始数据及其元数据,再以动态计算的方式来定位数据的所在,这就是 Object + CRUSH。在此基础之上,RADOS 的引入就是为了解决分布式运行环境中的数据一致性、高可用性和自我管理等问题;PG 的引入就是为了缩小 RADOS 的管理对象以及 Objects 的遍历寻址空间,进一步提升性能和降低内部实现复杂度,在进行数据迁移时,也是以 PG 作为迁移单位。有了 PG 之后 Ceph 会尽量避免直接操作 Objects;Pool 的引入就是为了抽象出更加友好的资源管理与操作模型,例如:PG 的数量以及 PG 的副本数都是以 Pool 为单位设定的,提供了良好的用户操作体验。在如此可靠、自动、分布式的对象存储系统之上再构建 librados、RBD、RGW、CephFS 等多种更便于应用或客户端使用的上层接口类型,真正实现了下层抽象统一、上层异构兼容的统一存储解决方案。回头再看,Ceph 成功的原因很简单,其 SDS(软件定义存储)基因所带来的 “可编程性” 不正是云时代所渴望的吗?
Ceph 存储系统中,数据存储分三个映射过程:
Step 1. 将要存储的 File 数据映射(切分)为 RADOS 可以处理的 Objects(1:N):(ino, ono) -> oid
Step 2. 将 Objects 映射到 PG(N:1):hash(oid) & mask -> pgid,Ceph 指定一个静态 HASH 函数将 oid 映射成一个近似均匀分布的伪随机值,然后将这个值与 mask 按位相与,得到 PG ID。
Step 3. 将 PG 映射到 OSDs(N:M):CRUSH(pgid) -> (osd1,osd2,osd3),传入 pgid 到 CRUSH 算法计算得到一组 OSD 数组(长度与 PG 副本数相同)。
File 数据最终分散保存在这些 OSDs 中。
调度算法的伪代码:
当客户端读写数据时,首先需要与 MON 节点建立连接并下载最新的 Cluster Map。客户端读取 Cluster Map 中的 CRUSH Map(包含 CRUSH Rules、CRUSH bucke)和 OSD Map(包含所有 Pool 的状态和所有 OSDs 的状态),Crush Rules 就是数据映射的策略,决定了每个 Object 有多少个副本,这些副本应该如何存储,当把数据写入一个 Pool 时,可以通过 Pool name 匹配到一个 CRUSH Rules。客户端通过这些信息在本地执行 CRUSH 算法获得数据读写的 Primary OSD 的 IP:Port,然后就可以与 OSD 节点直接建立连接来传输数据了。CRUSH 动态计算的动作发生在客户端,不需要有集中式的主节点用于寻址。客户端分摊了这部分工作,进一步减轻了服务端的工作负载。

首先我们需要了解到 Ceph 的读写操作采用了主从模型,当客户端读写数据时,都只能向 Object 对应的 Primary OSD 发起请求。同时 Ceph 还是一个 强一致性 分布式存储,也就是说保证分布式数据的一致性同步过程并非异步操作。只有当所有的主从 OSD 都写入数据之后才算是一次成功的写入。客户端使用 CRUSH 算法计算 Object 映射的 PG ID 以及 Acting Set 列表。Acting Set 的首元素就是 Primary OSD,剩下的为 Replica OSD。

强一致性的数据同步方式在带来了高度的数据一致性的同时也存在一些缺点,比如:写性能低。为了提高写性能,Ceph 采用了一种普遍的应对方式 —— 日志缓存的机制。
当有突发的写入峰值时,Ceph 会先把一些零散的、随机的 IO 请求保存到缓存中进行合并,然后再统一向内核发起 IO 请求。这种方式有效提升了执行效率,但一旦 OSD 节点崩溃,缓存中的数据也会随之丢失。所以,Ceph OSD 包含两个不同的部分:OSD 日志部分(journal 文件)和 OSD 数据部分。每一个写操作都包含两个步骤:首先将 Object 写入日志部分,然后再将同一个 Object 从日志部分写人数据部分。当 OSD 崩溃后重新启动时,会自动尝试从 journal 恢复因崩溃而丢失的缓存数据。因此,journal 的 IO 是非常密集的,而且由于一个数据要 IO 两次,很大程度上也损耗了硬件的性能。从性能的角度来看,在生产环节中建以使用 SSD 存储来日志。使用 SSD 可以通过减少访问时间和读取延迟实现吞吐量的显著提升。
如果使用 SSD 来存储日志,我们可以在每一个物理 SSD 磁盘上创建一个逻辑分区来作为日志分区,这样每个 SSD 的日志分区就可以映射到一个 OSD 的数据分区,journal 文件的大小默认为 5G。在这种部署方案中,切记不要在同一个 SSD 上存储过多的日志以避免超出它的上限而影响到整体性能。建议在每一个 SSD 磁盘上不应该存储超过 4 个 OSD 的日志。但需要注意的是,使用单一 SSD 磁盘来存储多个日志的另一个坏处是容易发生单点故障,所以也建议使用 RAID1 来避免这种情况。


心跳机制是最常见的故障检测机制,Ceph 使用心跳机制来检测 OSD 是否正常运行,以便及时发现故障节点并进入相应的故障处理流程。OSD 的状态(Up/Down)反映了其健康与否,OSD 加入到集群后会定期的上报心跳到 MON,但假如 OSD 已然故障的话就不会在继续上报自己为 Down 的状态了,所以具有关联关系的 OSDs 之间也会互相评判对方的状态,如果发现对方 Down 掉了,则会协助上报到 MON。当然了,MON 也会定期的 Ping 这些 OSD 以此来确定它们的运行情况。Ceph 通过伙伴 OSD 汇报失效节点以及 MON 统计来自 OSD 的心跳检测两种方式判定 OSD 节点是否失效。
OSD 服务进程会监听 Public、Cluster、Front 和 Back 四个端口:

OSD 心跳机制的特征:


将一个新的 OSD 添加到 Ceph 集群时,Cluster Map 也会更新,这一变化会改变 CRUSH 计算时输入的参数,也就间接的改变了 Object 放置的位置。CRUSH 算法是伪随机的,但会均匀的放置数据,所以 CRUSH 会开始执行再平衡操作,并且会让尽量少的数据发生迁移。一般迁移的数据量是集群总数据量与 OSD 数量的比值,例如:在有 50 个 OSD 的集群中,当新增加一个 OSD 时,就只会有 1/50(2%)的数据会被迁移。并且所有旧 OSD 会并行地移动数据,使其能够迅速的完成。在生产环境中对于利用率高(刷写量大速快)的 Ceph 集群,建议先将新的 OSD 权重设为 0,再逐渐增加权重。通过这种方式,能够减少再平衡操作对 Ceph 集群性能有较大的影响。再平衡机制保证了所有磁盘能能够被均匀的使用,以此提升集群性能和保持集群健康。
扩容前:

扩容后:

上图可见,虚线的 PGs 会自动迁移到新的 OSD 上。
数据擦除是 Ceph 维护数据一致性以及整洁性的手段。OSD 守护进程会完成 PG 内的 Object 清理工作,它会比较副本间 PGs 内的 Object 元数据信息,捕获异常或文件系统的一些错误,这类 Clean 是 Day 级别的调度策略。OSD 还只会更深层次的比较,对数据本身进行按位比较,这种深层次的比较可以发现驱动盘上坏的扇区,一般是 Week 级别的调度策略。
原文:https://www.cnblogs.com/deny/p/12904404.html