- Curator: Self-Managing Storage for Enterprise Clusters翻译
Curator: Self-Managing Storage for Enterprise Clusters翻译
Curator:企业集群的自我管理存储系统
摘要
现代集群存储系统执行一系列的后台任务,以提高数据存储的性能、可用性、持久性和成本效益。例如,清理器压缩碎片数据来生成长时间连续的运行,分层服务根据使用情况自动在固态硬盘和机械硬盘之间迁移数据,数据恢复机制提前备份数据以提高故障时的可用性和耐用性,成本节省技术通过执行数据转换来降低存储成本。
在这项工作中,我们提出了Curator,这是在企业集群中使用的分布式存储系统的背景下用于集群管理任务的MapReduce后台执行框架。我们描述了Curator的设计和实现,并使用一些相关指标来评估其性能。我们进一步报告了在其五年建设期间的经验和教训,以及数以千计的客户部署。最后,我们提出了一个基于机器学习的模型,针对Curator的管理任务确定有效的执行策略,以适应不同的工作负载特征。
1.介绍
当今的集群存储系统为了满足企业集群的需求,会拥有各种功能。 例如它们提供自动备份和数据恢复以处理故障,支持扩展,并提供固态开盘和机械硬盘的无缝集成。 此外,它们通过快照和垃圾回收等机制支持适用于虚拟机的存储工作,并执行例如数据去重,压缩,擦除编码等节省空间的转换。
对这些任务仔细检查后发现许多功能可以在后台执行。 基于此结果,我们设计并实现了一个用于在企业集群存储中的后台自我管理层。 作为这项工作的一部分,我们解决了重新组合这样一个系统所面临的一系列工程和技术挑战。首先,我们需要一个可扩展、灵活的、可伸缩的框架来执行一系列的任务来维护存储系统的运行状况和性能。为此,我们借用了通常在不同领域中使用的大数据分析技术,构建了由两个主要组件组成的通用系统:
- Curator,一个用于集群管理任务的后台执行框架,其中所有任务都可以表示为对相应数据的MapReduce式操作
- 一个在多处备份且保持一致的key-value存储系统,用于维护存储系统中所有重要的元数据。
其次,我们需要在后台任务和前台任务之间进行适当的同步。 我们通过让后台守护程序充当存储系统的客户端来解决了这些同步问题,然后让后来者处理所有同步。 第三,要求对前台任务的干扰最小。 我们通过使用任务优先级和调度启发式方法来最大程度地减少开销和干扰,从而完成了这一任务。 由此产生的框架使我们能够实施各种后台任务,这些任务使存储系统能够连续执行一致性检查,并具有自我修复和自我管理的能力。
我们在Nutanix开发的商业企业集群的产品中执行了这项工作。 我们在五年的时间内开发了该系统,并将其部署在数千个企业集群中。 我们报告了系统的性能以及从构建和完善系统中获得的经验。 我们发现Curator有效地执行了垃圾回收和备份,平衡了各个磁盘的存储,并通过许多优化使存储访问效率更高。 而且我们意识到在简化了存储系统的构建下,该框架足够通用到可以在各种背景下进行转换。
尽管如此,我们注意到启发式方法不一定在所有集群中都有效,因为它们之间存在很大的异构性。 因此,我们最近开始开发一个基于机器学习的框架,以解决何时应该执行这些后台管理任务以及应该执行多少工作的问题。基于ML的方法有两个关键要求:1)较高的预测准确率;2)学习或适应(不断变化的)工作量特征的能力。我们提出使用强化学习,特别是Q-学习算法。我们最初的工作重点集中在以下分层问题上:在ssd和hdd中要保留多少数据?对五个模拟工作负载的实证评估证实了我们方法的总体有效性,并显示对延迟有了高达20%的改进。总之,我们的主要贡献是:
- 我们对Curator的设计和实现进行了广泛的描述,Curator是一种先进的分布式集群后台管理系统,它根据使用情况在存储层之间执行数据迁移、数据复制,磁盘平衡,垃圾回收等工作。
- 我们为了衡量Curator的优势使用了一系列相关指标,例如,在封闭的本地环境以及在客户部署和内部企业集群中的延迟、每秒的I/O操作(IOPS)、磁盘使用率等。
- 最后,我们提出一种基于强化学习的模型,以改善Curator的任务调度。 我们提供有关存储分层任务的经验结果,以展示我们解决方案的优点。
2 企业集群的分布式存储
我们在Nutanix为企业集群设计的分布式存储系统的环境中执行我们的工作。 在本节中,我们概述了软件体系结构,存储系统提供的关键功能以及用于支持它们的数据结构。 在这里我们提供了必要的背景资料,以了解Curator的设计。
2.1 集群体系结构
该软件体系结构是为各种规模的企业集群设计的。 Nutanix在数千个不同的客户位置部署了集群,集群的大小通常从几个节点到几十个节点不等。因为客户可以根据需要添加节点,所以集群节点可能具有异构资源。 集群支持(传统)应用程序的虚拟化执行,这些应用程序通常打包为VM。 集群管理软件为用户提供了一个创建,启动,停止和销毁VM的管理层。 此外,该软件会根据当前用户身份和单个节点的负载,自动调度和迁移VM。 这些任务由在每个节点上运行的Controller Virtual Machine(CVM,控制器虚拟机)执行。
这些CVMs共同形成一个分布式系统,该系统管理集群中的所有存储资源。 CVMs和它们管理的存储资源提供了分布式存储结构(DSF,distributed storage fabric)的抽象,该结构随节点数扩展,并提供对在集群中任何节点上运行的用户VM(UVM,user VM)的透明存储访问。 图1显示了集群体系结构的高级概述。
在UVM中运行的应用程序使用传统文件系统接口(例如NFS,iSCSI或SMB)访问分布式存储结构。 这些传统文件系统接口上的操作插入在管理程序层,并重定向到CVM。 CVM将显示为磁盘的一个或多个块设备导出到UVM。 这些块设备是虚拟的(它们由CVM内部运行的软件实现),称为vDisk。 因此,对于UVM,CVM似乎正在导出存储区域网络(SAN,storage area network),其中包含在其上执行操作的磁盘。UVM中的所有用户数据(包括操作系统)都驻留在这些vDisk上,并且最终将vDisk操作映射到位于群集内部任何位置的某些物理存储设备(SSDs或HDDs)。
尽管使用CVM在资源利用率方面带来了额外的开销,但也提供了很大的好处。 首先,它允许我们的存储堆栈在任何虚拟机监控程序上运行。 其次,它可以在不关闭节点的情况下升级存储堆栈软件。 为了支持此功能,我们在虚拟机监控程序级别实施了一些简单的逻辑,以将其I / O通过多路径有效的传输到能够满足存储请求的另一个CVM。 第三,它提供了角色的明确分离和更快的开发周期。 在管理程序(甚至内核)中构建复杂的存储堆栈将严重影响我们的开发速度。
2.2 存储系统和关联的数据结构
现在我们要描述DSF的几个关键要求,以及这些要求如何影响用于存储元数据和Curator的设计的数据结构。
- R1-可靠性/弹性:系统应能及时处理故障。
- R2-保存位置:数据应迁移到经常访问它的VM的节点。
- R3-分层存储:数据应在SSD,硬盘驱动器和公共云之间分层。 此外,SSD层不仅应充当热数据的缓存层,而且还要负责用户数据的永久存储。
- R4-快照:系统应允许用户快速创建快照以增强鲁棒性。
- R5-空间利用率:系统应在支持传统应用程序的同时实现高存储效率,而不必对文件大小或其他工作负载模式作出任何假设。
- R6-可扩展性:系统的吞吐量应该随着系统中节点的数量而扩展。
以上需求集在系统设计中以两种方式体现出来:(a)我们用于存储元数据的数据结构集,以及(b)系统将执行的管理任务集。 我们接下来讨论数据结构,将Curator执行的管理任务推迟到3.2中讲解。
每个vDisk(在2.1节中介绍的)都对应一个虚拟地址空间,该虚拟地址空间使用不同字节表示一个磁盘,并公开给用户VM。 因此,如果vDisk的大小为1 TB,则维护的相应地址空间为1TB。 该地址空间分为相等大小的单元,称为vDisk块。 每个存储在物理磁盘上的[vDisk块中的数据]都称为extent。 为了细化和提高效率,写入/读取/修改的操作在extent的子区域上(也称为切片)进行。 extent大小与vDisk块内的活动数据量相对应。 如果vDisk块包含未写入的区域,则extent的空间小于vDisk块空间(因此满足R5)。
【即vDisk和块代表磁盘空间,extent代表磁盘存储的数据】
多个extent被分组为一个单元称为extent组。 每个extent和extent组都分配有一个唯一的标识符,分别称为extent ID和extent组ID。 extent组是物理分配的单位,并作为文件存储在磁盘上,热extent组存储在SSD中,冷extent组存储在硬盘(R3)上。 extent和extent组在节点上动态分布,以实现容错,磁盘平衡和性能目标(R1,R6)。
给定以上核心构造(vDisk,extent和extent组),我们现在描述系统如何存储元数据,以帮助定位每个vDisk块的实际内容。系统维护的元数据包含以下三个主要映射:
-
vDiskBlock映射:将vDisk和偏移量(标识vDisk块)映射到extentID。这是一个逻辑映射。
-
extentID 映射:将extent映射到包含它的extent组里。这也是一个逻辑映射。
-
extentGroupID 映射:将extentGroupID映射到该extentGroupID的副本的物理位置及其当前状态。这是一个物理映射。
关于上述数据结构,这里有一些含义。 通过快照创建的多个vDisk可以共享同一extent。 快照的vDiskBlock映射可以直接指向与先前快照共享的extent,也可以缺少条目,在这种情况下,会参考上一个快照的vDiskBlock映射。 此功能允许即时创建快照,即,我们可以创建一个空的vDiskBlock映射条目,并使其指向所有未填充项(R4)的上一个快照。 同时,它通过延迟填充缺少的条目来进行元数据查找的后续优化(章节3.2.4)。 在新快照上更新vDisk块时,将创建一个新的extent来保存更新的数据。 图2显示了一个示例,其中将vDisk 1创建为快照,并且已经用指向相应extent的正确指针填充了vDiskBlock映射(左部分)。 之后,在更新其vDisk块之一(右侧部分)时,将其更新以指向新的extent。
只要来自一个extent组的数据重定位到另一extent组(例如,以优化访问权限),extentID映射引入的间接级别就可以进行有效的更新,因为它是我们将物理extentGroupID存储在单个位置 extent所在的位置(从而实现R2,R3)。
最终,仅通过查询extentGroupID映射即可执行一组管理操作。 例如,我们可以仅通过访问此映射来检测(并修复)给定程度上GroupID的副本数是否低于某个阈值-逻辑映射将保持不变-从而实现了R1。
总体而言,由此产生的数据结构使我们能够以高效且响应迅速的方式执行章节3.2中描述的各种管理任务。
3.Curator
Curator是集群管理组件,负责在整个集群中管理和分配各种存储管理任务,包括连续一致性检查,故障恢复,数据迁移,空间回收等。在这一节,我们描述Curator的体系结构(章节3.1),它执行的任务(章节3.2)以及执行这些任务的策略(章节3.3)。 最后,我们通过一组经验结果(章节3.4)展示其价值,并分享从构建Curator(章节3.5)中获得的经验和教训。
3.1 Curator 结构
Curator的设计受到以下考虑因素的影响。 首先,它应随存储系统所服务的存储量进行扩展,并应对节点资源中的异构性。 其次,Curator应提供一个灵活且可扩展的框架,以支持广泛的后台维护任务。 第三,Curator的机制不应干涉底层存储结构的操作或使其更复杂。 基于这些考虑,我们设计了一个具有以下关键组件和(或)概念的系统:
-
分布式元数据:元数据(即上一节中讨论的地图)以分布式环状方式存储,基于经过大量修改的Apache Cassandra [22],经过增强后可以为复制密钥的更新提供强大的一致性。 让元数据分布式存储是因为我们不希望元数据操作成为系统的瓶颈。 Paxos [23]用于强制执行严格的一致性以保证正确性。
-
分布式MapReduce执行框架:使用主/从体系结构,Curator在群集中的每个节点上作为后台进程运行。 主机是使用Paxos选举产生的,并负责任务和工作的委派。 Curator提供了一个MapReduce样式的基础结构[10]来执行元数据扫描,而主Curator进程管理MapReduce操作的执行。 这确保了Curator可以随群集存储量进行扩展,适应集群节点之间资源可用性的变化以及对元数据表执行有效的扫描/连接。
尽管我们的框架类似于一些数据并行引擎(例如Hadoop、Spark),但编写我们自己的框架而不是重新设计已有的框架有两个原因:1)效率,这些开源的大数据引擎没有对单个节点或小集群的高效工作进行很好的优化,而这对我们来说是必须的,2)它们对分布式存储系统(如HDFS)的需求,我们不希望在集群存储系统中具有递归依赖性。
3.2 Curator 管理任务
在本节中,我们描述了Curator组件如何协同工作以执行四个主要类别的任务。 附录A中的表2总结了每个任务涉及的类别、任务和元数据映射。
3.2.1 恢复任务
磁盘故障/移除(DF,Disk Failure/Removal)和容错(FT,Fault Tolerance):如果磁盘或节点发生故障,或者用户希望移除/替换硬盘,Curator将收到通知并启动元数据扫描。这样一次扫描可以查找到在【故障/已删除/已替换节点/磁盘】上具有副本的所有数据块组,并通知底层存储系统修复这些没有充分备份的extent组,以满足备份要求。存储系统将此作为由高优先级事件触发的关键任务来处理,该事件的目的是减少集群中部分数据备份不足的时间。注意,这些任务只需要访问extentGroupID映射,就能将元数据分解为单独的逻辑映射和物理映射。
3.2.2 数据迁移任务
层(T):此任务将冷数据从较高的存储层移动到较低的层,例如从SSD到HDD,或从HDD到公共云。 Curator仅涉及向下迁移,而不涉及向上迁移(即它不将数据从HDD迁移到SSD或从公共云迁移到HDD)。 另一方面,向上迁移是由DSF在重复访问热数据时完成的。 总之,Curator和DSF的行动旨在将最常用的数据仅保留在最快的存储层中,以减少总体用户的访问延迟。
此任务的成本很高,因为它涉及真实的数据移动,而不仅仅是元数据修改。 Curator在元数据扫描期间计算数据的“常用度”,并通知DSF对最不常用的数据进行物理迁移。 数据的“常用度”是基于LRU(最近最少使用)进行计算的, 通过在扫描期间检索到的修改时间(mtime)和访问时间(atime)来识别不常用的数据。 mtime(写)和atime(读)都存储在不同的元数据映射中。 前者位于extentGroupID映射中,而后者位于称为extentGroupIDAccess映射的特殊映射中。 后一个访问映射专门用于支持非关键时间数据的最终一致性(与extentGroupID映射的严格一致性要求相反),从而提高了访问性能。 由于存储在单独的映射中,extentGroup的mtime和atime可能位于不同的节点中,因此,可能需要在不同节点之间通信以组合这两个属性。
为了计算数据的“常用度”,一个MapReduce作业将被触发以扫描上述元数据映射。 映射任务将extentGroupID作为键,将mtime(或atime)作为值。 reduce任务根据extentGroupID键执行类似join的reduce。 reduce任务为不同的extentGroup生成(egid, mtime,atime)元组,并对这些元组进行排序以找到冷extent Groups。 最后,将最不常用的的extent groups发送到DSF,以进行实际的数据迁移。
磁盘平衡(DB,Disk Balancing):磁盘平衡是一项任务,用于将同一存储层中的数据从高使用率的磁盘移动到低使用率的磁盘。 目的是使每个磁盘的使用率在同一层(例如集群的SSD层)内尽可能接近该层的平均使用量。 此任务不仅减少了存储层的不平衡,而且还降低了节点/磁盘故障时的复制成本。 为了最大程度地减少不必要的磁盘平衡操作,即使某个磁盘的使用率很高,但如果平均使用率较低,Curator也不执行平衡。 此外,如果像分层一样执行平衡,则仅尝试移动冷数据。 MapReduce扫描可识别不平衡的源磁盘和目标磁盘以及冷数据,并通知存储结构执行extent组的实际迁移。
3.2.3 空间回收任务
垃圾回收(GC,Garbage Collection):存储系统中有许多垃圾源,例如,删除extent但extent group仍具有多个活动extent且无法删除时,在extent group上预先分配了的较大磁盘空间但没有用完所有的分配额度且数据不再变化时,当extent Groups的压缩因子发生变化等情况下。GC通过回收垃圾并减少碎片来增加可用空间。 有以下三种方式来执行此任务:
- 迁移extent:将活动的extent迁移到新的extent组,删除旧的extent组,然后回收旧的extent组的垃圾。 这个操作成本很高,因为它涉及数据读取和写入。 因此,Curator针对每个extent组执行成本收益分析,并仅选择收益(扩展组中的死区数量)大于成本(被迁移的有效extent的空间总和)的extent组进行迁移。
- 打包extent:尝试在一个extent组中打包尽可能多的活动extent。
- 截断extent组:通过截断extent组来回收空间,即减小其空间大小。
数据删除(DR,Data Removal):2.2节中引入的数据结构更新的方式是不能有悬空的指针,即不能有vDisk指向不存在的extent,或一个extent指向一个不存在的extent组。 但是可能存在无法访问的数据,例如任何虚拟磁盘都未引用的extent或任何extent都未引用的extent组。 这些可能是由于vDisk /快照删除操作的副作用或DSF操作失败导致的。
在DSF中,首先创建extent组,然后创建extent,最后创建vDisk。 对于删除,该过程是反向的:首先删除未使用的虚拟磁盘,然后删除extent,最后删除未引用的extent组。 DR任务分阶段执行此删除过程(可能在连续扫描过程中),并启用系统中未使用的回收空间。
3.2.4 数据转换任务
压缩(C,Compression)和纠删码(EC,Erasure Coding):如果extent组的当前压缩类型与所需的压缩类型不同,或者extent组足够的不常用。则Curator扫描元数据表并标记extent组作为压缩/编码的候选
一旦Curator识别了要进行压缩/编码的extent组(或者是extent),它就会向DSF发送请求,DSF通过迁移extent来执行实际的转换。 该请求的主要输入参数是要压缩(或迁移)的extent集,以及将这些extent数据块迁移到其中的extentGroupID。 如果未指定后者,则创建一个新的extent组。 该API允许我们将多个源extent组的extent打包到单个extent组中。 同样,Curator可以选择一个现有的extent组并将更多extent打包到其中,而不是总是创建一个新的extent组来打包这些extent。 目标extent组也通过MapReduce的扫描和排序来进行标识。
重复数据删除(DD,Deduplication):重复数据删除是一种稍有不同的数据转换,因为它涉及访问其他元数据映射。 在扫描期间,Curator根据指纹检测出重复的数据,并通知DSF执行重复数据删除。
快照树深度缩减(STR,Snapshot Tree Reduction):如2.2节中所述,存储系统支持快照,快照是数据的不变轻量级副本(类似于sim-link),因此可以生成虚拟磁盘的即时副本。 每次系统拍摄快照时,都会将一个新节点添加到称为快照树的树中,并继承虚拟磁盘元数据。 快照树可能会变得很深。 为了能够从树中读取叶节点,系统需要遍历一系列vDiskBlock映射条目。 树的深度越大,读取操作的效率越低。
为了解决这个问题,STR任务通过将vDiskBlock映射元数据从父节点复制到子节点来“剪切”快照树。 有两种形式,即部分STR和完全STR,它们的使用取决于我们是否仅需要某些祖先(部分或全部)的vDisk元数据。 执行复制后,子虚拟磁盘将具有直接读取所需的所有信息,即无需访问祖先的元数据,因此减少了读取延迟。
3.3 策略
3.2节中描述的任务是大致基于如下所述的四种不同策略执行的:
- 事件驱动:这些任务由事件触发。 例如,每当磁盘/节点发生故障时,无论执行什么操作都将执行恢复任务。 这些是至关重要的,更高优先级的任务。
- 基于阈值:这些任务是基于固定阈值违例而动态执行的。 例如,当层使用情况为“高”时,或者磁盘使用情况太而不平衡时等等。我们在下面提供了两个示例。
为了有资格执行Tiering任务,我们要向下迁移的数据的所在的存储层的使用率应超过某个阈值f,而目标层使用率应不超过阈值d,即它应具有足够的空间存储要移动的数据。 此外,将要降低多少使用率的百分比我们用阈值h表示。
关于DB,为了考虑平衡,层的平均使用率应超过阈值m,磁盘使用率差异(spread)应大于阈值s。 磁盘使用率差异是层中使用率最高的磁盘与使用率最低的磁盘之间的差。
-
定期部分扫描:接下来,我们考虑既不是事件驱动也不是阈值驱动的任务,而是仅访问元数据映射的子集。 这些任务每h1小时执行一次,并根据它们扫描的元数据表进行分组。
-
定期完整扫描:每隔h2小时执行一次完整扫描,作为完整扫描的一部分。 我们称此策略为完整策略,因为它将扫描Cassandra中的所有三个元数据表,vDiskBlock,extentID和extentGroupID映射。 由于部分扫描仅适用于元数据图的子集,因此它可以比完整扫描更频繁地运行,即h1 < h2。 通常,扫描的成本是很高的,因此,在运行扫描时,Curator会尝试识别尽可能多的异步任务,并让它们随时间流向DSF。 换句话说,Curator结合了必须完成的不同任务的处理,以减少扫描的开销。
3.4 评估
在本节中,我们将从多个指标评估Curator的有效性。 我们的报告基于三种不同的设置:
a)客户的集群,其中Curator始终处于打开状态;
b)公司内部的生产集群,Curator也处于打开状态;
c)内部的本地集群,在其中我们分别启用和禁用Curator进行受控的实验。
3.4.1 客户和企业集群
我们利用来自许多集群的真实历史数据来评估Curator的能力。 特别是,我们在两个半月的时间内收集了50个集群的数据,以展示Curator对集群整体弹性和数据迁移任务的贡献。 我们还从十个公司的内部集群中收集了三天的数据。 这些集群在压力和工作负载方面非常不同,因为它们被不同的团队用来测试不同功能
恢复:图3显示了平均未复制数据的累积分布函数(CDF)占客户集群中总体存储容量的百分比(对数规模)。 我们观察到大约60%的集群不存在数据备份不足的问题,95%的集群最多具有约为0.1%的备份不足的数据。
为了进一步确认,我们从图3中报告的那些存在数据备份不足的集群中(剩余的40%)获取了可用性案例。 我们仅考虑那些在发生数据备份不足事件后的集群的案例两周内的情况(用公制时间戳表示),并在这些集群中查找意外的停机时间。 在这些集群中,我们没有发现任何计划外的停机时间,这表明Curator确保在检测到备份不足事件后立即进行数据复制,从而避免造成可用性损失。
分层:图4显示了客户的集群中SSD和HDD使用率的CDF。 我们观察到40%的集群的SSD利用率最多为70-75%。 在其余60%的集群中,大部分集群的SSD使用率在75%左右,这表明分层的任务已经被执行; 为了SSD可以接受新写入的或向上迁移来的热数据,数据被会向下迁移。 剩下10%的SSD的使用率略高,这意味着尽管正在执行Tiering任务,它也不能完全应付这样繁重的工作负载。 我们还注意到,HDD的利用率通常较低,其中80%的集群的HDD利用率低于50%。
垃圾收集:图3还显示了在企业集群中,垃圾占总存储容量(按对数比例)的百分比的CDF。 我们观察到90%的集群的垃圾少于2%,这证明垃圾收集任务非常有效。
磁盘平衡:图5验证了在企业集群中的磁盘平衡。 我们用磁盘最大使用率与平均使用率的比值进行绘图。 我们观察到在这个情形下60%的SSD和80%的HDD的最大磁盘使用率几乎与平均值相同。
3.4.2 内部集群
对于“ Curator-less”系统,我们有兴趣评估Curator产生的成本及其所带来的收益,即我们想比较启用Curator时和禁用Curator时的集群行为。 鉴于我们无法在客户的部署中切换Curator状态(开/关),因此在本节中,我们将在内部的测试集群中进行切换。 我们在附录B的表3中总结了我们的发现。
配置:我们在实验中使用包含4个节点的集群。 该集群具有4个SSD和8个HDD,SSD的总大小为1.85 TB,HDD的总大小为13.80 TB,总体CPU时钟频率为115.2 GHz,总内存为511.6 GiB。
工作量:我们使用灵活的 I / O 测试器(fio)来生成用于测试两种设置的相同的工作负载。 当切换Curator状态时,我们会将集群镜像恢复到初始状态。
我们模拟了三个在线事务处理(OLTP,online transaction processing)的工作负载,分别为小型,中型和大型,它们作为一次运行的一部分依次执行。 这些工作负载中的每一个都经过三个阶段,即预填充,执行和销毁。 在预填充阶段,他们将创建自己的用户虚拟机(UVM)及其关联的虚拟磁盘。 在预填充阶段完成之后,它们继续执行,在此执行实际的工作负载操作(读写分别或同时进行)。 执行阶段完成之后,销毁阶段开始,销毁UVM和关联的虚拟磁盘,即可以回收虚拟磁盘的空间。 附录C更详细地描述了工作负载。
优点:就优点而言,我们考虑到了延迟和存储的使用率,这主要是有关分层和数据删除任务的优点。
图6显示了Curator开启和关闭时随时间变化的SSD和HDD使用情况。 我们观察到Curator关闭时SSD和HDD的使用遵循非递减模式。 当SSD充满(在125分钟)时,所有数据开始直接存入HDD中。 相反,当Curator打开时,我们会看到分层的效果,当超过默认使用阈值(3.3节)时,不常用的数据被移动至HDD。 尽管分层任务及时的启动了,但数据接收率很高以至于分层任务无法完全执行完毕,因此我们可以观察到SSD的使用率最高达到了90%,但最后降低到了70%。
图6还说明了Curator中垃圾收集和数据删除任务的好处。在运行结束时,如果Curator是禁用的,我们观察到96%的SSD和23%的HDD使用率(5 TB),而启用Curator时,我们看到的是76%的SSD和6%的HDD使用率( 2.27 TB)。 在整个运行过程中,平均存储使用量在Curator打开时为2TB,在关闭时为3TB。这些差异主要归因于数据删除任务(3.2.3节)。 如上所述,销毁UVM和其关联的vDisk的每个工作负载的销毁阶段,数据删除任务启动以节省大量存储空间。
关于延迟,当Curator打开时,延迟平均为12毫秒,关闭时,延迟平均为62毫秒。 这些数值是在执行工作负载时测量得到的。 如果禁用Curator,随着时间的增长,延迟会逐渐增加。 我们推测这是因为SSD已经满了,所以新来的数据直接写入HDD,因此在读写操作时会产生较高的延迟。
开销:我们考虑CPU和内存使用情况以及执行的I / O操作数目作为开销。
在Curator打开时,执行的IOPS数量更高,因为许多任务都需要在真实的硬盘上读取和写入数据。 尽管如此,启用了Curator的平均IOPS与禁用Curator时的IOPS处于相同的范围(1400对比于1150)。
我们还注意到当Curator打开时,CPU使用率会略高。 这是由于Curator内部的mapReduce基础结构导致的。 尽管mappers主要扫描元数据(主要是I / O密集型),但reducer需要大量逻辑操作来处理扫描的信息(主要是CPU密集型)。 即使启用Curator时的平均CPU使用率较高(18%,关闭Curator时为14%),但该值仍在可接受的范围内,并且随着时间的推移显示出某种稳定的模式。 关于内存使用情况,我们看不到两个版本之间的差异,如表3所示。
3.5 经验教训
在本节中,我们重点介绍了从构建Curator中获得的一些关键经验。 首先,我们有一个后台处理框架,以Curator的形式简化了向文件系统中添加新功能的过程。 每当要添加新功能时,我们都会系统地确定如何将该功能分解为前景组件(将作为DSF的一部分运行)和背景组件(将作为Curator的一部分运行)。 这样可以轻松集成新功能,并避免前台工作变得复杂。 例如,我们的前台操作不会对元数据执行事务更新,而是依靠Curator来回滚不完整的操作,作为其连续的后台一致性检查的一部分。
其次,我们拥有后台MapReduce进程来进行后期处理/惰性存储优化,使我们能够为用户I / O提供更少的延迟。 在处理I / O请求时,DSF不必做出全局最优的决定将数据放置在何处,以及要对该数据应用哪些转换(压缩,重复数据删除等),而是基于最小的本地上下文进行决策,这样使我们能够更快地为用户I / O服务。在稍后,后台的Curator将重新检查这些决策,并为数据放置和转换做出全局最佳选择。
第三,几乎所有的Curator任务都需要使用MapReduce结构(映射和归约操作,map & reduce)来表示。 对于大多数任务来说这很简单,这使我们能够构建更多高级功能。 但是,MapReduce在其他一些方面比较麻烦,因为它要求我们在映射阶段扫描整个元数据表。 在执行实际分析之前,我们再次利用我们的架构首先筛选出要分析的元数据映射。 此过滤器步骤成为另一个映射操作,可以灵活地添加到MapReduce管道的开头。 回顾过去,考虑到我们选择的元数据存储库(即分布式键值存储),我们认为MapReduce是正确的选择,因为它提供了一种简单有效的方法来处理我们的元数据,我们可以利用多个节点的计算能力,并确保初始map操作是在节点本地元数据上执行的,而通信仅发生在传递给reduce步骤的元数据的较小子集上。
在分布式键值存储方面,尽管从处理元数据的角度来看需要更多的工作,但它为我们提供了一种从小型集群(例如三个节点)扩展到较大集群(数百个节点)的方法。 如果我们决定将数据保留在单个节点中,那么像DB这样的SQL-Lite就足以完成我们在MapReduce框架中正在进行的大多数处理。 虽然许多其他商业存储产品也这样做了,但是我们观察到两个主要问题:1)元数据的专用节点会导致单点故障; 2)这些节点需要进行纵向扩容–由于这些存储节点的物理大小随逻辑实体数量的增加而增加,将需要在内存/ CPU方面对其进行替换或升级。
最后,我们注意到尽管群集中的节点通常是具有同质性的,但是不同的集群设置里了不同的资源量,所以在集群之间存在相当大的异质性。 集群的工作负载模式也是不同的,有些是运行服务器的工作负载,有些是用于虚拟桌面基础设施(VDI),而有些是用于大数据应用程序。 此外,有些集群不同时间(每周或每天)的负载变化很明显,而在其他集群中则没有。 鉴于这种异质性,我们的启发式方法在许多集群中并不是最优的。这促使我们使用基于机器学习的方法来优化任务调度,我们将在下面讨论。
4 机器学习驱动策略
到目前为止,我们已经描述了分布式存储结构的概况,并进一步研究了Curator的设计与实现、任务和执行策略等。在本节中,我们提出基于机器学习的建模策略,以改进3.3节中引入的基于阈值的策略。 请注意,此处介绍的技术尚未部署。
在4.1节中,我们激发了对机器学习驱动策略的需求。 我们在4.2.1节中提供了用于建模的通用强化学习框架的背景信息,并在4.2.2节中更详细地描述了Q-learning。 最后,我们在4.3节中展示了关于Tiering(我们的主要用例)的一些实验的结果。
4.1 动力
我们观察到整个集群部署中工作负载的异构性很大。 鉴于工作负载具有这些独特的特征,我们注意到3.3节中引入的基于阈值的执行策略并非对每个集群都是最优的,随着时间的流逝,某些集群在不同时间会有不同的工作负载,所以对于单个集群也不一定是最优的。 因此,为了有效地执行Curator管理任务,需要构建一个可在运行时根据具体情况进行调整的更智能的策略。
提高性能的传统方法是在集群部署开始时使用分析来优化某些参数。 然而,仅通过配置文件无法适应集群在其整个生命周期内承受的各种负载(以及不断变化的工作负载)。因此我们需要经常运行分析器来优化参数,但这样无法记录集群的运行模式。 因此,我们提出使用基于机器学习的解决方案,该解决方案利用统计模型的潜力来检测当前的模式并预测未来的行为。
4.2 背景
我们将这个问题包含在4.2.1节中解释的抽象灵活的强化学习框架中。我们还使用了4.2.2节中描述的无模型的流行Q-learning算法。
4.2.1 强化学习(RL)
强化学习是指学习主体为了达到目标而与环境进行交互的问题。这样的Agent必须能够在某种程度上感知情景的状态,并且能够实施改变情景状态的行为。Agent将根据不同的行为获得奖励或惩罚,并从中确定接下来该做什么。RL是学习一个将情景映射到行动的策略P,其目的是使奖励的信号最大化。这个Agent没有被告知要采取哪些行动,而是必须通过尝试来发现哪些行为能产生最大的收益。
更正式地说,Agent以一系列离散的时间步长与环境交互(t = 0,1,2,3 …)。 在每个时间t,Agent会感知环境状态$s_t$($s_t\in S$,其中S是所有可能状态的集合),并选择一个动作$a_t$($a_t \in A(s_t)$,其中$A(s_t)$是所有动作的集合)。 Agent收到奖励$r_{t+1} \in R$,并让自己处于新状态$s_{t+1}$。
Agent的目标是使获得的收益最大化。 如果在时间t之后收到的奖励序列是$r_{t+1},r_{t+2},r_{t+3},…$那么该学习的目的是使收益$G_t$最大化。 $G_t$的计算方法如下: $G_t = r_{t+1}+\gamma r_{t+2} + \gamma ^2 r_{t_3} + … = \sum_{k=0}^\infty \tag{1}$
其中γ(0≤γ≤1)被称为折扣系数。如果γ=0,则会让Agent只考虑眼前的收益,当γ→1的时候,Agent将计算长期的收益。
考虑到我们没有期望的行为示例(即训练数据),但我们可以给行为示例指定一个优秀的度量标准(即奖励),RL就可以拟合我们的问题。
4.2.2 Q-Learning
Q-Learning是一种强化学习算法,属于时间差异(TD,temporal difference)方法类别[40,41],Agent在特定状态$s_t$尝试动作$a_t$,它根据即时奖励$r_{t +1}$及其对状态$s_{t+1}$的估计值来评估其效果。 通过反复尝试所有状态下的所有操作,它会知道那个操作是最好的。即它会学习由长期折现收益判断的最优策略p*。
这种无模型算法的优点之一是它有无需基于环境的模型即可学习的能力,而这正是基于模型的方法所没有的。 另外,与基于模型的方法相比,无模型方法通常在状态空间较大时(符合我们这种情况)效果很好,而基于模型的方法则在状态空间容易管理时效果更好。
Q-Learning使用函数Q来接受状态$s_t$和动作$a_t$,并输出一个对应的值,该值是在状态$s_{t}$处进行动作$a_{t}$的期望值(折现收益)的估计,然后遵循最优策略p*。它最简单的形式,一步式Q-learning,如下所示:
$Q(s_t,a_t)=Q(s_t,a_t)+\alpha [r_{t+1}+\gamma max_aQ(S_{t+1},a)-Q(s_t,a_t)] \tag{2}$
其中α(0≤α≤1)是学习率,确定新信息在多大程度上覆盖旧信息。
尽管学习到的Q函数可用于确定最佳动作,但该算法并未指定Agent实际应采取的动作[21]。 对于Agent而言,有两件事是有用的,即探索/权衡取舍:
-
开发:通过根据当前的状态$s_t$作出对应的动作$A(s_t)$使$Q(s_t,A(s_t))$最大化。
-
探索:建立一个有更好的估计值得最优化Q函数。 也就是说,它应该选择与当前认为最好的操作不同的操作。
可以使用简单的查询表来实现上述Q函数。 然而,当状态动作空间很大,例如连续空间时,将Q值存储在表中变得很困难。 Q函数需要通过函数逼近器来逼近。
4.3 用例:分层
在介绍了强化学习和Q-learning之后,我们在本节中建议使用后一种算法来决定何时触发分层任务。 尽管我们最初的在分层问题的工作是决定在固态硬盘中保留多少数据,但我们的方法可以概括为之前介绍的每一个基于阈值的任务。
4.3.1 状态-行为-奖励
为了应用Q-learning,我们需要定义状态S的集合,可能的动作A集合以及奖励r。
我们用在时间t时的元组$(cpu,mem,ssd,iops,riops,wiops)_t$ 来定义状态s。其中cpu代表cpu使用率(范围0~100),mem代表内存使用率(范围0~100),ssd代表固态硬盘使用率(范围0~100),iops代表每秒所有的IO操作(范围:全体实数),riops和wiops分别代表读和写的IO操作总数。都是在时间t时的数据,所有的状态$s_t \in S$。
我们还为每个状态定义了两个可能的操作,即是否运行分层任务。 从数学上讲,动作集A由A ={0,1}$\forall s_t \in S$表示,其中0对应于不运行分层任务,1对应于运行任务。
最后,我们使用延迟作为奖励。由于较高的奖励会更好,但我们更喜欢较低的延迟,所以我们实际使用的是负延迟,即在时间t时的奖励r由$r_t = -lat_t$给出,其中$lat_t \in R$是在时间t时的延迟(以毫秒为单位)。
4.3.2 函数逼近器
假设我们有一个连续的状态空间S,如4.3.1节所定义的,因此我们不能使用Q-learning的表格实现,而是使用函数逼近器。我们从它应用的广泛性中受益。
在此之前已经研究了很多的逼近器,如深度神经网络、决策树、线性函数、基于核的方法等。在此我们选择线性逼近。这一决定背后的原因有两个:a)我们没有足够多的历史数据来训练更先进的模型(例如神经网络),b)我们观察到它在实践中运作良好。
4.3.3 数据集
强化学习的一个关键方面与Agent的部署方式有关。 如果在部署之前有足够的时间让Agent学习(例如使用带有离线历史数据的批处理学习),则它可能更快地开始做出正确的选择,即遵循的策略可能更接近于最佳策略。 而如果在没有任何先验知识的情况下部署Agent,即在部署时从头开始学习,那它可能永远无法学习到最佳策略。
我们也面临着这一挑战,因为问题可能在真实的集群中从头进行在线学习而产生;可能需要很长的时间充分探索状态空间。为了克服这一局限性,我们从3.4.1节中提到的50个客户集群的子集中收集的数据构建了一个数据集。我们还使用40个集群,从中我们有细粒度数据还表示状态、行为和奖励。数据包含32K个转换,采样于基于阈值的策略(次优)。每个集群中使用的默认阈值都与3.3节中描述的相同。即使是使用次优的策略来“引导”模型,也有助于其更快的达到良好的状态,这也是离线强化学习的常用做法。
遵循机器学习的一般步骤,我们将数据集分为训练集(80%)和测试集(20%),并在训练集中进行3折交叉验证以进行超参数调整。 我们通过去均值和按比例缩放到单位方差来标准化特征。 我们使用随机梯度下降算法(SGD),结合平方损失训练了两个线性模型,每个模型都对应一个动作。
4.3.4 评估
在本节中,我们将评估Q-learning模型,并将其与基本的策略进行比较(即基于阈值的策略)。我们使用与3.4.2节中相同的内部集群设置来运行试验。
我们使用4.3.3节中描述的数据集部署经过“预训练”的Agent。 部署后,Agent将继续与环境互动,探索/利用状态空间。 我们使用流行的$\epsilon-$贪婪策略,即代理人以概率$\epsilon$选择一个随机动作,Agent以概率$1- \epsilon$选择贪婪行为(它当前认为是最佳的动作)。 我们在所有实验中都使用$\epsilon=0.2$。 为了更好的支持初期阶段的开发,并随着时间的推移进行更好的优化,$\epsilon$可能会随着时间的推移而变化。我们将其留给未来的工作。 此外,我们设置γ = 0.9。
表1列出了Q-learning模型在附录D中所述的五种不同负载下工作的结果。我们基于工作负载的执行阶段(即在完成预填充阶段之后以及实际执行读写操作执行完成后)来计算结果。 附录E中包含了更多结果。因为当前的实验是在较短的时间范围内(几小时)完成的,所以在更长的运行任务中我们期望看到更好的结果。我们观察到,在所有情况下我们的Q-learning解决方案降低了平均延迟,从从oltp可变工作负载中的2%降低到oltp-skewed的工作负载中的20%,并提高了SSD的读取的总字节数。我们相信,通过在状态中添加更多的特征可以实现进一步的改进(例如传统的时间特征、机械硬盘使用情况等)。我们还注意到Q-learning会导致更多的IOPS。这种情况是因为Q-learning解决方案通常比基本的解决方案触发更多的任务,从而执行更多的I/O操作。总的来说,我们看到Q-learning方法可以在IOPS的数量方面以可接受的增长换取固态硬盘命中率的显著提高,在我们的大多数实验设置中,这进一步转化为显著的延迟减少。
5 相关工作
我们借鉴了先前在集群存储和分布式系统方面的工作,然后以新的方式组合它们,以解决我们在集群设置中的独特特性。请注意,我们的设置对应于节点是异构的、未修改(遗留)客户端应用程序打包为vm的集群,并且集群节点可以配备快速存储技术(SSDs、NVMe等)。给定此设置,我们设计了一个系统,其中客户端应用程序运行在与存储结构相同的节点上,元数据分布在整个系统中,并且有效地使用了集群节点上更快的存储。鉴于执行设置和设计概念上的这些差异,我们现在将我们的工作与其他相关工作进行对比。
像GFS和HDFS这样的系统被设计成更具可伸缩性,它们被定制为与经过修改的应用程序一起工作,并利用了它们的一些特点(例如大文件支持,仅追加文件等)。此外,它们不分发元数据,因为考虑到使用大型文件和不频繁的元数据交互,单个节点可以充当目录服务器。这些系统没有利用快速存储的优势—所有文件操作都涉及网络访问,服务器端快速存储的增量收益很小。
集群存储系统(如SAN和NAS)也不会将应用程序进程/VM与服务器一起定位。它们采用了一个分解的计算模型,其中应用程序运行在客户机器上,而数据都来自专用的集群。这些系统提供了可伸缩性优势和广泛的功能(例如快照功能),在我们的系统中也借鉴了这些功能。但区别在于,我们的系统通过分层、数据迁移和磁盘平衡有效地使用高速本地存储。此外,我们相信我们的系统是第一个运行连续一致性检查的系统,它可以显著减少停机时间。
我们使用分布系统中的许多概念和解决方案:MapReduce对元数据执行集群范围的计算,Cassandra将分布式元数据存储为键值存储,Paxos对协调任务执行leader选举。有趣的是,MapReduce不是运行在存储系统之上的应用程序,而是存储系统框架本身的一部分。
近年来,在制造业、传感器系统、多核数据结构、自主计算、操作系统、计算机体系结构等领域,应用机器学习技术改进调度决策的文献越来越多。在Paragon中,作者提出一个基于协同过滤的贪婪模式的应用模型,这种方式以最大程度减少干扰,并在具有异构硬件的集群上最大化服务器利用率。他们的工作更多的集中在终端用户工作的在线调度上,而我们的工作则集中在集群维护任务的后台调度上,以提高集群的整体性能。
Wrangler提出了一个基于支持向量机的模型来构建一个能够选择性地延迟某些任务执行的调度程序。与我们的工作类似,他们以离线方式训练基于CPU、磁盘、内存以及其他系统级功能的线性模型,然后将其部署以做出更好的调度决策中。相比之下,我们的离线(有监督的)训练模型仅“引导”强化学习模型,它在运行时(即以在线方式)不断进行调整和学习。Smart Locks 是一种自调整的自旋锁机制,它使用RL优化不同线程在争夺锁时获得锁的顺序和相对频率。他们使用一种有点类似的方法,尽管他们将调度决策的目标放在了更低的层次上。
也许最相似的工作是来自乐观控制[24,2,3,4]。 Prashanth等人的论文。 [2,3]提出使用RL调整交通信号灯控制系统上的固定阈值。 他们提出了一种Q学习模型,该模型可以适应不同的交通状况,以切换交通信号灯。 我们使用类似的方法,但是在不同的环境中,我们学会了在多层存储系统中更好地调度数据迁移。
也许最相似的工作来自最优控制(Prashanth等人的论文)。提出使用强化学习来调整交通信号灯控制系统上的固定阈值。他们提出了一个Q-learning模型以适应不同的交通状况,以切换交通灯信号。我们使用了类似的方法但设置不同,我们学习如何更好地在多层存储系统中进行数据迁移调度。
6 结论
如今,集群存储系统内置了多种功能,可以维持/改善存储系统的运行状况和性能。 在这项工作中,我们介绍了Curator,这是在企业集群中使用的分布式存储结构的背景下,用于存储系统的后台自我管理层。 我们描述了Curator的设计和实现、它的管理任务、我们如何选择在集群中的多个节点之间分布元数据、如何使Curator的MapReduce基础架构变得必要和高效。 我们以许多相关指标评估了该系统,并报告了在时长为5年的建设期间以及在现场进行的数千次部署中收集的经验。 考虑到集群中存在的异构性,我们将注意力集中在构建更智能的任务执行策略上。 我们提出了一个初始模型,该模型使用强化学习来解决何时应该执行Curator的管理任务的问题, 我们对其模拟了工作负载并进行了评估,结果是非常积极的:延迟降低了20%。