云原生技术日益普及的今天,在 Kubernetes 上运行无状态应用已经非常成熟,平滑扩展能力也很强,但对于有状态的应用,数据需要持久化存储,面临着很多挑战。

目前市场上一些主要的云原生存储方案:

  1. Ceph on Kubernetes with Rook
    Ceph 是基于 RADOS (Reliable Autonomic Distributed Object Store) 的高可用存储,在云原生时代之前已经广泛生产部署的高可用存储,支持块存储RBD、文件存储 Cephfs,以及对象存储访问协议。
    RedHat/SUSE 目前是 Ceph 最主要的商业化支持者,在多个容器平台落地案例中,RBD、 CephFS 都被采用作为容器平台实施的主要存储,用来弥补基础云存储的缺失。而 Rook 目前是在 Kubernetes 产品级可用的部署和运维 Ceph 编排工具。
  2. Portworx
    以容器服务的方式部署,每个节点称为PX,向下对接各种公有云的块存储或者裸金属服务器,向上提供块或文件服务。
  3. OpenEBS
    OpenEBS 托管于 CNCF 基金会,目前该项目处于沙箱阶段,OpenEBS 是一种模拟了AWS的EBS、阿里云的云盘等块存储实现的开源版本,其核心理念是存储和应用一样采用微服务架构,并通过 Kubernetes 来做资源编排。其架构实现上,每个卷的 Controller 都是一个单独的 Pod,且与应用 Pod 在同一个 Node,卷的数据使用多个 Pod 进行管理、

先让我们看看 Ceph on Kubernetes with Rook~

一、概述

1.1. 什么是 Ceph?

Ceph是一个统一的分布式存储系统,其设计初衷是提供较好的性能、可靠性和可扩展性。随着云计算的发展,Ceph 乘上了OpenStack 的春风,进而成为了开源社区受关注较高的项目之一,目前己得到众多云计算厂商的支持并被广泛应用。

1.1.1. 优势

  1. 高性能

    • 摒弃了传统的集中式存储元数据寻址的方案,采用 CRUSH 算法,数据分布均衡,并行度高
  2. 高可用

    • 副本数可以灵活控制

    • 数据强一致性

    • 具备自愈自治能力

    • 没有单点故障,自动管理

  3. 高扩展性

    • 去中心化

    • 扩展灵活

  4. 特性丰富

    • 统一存储,Ceph 可以同时提供对象存储、决存储RBD、文件系统存储 Ceph FS。

    • 支持自定义接口,支持多种语言驱动

1.2. 什么是 Rook?

Rook 目前是 Kubernetes 的开源的云原生存储协调器,为不同的存储解决方案提供平台、框架和支持,以便与云原生环境自然整合。

截屏2022-08-11 21.45.34

Rook 深度整合到云原生环境中,将存储软件变成自我管理、自我扩展和自我修复的存储服务,使用底层云原生容器管理、调度和编排平台提供的设施来履行其职责。

Rook 由 Cloud Native Computing Foundation (CNCF) 托管,是一个毕业项目。

1.2.1. Rook 组件

  1. Operator
    实现自动启动存储集群,并监控存储守护进程,并确保存储集群的健康;

  2. Agent
    在每个存储节点上运行,并部署一个 CSI / FlexVolume 插 件,和 Kubernetes 的存储卷控制框架进行集成。
    Agent 处理所有的存储操作,例如挂载存储设备、加载存储卷以及格式化文件系统等;

  3. Discovers
    检测挂接到存储节点上的存储设备

Rook 将 Cenh 存储服务作为 Kubernetes 的一个服务进行部署,MON、 OSD、 MGR 守护进程会以 pod 的形式在Kubernetes 进行部署,而 rook 核心组件对 ceph 集群进行运维管理操作。

换句话说,Rook就是一组 Kuberetes 的 Operator,它可以完全控制多种数据存储解决方案(例如 Ceph、 Cassandra)的部署,管理以及自动恢复。

二、设计理念

Ceph 根据场景可分为对象存储、块设备存储和文件存储。

截屏2022-08-11 08.03.30

2.1. 核心组件

Ceph 存储集群核心组件包括 Ceph Monitor和 OSD 守护进程,当 Ceph 存储集群设定的数据有两个副本时(一共存两份),则至少需要两个OSD守护进程即两个OSD节点,集群才能到达active+clean状态,而运行 Ceph 文件系统客户端时,则必须要有元数据服务器 MDS (Metadata Server)。

2.1.1. OSD

OSD 用于集群中所有数据与对象的存储。处理集群数据的复制、恢复、回填、再均衡。并向其他 OSD 守护进程发送心跳,向 Monitor 提供一些监控信息。

2.1.2. MDS (可选)

为 Ceph 文件系统提供元数据计算、缓存与同步。在 Ceph 中,元数据也是存储在 OSD 节点中的,MDS 类似于元数据的代理缓存服务器。MDS 进程并不是必须的进程,只有需要使用 CephFS 时,才需要配置 MDS 节点。

2.1.3. Monitor

监控整个集群的状态,维护集群的 Cluster MAP,保证集群数据的一致性。

