Ceph最初一般都是部署在机械硬盘上,适用于数百IOPS的读写和数十G的磁盘容量。目前,存储和网络的硬件性能极速提高,NVMe设备能够提供数百万的IOPS读写,并支持TB级的磁盘容量,网卡设备也能提供更高的速度。但CPU只有核心数量在增加,单核频率和性能增长并不明显。
由于传统Ceph的架构使用线程池和队列的方式,在系统调用和锁竞争时需要消耗CPU,依赖于CPU单核的性能,导致Ceph不能跟上存储设备能力的提升。特别是在Ceph的对象存储守护进程(OSD),OSD需要频繁地通过线程池处理不同的IO,存在上下文切换和跨CPU核心的通信,会产生大量的延迟开销。为了减少或消除这些开销成本,Ceph打算基于Seastar框架重构OSD,称为Crimson OSD。
Seastar框架
目前在编程模型中,大家较为熟知的有pipeline模型和RTC(run to completion)模型。pipeline模型将一整个任务划分为一系列的阶段,不同阶段完成不同的事,每个阶段之间使用队列传递任务,这样的模型能够根据不同的任务类型(CPU操作密集型和IO操作密集型)来分配专用的处理单元。RTC模型采用单线程的reactor模型(基于事件驱动的线程模型,所有逻辑都在一个线程中执行),整个IO链路基本都在同一个核/线程上,直到结束。
由于pipeline每个阶段都需要消息传递,会增加CPU的消耗,使得pipeline模型无法跟随IO的发展,因此RTC模型重新进入大家的视线。RTC模型主要存在两个问题:
1、一个事件驱动的异步服务模型会涉及到许多回调函数和状态机的编写,十分复杂;
2、RTC线程中应尽量避免调用会造成阻塞的函数,如果在线程中发生阻塞,便会影响之后的IO请求。
Seastar是一个基于C++14、非阻塞异步的RTC事件驱动编程框架,很好地解决了上述的问题。Seastar完全是share-nothing的设计,每个逻辑CPU核对应一个thread,每个核都有自己的Memory、network stack、磁盘IO等,核之间也没有资源的竞争,整个IO链路都在同一个核上,减少了上下文切换、Cache miss、锁的开销,性能和吞吐随着核增长而线性增长。同时,利用Seastar的编程框架,可以使用同步编程的思维去编写异步程序,大大减少异步编程的复杂度。可以看出,Crimson OSD需要解决的问题可以通过Seastar框架来解决,因此Crimson OSD基于Seastar框架重构传统OSD,能够减少编程难度。
与传统OSD对比
Ceph OSD用于实现数据存储与维护。Ceph OSD利用节点的CPU、内存和网络来执行数据复制、重新平衡、恢复、监控和报告功能。Crimson OSD提供相同的接口和功能,从客户端和OSD的角度看与现有的RADOS协议兼容。
传统的OSD架构中,每个组件都有线程池,利用共享队列处理任务。一个IO操作先进入Messenger模块的线程池,由messenger worker线程处理,将原始数据流解码成消息,放入消息队列osd_op_queue进行调度。之后PG worker线程从消息队列中获取消息,将消息转换为事务交给ObjectStore。事务完成后,通过out_queue队列提交,messenger worker线程发送回复。这些线程之间默认共享资源,因此会引入锁竞争的问题,随着任务数和CPU核数的增加,锁竞争的开销更迅速扩大。因此,传统OSD性能无法跟着CPU数量的增加得到线性扩展。
Crimson OSD的Messenger、OSD服务和ObjectStore等模块的功能没有太大的变化。针对传统OSD无法随CPU扩展的问题,Crimson OSD通过shared-nothing设计和run-to-completion模型,强制每个core或CPU运行一个固定线程并在用户空间中分配非阻塞任务。如果IO请求以及它们的资源可以被分配到同一个核,在理想情况下,Crimson将不再需要所有的锁和上下文切换,因为每个正在运行的非阻塞任务都使用到CPU,一直到它完成任务。随着核数量增加,Crimson的性能线性提升。
Crimson OSD与BlueStore适配方案
BlueStore使用RocksDB存储元数据。RocksDB至少有两个线程,分别为落盘和压缩线程。两个线程的优先级不同,落盘线程的优先级别比压缩线程的优先级别高。在RocksDB中实现了线程池,承担创建、销毁线程的功能;RocksDB可以动态增加、减少线程池中的线程数目;RocksDB能够动态调整线程的获取CPU和IO的优先级。因此RocksDB线程模型在外部不可控,Crimson需要对BlueStore进行专门适配。Crimson OSD把BlueStore的线程指定在固定的CPU core中(除了SeaStar线程运行的CPU core),引入AlienStore将seastar线程的IO任务提交到BlueStore线程。
对于BlueStore的适配可以作为中间过渡方案,但BlueStore中的RocksDB无法在seastar框架中执行。因此基于Seastar模型重写一个存储引擎是更好的方案,于是便有了SeaStore。