UCloud 在 2015 年推出了为云主机磁盘提供继续数据保护(CDP)的数据方舟(UDataArk)产品,反对最小准确到秒级的复原,针对数据删除或者失落事件,可能最大水平的挽回数据。数据方舟曾经在多个数据安全案例中失去利用,并失去了泛滥客户的认可。
近些年,随着用户高性能存储场景需要的增多,SSD 云盘和 RSSD 云盘成为支流抉择,然而数据方舟只针对本地盘及一般云盘,SSD 云盘和 RSSD 云盘不足高效的备份伎俩成为用户的痛点。为此咱们推出了磁盘快照服务(USnap),USnap 基于数据方舟 CDP 技术并进一步降级,以更低的老本为全系列云盘(一般 /SSD/RSSD)提供了数据备份性能。
如何接入 SSD/RSSD 云盘等高性能设施以及如何升高间断数据保护性能的实现老本,是 USnap 产品要解决的两个外围问题。这不仅仅须要在数据方舟架构层面上做出改良,所有 IO 门路的相干模块也须要做从新设计。本文将具体介绍 USnap 是如何应用数据方舟 CDP 技术并对其降级革新的技术细节。
Client 捕捉用户写 IO
方舟备份存储集群独立于 UDisk 存储集群,是咱们重要的设计前提,这保障了即便呈现了 UDisk 集群遭逢故障而导致数据失落的极其事件,用户仍能从备份存储集群中复原数据。对此,咱们实现了一个 ark plug-in,集成到了 UDisk 的 client 中,这个 plug-in 会异步的捕捉 UDisk 的写 IO,并将其推送到方舟备份存储集群。
如何高效的捕捉 UDisk IO 是个重要的问题,咱们心愿对 UDisk 的 IO 门路影响到最低。对于 SSD UDisk client 和 RSSD UDisk client,IO 的捕捉模式是齐全不同的。
对于 SSD UDisk,Bdev 线程在承受一个 IO 后,先提交到 UDisk 的 IO 线程中,如果是写 IO 还须要推送至方舟备份存储集群。对此 Bdev 线程会构建一个 ArkIORequest,拷贝一份蕴含 data 的智能指针对象,退出到无锁队列中。ArkHandle 线程从无锁队列中获取 IO,转发给 ArkIO 线程进行推送。UDisk IO 实现后,无需期待方舟 IO 实现即可返回胜利。UDisk IO 和方舟 IO 均实现后,data 才会被开释。
对于 RSSD UDisk,因为采纳 SPDK Vhost 计划,Vhost 和 guest VM 共享内存,UDisk IO 实现后,data 内存空间会立刻被 guest VM 应用。为此咱们退出了一个 copy 线程,由 copy 线程从无锁队列中获取 bdev_io,进行数据 copy,数据 copy 结束后再构建一个 ArkIORequest 转发给 ArkIO 线程进行推送,方舟 IO 实现后 data 由方舟 plug-in 中的 ArkHandle 进行开释。
咱们模仿了各种类型的 IO 场景,钻研方舟 plug-in 对 UDisk 性能的影响。发现在低 io_depth 的场景下,方舟性能对于 UDisk 性能的影响最大不会超过 5%,在高 io_depth 的场景下,方舟性能对于 UDisk 性能的影响靠近 0%。可见方舟 plug-in 实现了高效的数据捕捉与转发,不会影响用户的线上业务。
块层 IO 能够了解为一个三元组(sector, sector_num, data),代表读写地位、读写大小和理论数据。对于 CDP 零碎,IO 的三元组信息是不够的,须要标记额定信息,才可能复原到任何一个工夫点。在数据捕捉时,所有的写 IO 都会标记好序列号(seq_num),序列号保障严格间断递增,这是咱们保障块级数据一致性的根底。并且所有的写 IO 也会打上工夫戳,方舟 plug-in 会保障即便在呈现时钟跳变的状况下,工夫戳也不会呈现回退。这样数据变动及其工夫戳都被保留下来,后端能够依据这些信息通过某种形式回放,复原到过来的任意时刻,这就是 CDP 技术的基本原理。在推送到方舟备份存储集群前,方舟 plug-in 会对 IO 进行合并,这能够显著缩小方舟接入层的 IOPS。
Front 实时 IO 接入层
方舟备份集群采纳分层存储,实时 IO 接入层应用大量的 NVME 等高速存储设备,承接海量实时 IO,实时 IO 会定期下沉到采纳大量 HDD 设施构建的容量存储层。方舟的接入层(Front)是整个数据方舟零碎的门户,其性能关系到是否接入 SSD/RSSD 云盘等高性能的设施。
原始的 Front 是基于 Log-structured 的设计,每块逻辑盘会被调配一组 Front 节点,对于一次简略的磁盘 IO 写入操作,client 将 IO 转发到 Primary Front 节点,Primary Front 节点将此次的 IO 追加写入到最新的 Log 中,并将 IO 同步到 Slavery Front 节点。
剖析可知该设计存在以下问题:1. 一块逻辑盘的实时 IO 只落在一组(Primary-Slavery)Front 节点上,所以零碎对于单块逻辑盘的接入性能受到 Front 单节点性能限度。这种设计是无奈接入 RSSD 云盘这种超高性能设施的。2. 尽管通过 hash 的形式将用户逻辑盘打散散布到整个接入层集群,然而可能呈现调配在同一组 Front 节点的多块逻辑盘同时存在高 IO 行为,由此产生了热点问题,尽管能够通过运维伎俩将其中的局部逻辑盘切换到闲暇的 Front 节点上,但这并不是解决问题的最佳形式。
针对于此,咱们提出了基于 Stream 数据流的设计,以满足高 IO 场景下业务对于接入能力的要求。Stream 数据流的概念即是将逻辑盘的所有写入数据抽象成为一段数据流,数据只在 Stream 尾部进行追加写。Stream 依照固定大小分片,每个分片依照一致性 hash 算法映射到一个归置组,归置组代表一个正本组,由存储资源依照肯定策略组成。这样就将一块逻辑盘的实时 IO 打散到了所有接入层集群上,这不仅解决了接入 RSSD 云盘这种超高性能设施的问题,同时还解决了接入层热点的问题。
Stream 数据流合乎 Buffer 的个性,即从尾部写入、从头部读出。咱们应用一组数据来标识 Stream 数据流的无效区域:read_offset 和 write_offset。当 Stream 有实时数据写入,write_offset 增长。Shuffle 模块会解决实时 IO 下沉到容量存储层的工作。Shuffle 会从 Front 定期拉取数据,在内存中进行分片(sharding),并组织为 Journal 数据,推送至上层的 Arker 容量存储层。推送 Arker 胜利后,read_offset 更新。对于曾经下沉到方舟 Arker 容量存储层的数据,咱们会对其进行回收以开释存储资源。
Arker 容量存储层
CDP 数据须要依照粒度(Granu)进行组织。依据业务须要,Granu 被分为 5 种类型:journal、hour、day、base 和 snapshot,journal 是秒级数据,蕴含用户的原始写申请;hour 代表小时级别的增量数据;day 代表天级别的增量数据;base 是 CDP 的最底层数据;snapshot 是用户的手动快照数据。Granu 会依照设定的备份策略进行合并。以默认的反对复原到 12 小时内任意一秒、24 小时内的任意整点以及 3 天内的任意零点为例,journal 至多会被保留 12 小时,超过 12 小时的 journal 会被合并为 hour,此时数据的 tick 信息会被抛弃,之后的工夫区间无奈再复原到秒级,超过 24 小时的 hour 会被合并为 day,超过 3 天的 day 会和 base 合并为新的 base,对于 snapshot 则会短暂保留除非用户被动删除了快照。
作为方舟的容量存储层,Arker 为 5 类不同的 Granu 提供了对立的存储;对于 5 种类型的 Granu,又存在 3 种存储格局:BASE Blob、CUT Blob 和 JOURNAL Bob。其中 base 和 snapshot 两类 Granu 以 BASE Blob 格局存储,day 和 hour 两类 Granu 以 CUT Blob 格局存储,journal 类型的 Granu 以 JOURNAL Blob 格局存储。
对于 journal、hour 和 day 三类 Granu,咱们间接按分片进行存储,每个有数据存在的分片都惟一对应了一个 inode 对象,这个 inode 对象关联一个 JOURNAL Blob 或 CUT Blob。对于 base 和 snapshot 两类 Granu,咱们将分片中的数据进一步细化,切分成一系列的 TinyShard 作为重删单元,每个 TinyShard 也会惟一对应一个 inode 对象,这个 inode 对象会关联一个 BASE Blob,数据雷同的 TinyShard 会指向同一个 inode 对象,复用 BASE Blob,由此达到了重删的目标。
为了进步合并效率,咱们还将索引和数据的存储进行拆散,以上所有业务元数据(Granu、Shard/TinyShard、Inode)都以 key-value 的模式存储在 KVDevice 中,Blob 数据通过压缩后存储在 FSDevice 中,数据压缩算法采纳 zstd 算法,比起原先应用的 snappy 算法,又节约了至多 30% 的存储老本。
一次残缺的回滚流程
整个回滚流程由调度模块 Chrono 进行管制。当用户指定了一个回滚工夫点,Chrono 首先通过查问 Granu 元数据确认该指标点数据命中的地位。命中地位只有两种状况,一种是指标点数据还在 Front 接入层,尚未被 Shuffle 推送至 Arker 容量存储层,另一种是曾经被 Shuffle 推送至 Arker 容量存储层。
如果是第一种状况,Chrono 会命令 Shuffle 被动拉取这部分数据至 Arker 容量存储层。在确认指标点数据曾经在 Arker 容量存储层后,Chrono 会查问获取到所有须要合并的 Granu 以及须要合并到哪个 seq_num,并散发合并工作至所有 Arker。Arker 容量存储层会对这些 Granu 进行合并,对于一个合并工作,会首先进行索引合并,随后会依据曾经合并实现的索引进行数据合并,合并实现后最终会生成一份新版本的 BASE,这就是复原后的全量数据。在失去复原后的全量数据后,再将数据写回到 UDisk 集群中。
咱们能够看到,数据合并阶段是以 shard 为单位并发进行的,能利用到所有容量层磁盘的 IO 能力;数据回吐 UDisk 阶段,也利用了方舟和 UDisk 都是分布式存储,能够采取分片并发对拷的形式将数据写入到 UDisk 集群。因而复原的 RTO 也能失去保障,1TB 的数据恢复工夫通常在 30min 以内。
总结
本文围绕着私有云 CDP 备份零碎如何构建、CDP 零碎如何接入高性能 IO 设施以及 CDP 零碎如何升高实现老本等几个次要问题,介绍了 UCloud 磁盘快照服务 USnap 在业务架构、存储引擎等多方面的设计思考和优化计划。
后续咱们还会在多个方面持续晋升磁盘快照服务 USnap 的应用体验。产品上将会提供能够自定义备份工夫范畴的增值服务,让用户能够自定义秒级、小时级、天级的爱护范畴,满足用户的不同需要。技术上,则会引入全量全删和 Erasure Coding 等技术进一步降低成本,以及应用 Copy On Read 技术放慢回滚速度,让用户可能享受到更先进技术带来的丰盛性能、性能晋升和价格红利。