Cluster MAP 描述了对象块存储的物理位置,以及一个将设备聚合到物理位置的桶列表。

2.2. 架构设计

Ceph 整体采用分层架构,其最底层是是 RADOS 集群,实现了分布式的基本特征,如数据可靠性保护(副本或者纠删码)、分布式一致性和故障检测与恢复等,在 RADOS 集群之上,Ceph构建了块存储、文件存储和对象存储等存储形态。同时,Ceph 集群为客户端提供了丰富的访问形式,对于块存储可以通过动态库或者块设备的方式访问。

所谓动态库,是指 Ceph 提供了一个APl,用户通过该API可以访问块存储系统。比如用于虚拟化常见的qemu对Ceph的访问就是通过动态库(librbd)的方式访问的。

截屏2022-08-11 06.40.35

2.2.1. 基础存储系统 RADOS

Reliable, Autonomic,Distributed Object Store,即可靠的、自动化的、分布式的对象存储系统,所有存储在 Ceph 系统中的用户数据事实上最终都是由这一层来存储的。而 Ceph 的高可靠、高可扩展高性能、高自动化等特性本质上也是由这一层所提供的。

2.2.2. 基础库 lirados

这层的功能是对 RADOS 进行抽象和封装,并向上层提供 APl,以便直接基于 RADOS(而不是整个Ceph)进行应开发。特别要注意的是,RADOS是一个对象存储系统,因此,librados实现的API 也只是针对对象存储功能的。

2.2.3. 高层应用接口

这层包括了三个部分:RADOS GW (RADOS Gateway) RBD (Reliable Block Device) 和CephFS (Ceph File System),其作用是在 librados 库的基础上提供抽象层次更高、更便于应用或客户端使用的上层接口。其中,RADOS Gw是一个提供与Amazon S3和Swift兼容的RESTful AP/的gateway,以供相应的对象存储应用开发使用。RADOS GW提供的API抽象层次更高,但功能不如librados强大。

2.2.4. 应用层

这层是不同场景下对于Ceph各个应用接口的各种应用方式,例如基于librados直接开发的对象存储应用,基于 RADOS GW开发的对象存储应用,基于RBD实现的云硬盘等等。

librados 和 RADOSGW 的区别在于,librados 提供的是本地 API,而 RADOS Gw 提供的则是 RESTfulAPl。

三、数据存储

Ceph 支持对象、块以及文件存储,无论使用哪种存储方式,存储的数据都会被切分成对象 (Objects)。

Obiects size大小可以由管理员调整,通常为 2M 或者 4M

每个对象都会有一个唯一的 OID,有 ino 和 ono 生成

  • ino 是文件的File ID,用于全局唯一标示每一个文件
  • ono 是分片的编号

例如,一个文件 FileID 是A,它被切成两个对象,一个编号为0,另一个编号为1,那么这两个对象的 OID 则为A0和A1。OID 的好处是可以唯一标示每一个不同的对象,并且存储了对象与文件的从属关系。

如此多的对象光是遍历寻址速度都会很缓慢;而且如果将对象直接通过某种国定映射的哈希算法映射到 OSD 上, 但是对象并不会直接存储进 OSD 中,因为对象的 size 很小,在一个大规模的集群中可能有几百到几千万个对象。当这个 OSD 损坏时,对象无法自动迁移到其他 OSD 上面,为了解决这些问题,Ceph 引入了归置组 PG。

截屏2022-08-11 07.29.20

3.1. PG

PG 是一个逻辑概念,Linux 系统中可以直接看到对象,但是无法直接看到 PG。PG 在数据寻址时类似于数据库中的索引:每个对象都会固定映射进一个 PG 中,当寻找一个对象时,只需要先找到对象所属的 PG,然后遍历这个 PG 就可以啦~,无需遍历所有对象。而且在数据迁移时,PG 作为基本单位进行迁移,Ceph 不会直接操作对象。

3.2. 对象映射

那么对象是如何映射进 PG 的?

截屏2022-08-12 21.02.00

首先使用静态 hash 函数对OID做hash取出特征码,用特征码与PG的数量取模,得到的序号就是PGID。

由于这种设计,PG的数量多少直接决定了数据分布的均匀性,所以合理设置的PG数量可以很好地提升Ceph集群的性能并使数据均匀分布。

PG会根据管理员设置的副本数量进行复制,然后通过CRUSH算法存储到不同的OSD节点上,第一个 OSD 节点即为主节点,其余均为从节点。

3.3. Pool

和 PG 一样,POOL 也是一个逻辑存储概念,创建存储池 POOL 时,需要指定 PG 和 PGP 的数量

就像 Object 是属于某个PG 一样,逻辑上 PG 属于某个 POOL

POOL是管理员自定义的命名空间,像其他的命名空间一样,用来隔离对象与PG。调用API 进行对象存储时,需要指定对象要存储进哪一个 POOL 中。除了隔离数据,也可以分别对不同的 POOL 设置不同的优化策略,比如副本数、数据清洗次数、数据块及对象大小等。

在创建 CephFS 时,要至少创建两个Pool,一个用于存储数据,另一个用于存放元数据。MDS 只是负责接收用户的元数据查询请求,然后从OSD中把数据取出来映射进自己的内存中供客户访问。

MDS类似一个代理缓存服务器,替OSD分担了用户的访问压力

四、读写数据流

Ceph 的读写操作采用主从模型,客户端要读写数据时,只能向对象所对应的的主 OSD 节点发起请求。保证了数据的强一致性,主节点在接受写请求时,会同步的向从OSD中写入数据。当所有的OSD节点都写入完成后,主节点才会向客户端报告写入完成的信息,因此保证了主从节点数据的一致性。

4.1. 写数据

当客户端需要向 Ceph 集群写入一个File时,首先需要在本地完成寻址流程,将 File 变为一个Object,然后找出存储该Object的一组共3个OSD,序号最靠前的那个OSD就是这一组中的Primary OSD,而后两个则依次SecondaryOSD 和 Tertiary OSD。

  • 找出3个OSD后,客户端将直接和Primary OSD进行通信,发起写入操作
  • Primary OSD收到请求后,分别向Secondary OSD和Tertiary OSD发起写人操作。
  • 当 Secondary OSD 和 Tertiary OSD 各自完成写入操作后,将分别向 Primary OSD 发送确认信息。
  • 当 Primaly OSD 确认其他两个 OSD 的写入完成后,则自己也完成数据写入,并向客户端确认Object写入操作完成
截屏2022-08-12 07.39.24

4.1.1. 如何处理延迟🤔️?

采用这样的写入流程,本质上是为了保证写入过程中的可靠性,尽可能避免出现数据丢失的情况。但是,这种可靠性机制必然导致较长的延迟。因此,Ceph可以分两次向客户端进行确认。当各个OSD都将数据写入内存缓冲区后,就先向客户端发送一次确认,此时客户端即可以向下执行。待各个OSD都将数据写入磁盘后,会向客户端发送一个最终确认信号,此时客户端可以根据需要删除本地数据。

4.1.2. 单个 OSD 会变成性能瓶颈吗?

在正常情况下,客户端可以独立完成 OSD 寻址操作,而不必依赖于其他系统模块。因此,大量的客户端可以同时和大量的OSD进行并行操作。同时,如果一个 File 被切分成多个 Object,这多个 Object 也可被并行发送至多个 OSD 上。
从 OSD 的角度来看,由于同一个 OSD 在不同的 PG 中的角色不同,因此,其工作压力也可以被尽可能均匀地分担,从而避免单个OSD变成性能瓶颈。

4.2. 读数据

读取数据时,客户端会向主OSD节点发送读请求,所有写操作都要交给主OSD节点来处理,在数据量很大的时候,性能会比较慢,为了克服这个问题以及让 Ceph 能支持事务,每个 OSD 节点都包含一个 Journal 文件,默认大小为5G

也就是说创建一个OSD节点,还没使用就要被 Journal占用5G的空间,这个值是可以调整的,具体大小要依据OSD的总大小而定。

Journal的作用类似于MySQL innodb引擎中的事务日志系统。当有突发的大量写入操作时,Ceph 可以先把随机的 IO 请求保存到缓存中进行合并,然后在同一向内核发起 IO请求。这样做效率会比较高,但是一旦 OSD节点崩溃,缓存中的数据就会丢失,所以数据在还未写进硬盘时,都会记录到Journal 中,当OSD崩溃后重新启动时,会自动尝试从 Journal 恢复因崩溃而丢失的缓存数据。

因此 Journal 的 IO 是非常密集的,而且由于一个数据 IO 两次,很大程度上也损耗了硬件的IO性能,所以通常在生产环境中,使用ssd来单独存储 journal文件以提高 Ceph的读写性能。

五、监控 Monitor

Monitor 节点中保存了最新的版本集群数据分布图 (Cluster Map) 的主副本,Monitor 节点之间使用Paxos算法来保持各节点Cluster Map的一致性,Monitor的主要任务就是维护集群视图的一致性,并将其实例化到数据库中,方便后续的访问。

Monitor基本 由三部分组成:

  1. 管理各种 map 和相关内容的PaxosService实例
  2. Paxos实例
  3. 对k/v store的封装,即 MonitorDBStore 实例。
截屏2022-08-12 07.22.57

Monitor 节点并不会主动轮询各个 OSD 的当前状态,相反,OSD只有在一些特殊情況下才会上报自己的信息,平常只会简单的发送心跳。
特殊情况包括:

  1. 新的OSD被加入集群
  2. 某个OSD发现自身或其他OSD发生异常。

Monitor 节点在收到这些上报信息时,则会更新Cluster Map信息并加以扩缩。Cluster Map信息是以异步且lazy的形式扩散的。Monitor并不会在每一次Cluster Map版本更新后都将新版广播至全体OSD,而是有OSD向自己上报信息时,将更新恢复给对方。类似的,各个OSD也是在和其他OSD通信时,如
果发现对方的OSD中持有的Cluster Map版本较低,则把自己更新的版本发送给对方。