构建一个企业级分布式存储系统对于任何一个团队来讲,都是一件极具挑战性的工做。不只须要大量的理论基础,还须要有良好的工程能力。SmartX 在分布式存储领域已经投入 5 年的时间,积累了不少宝贵的实践经验。咱们将经过一系列文章,向你们详细介绍 SmartX 如何构建分布式块存储产品。本文是第一部分,整理自 SmartX 联合创始人兼 CTO 张凯在 QCon 2018 大会上的演讲,着重介绍相关背景和元数据服务。node
你们下午好,我今天跟你们分享的题目是『ZBS:SmartX 自研分布式块存储系统』。SmartX 是咱们公司的名字,我本人是这个公司的联合创始人兼 CTO。ZBS 是 SmartX 研发的分布式块存储产品的名字。数据库
我毕业于清华计算机系,毕业之后加入百度基础架构部工做了两年,主要从事分布式系统和大数据相关的工做。我自己也是开源社区的代码贡献者,参与的项目包括 Sheepdog 和 InfluxDB。其中 Sheepdog 是一个开源的分布式块存储项目,InfluxDB 是一个时序数据库(Time Series Database,TSDB)项目。2013 年我从百度离职,和清华的两个师兄一块儿创办了 SmartX 公司。缓存
SmartX 于 2013 年成立,是一个以技术为主导的公司,目前主要专一于分布式存储及虚拟化这个两个领域。咱们的产品均为本身研发,目前已经运行在数千台物理服务器上,存储了数十 PB 的数据。SmartX 跟国内主流的硬件服务商、云服务商都有合做,咱们的产品已经服务了包括公有云、私有云以及金融业、制造业等核心领域的关键业务,其中也包括核心应用和核心数据库等应用场景。今天我将主要围绕分布式块存储进行介绍。安全
通常来讲,咱们根据存储的访问接口以及应用场景,把分布式存储分为三种类型,包括分布式块存储,分布式文件存储,和分布式对象存储。服务器
其中,分布式块存储的主要应用场景包括:架构
虚拟化:好比像 KVM,VMware,XenServer 等 Hypervisor,以及像 Openstack,AWS 等云平台。块存储在其中的角色是支撑虚拟机中的虚拟盘的存储。并发
数据库:好比 MySQL,Oracle 等。不少 DBA 都将数据库的数据盘运行在一个共享的块存储服务上,例如分布式块存储。此外也有不少客户直接把数据库运行在虚拟机中。负载均衡
容器:容器最近几年在企业中使用愈来愈普遍。通常来讲,容器中运行的应用都是无状态的,但在不少应用场景下,应用也会有数据持久化的需求。应用能够选择将数据持久化到数据库中,也能够选择将数据持久化到一个共享虚拟磁盘上。这个需求对应到 Kubernetes 中,就是 Persistent Volume 这个功能。框架
今天我将主要围绕 SmartX 如何打造分布式块存储进行介绍。SmartX 从 2013 年成立开始,到目前已经积累了 5 年左右的分布式块存储的研发经验,因此今天咱们除了分享 SmartX 如何实现咱们本身研发的分布式块存储 ZBS 之外,还会详细介绍咱们在分布式块存储的研发过程当中的一些思考和选择。此外也将介绍一下咱们产品将来的规划。运维
从普遍意义上讲,分布式存储中一般须要解决三个问题,分别是元数据服务,数据存储引擎,以及一致性协议。
其中,元数据服务提供的功能通常包括:集群成员管理,数据寻址,副本分配,负载均衡,心跳,垃圾回收等等。数据存储引擎负责解决数据在单机上存储,以及本地磁盘的管理,磁盘故障处理等等。每个数据存储引擎之间是隔离的,在这些隔离的存储引擎之间,须要运行一个一致性协议,来保证对于数据的访问能够知足咱们指望的一致性状态,例如强一致,弱一致,顺序一致,线性一致等等。咱们根据不一样的应用场景,选择一个适合的一致性协议,这个协议将负责数据在不一样的节点之间的同步工做。
有了这三部分,咱们基本上就掌握了一个分布式存储的核心。不一样的分布式存储系统之间的区别,基本也都来自于这三个方面的选择不一样。
接下来我会分别从这三个方面介绍一下咱们在作 ZBS 系统设计的时候是怎样思考的,以及最终决定采用哪一种类型的技术和实现方法。
首先咱们来介绍一下元数据服务。咱们先来谈谈咱们对元数据服务的需求。
所谓元数据就是『数据的数据』,好比说数据放在什么位置,集群中有哪些服务器,等等。若是元数据丢失了,或者元数据服务没法正常工做,那么整个集群的数据都没法被访问了。
因为元数据的重要性,因此对元数据的第一个需求就是可靠性。元数据必须是保存多份的,同时元数据服务还须要提供 Failover 的能力。
第二个需求就是高性能。尽管咱们能够对 IO 路径进行优化,使得大部分 IO 请求都不须要访问元数据服务,但永远都有一些 IO 请求仍是须要修改元数据,好比数据分配等等。为避免元数据操做成为系统性能的瓶颈,元数据操做的响应时间必须足够短。同时因为分布式系统的集群规模在不断的扩大,对于元数据服务的并发能力也有必定的要求。
最后一个需求是轻量级。因为咱们产品大部分使用场景是私有部署,也就是咱们的产品是部署在客户的数据中心的,且由客户本身运维,而非咱们的运维人员运维。这个场景和不少互联网公司本身来运维本身的产品是彻底不一样的场景。因此对于 ZBS 来讲,咱们更强调整个系统,尤为是元数据服务的轻量级,以及易运维的能力。咱们指望元数据服务能够轻量级到能够把元数据服务和数据服务混合部署在一块儿。同时咱们但愿大部分的运维操做均可以由程序自动完成,或用户只须要在界面上进行简单的操做就能够完成。若是你们了解 HDFS 的话,HDFS 中的元数据服务的模块叫作 Namenode,这是一个很是重量级的模块。Namenode 须要被独立部署在一台物理服务器上,且对硬件的要求很是高,且很是不易于运维,不管是升级仍是主备切换,都是很是重的操做,很是容易因操做问题而引起故障。
以上就是咱们对元数据服务的需求。接下来咱们来看一下具体有哪些方法能够构造一个元数据服务。
谈到存储数据,尤为是存储结构化的数据,咱们第一个想到的就是关系型数据库,例如 MySQL,以及一些成熟的 KV 存储引擎,例如 LevelDB,RocksDB 等。但这种类型的存储最大的问题就是没法提供可靠的数据保护和 Failover 能力。LevelDB 和 RocksDB 虽然很是轻量级,但都只能把数据保存在单机上。而尽管 MySQL 也提供一些主备方案,但咱们认为 MySQL 的主备方案是一个太过笨重的方案,且缺少简易的自动化运维方案,因此并非一个十分好的选择。
其次,咱们来看一下一些分布式数据库,例如 MongoDB 和 Cassandra。这两种分布式数据库均可以解决数据保护和提供 Failover 机制。可是他们都不提供 ACID 机制,因此在上层实现时会比较麻烦,须要额外的工做量。其次就是这些分布式数据库在运维上也相对复杂,不是很易于自动化运维。
也有一种选择是基于 Paxos 或者 Raft 协议本身实现一个框架。但这样实现的代价很是大,对于一个创业公司不是一个很划算的选择。而且咱们创业的时间是 2013 年,当时 Raft 也只是刚刚提出。
第四种是选择 Zookeeper。Zookeeper 基于 ZAB 协议,能够提供一个稳定可靠地分布式存储服务。但 Zookeeper 的最大的问题是可以存储的数据容量很是有限。为了提升访问速度,Zookeeper 把存储的全部数据都缓存在内存中,因此这种方案致使元数据服务所能支撑的数据规模严重受限于服务器的内存容量,使得元数据服务没法作到轻量级,也没法和数据服务混合部署在一块儿。
最后还有一种方式是基于 Distributed Hash Table(DHT)的方法。这种方法的好处元数据中不须要保存数据副本的位置,而是根据一致性哈希的方式计算出来,这样就极大地下降了元数据服务的存储压力和访问压力。但使用 DHT 存在的问题,就丧失了对数据副本位置的控制权,在实际生产环境中,很是容易形成集群中的产生数据不均衡的现象。同时在运维过程当中,若是遇到须要添加节点,移除节点,添加磁盘,移除磁盘的状况,因为哈希环会发生变化,一部分数据须要从新分布,会在集群中产生没必要要的数据迁移,并且数据量每每很是大。而这种于运维操做在一个比较大规模的环境中几乎天天都会发生。大规模的数据迁移很容易影响到线上的业务的性能,因此 DHT 使得运维操做变得很是麻烦。
以上介绍的方法都存在各类各样的问题,并不能直接使用。最终 ZBS 选择了使用 LevelDB(也能够替换成 RocksDB) 和 Zookeeper 结合的方式,解决元数据服务的问题。首先,这两个服务相对来讲都很是轻量级;其次 LevelDB 和 Zookeeper 使用在生产中也很是稳定。
咱们采用了一种叫作 Log Replication 的机制,能够同时发挥 LevelDB 和 Zookeeper 的优势,同时避开他们自身的问题。
这里咱们简单的介绍一下 Log Replication。简单来讲,咱们能够把数据或者状态看做是一组对数据操做的历史集合,而每个操做均可以经过被序列化成 Log 记录下来。若是咱们能够拿到全部 的 Log,并按照 Log 里面记录的操做重复一遍,那么咱们就能够完整的恢复数据的状态。任何一个拥有 Log 的程序均可以经过重放 Log 的方式恢复数据。若是咱们对 Log 进行复制,实际上也就至关于对数据进行了复制。这就是 Log Replication 最基本的想法。
咱们具体来看一下 ZBS 是如何利用 Zookeeper + LevelDB 完成 Log Replication 操做的。首先,集群中有不少个 Meta Server,每一个 Server 本地运行了一个 LevelDB 数据库。Meta Server 经过 Zookeeper 进行选主,选出一个 Leader 节点对外响应元数据请求,其余的 Meta Server 则进入Standby 状态。
当 Leader 节点接收到元数据的更新操做后,会将这个操做序列化成一组操做日志,并将这组日志写入 Zookeeper。因为 Zookeeper 是多副本的,因此一旦 Log 数据写入 Zookeeper,也就意味着 Log 数据是安全的了。同时这个过程也完成了对 Log 的复制。
当日志提交成功后,Meta Server 就能够将对元数据的修改同时提交到本地的 LevelDB 中。这里 LevelDB 中存储的是一份全量的数据,而不须要以 Log 的形式存储。
对于非 Leader 的 Meta Server 节点,会异步的从 Zookeeper 中拉取 Log,并将经过反序列化,将 Log 转换成对元数据的操做,再将这些修改操做提交到本地的 LevelDB 中。这样就能保证每个 Meta Server 均可以保存一个完整的元数据。
前面提到,因为 Zookeeper 存储数据的容量受限于内存容量。为了不 Zookeeper 消耗过多内存,咱们对 Zookeeper 中的 Log 按期执行清理。只要 Log 已经被全部的 Meta Server 同步完, Zookeeper 中保存的 Log 就能够被删除了,以节省空间。一般咱们在 Zookeeper 上只保存 1GB 的 Log,已经足够支撑元数据服务。
Failover 的逻辑也很是简单。若是 Leader 节点发生故障,其余还存活的的 Meta Server 经过 Zookeeper 再从新进行一次选主,选出一个新的 Meta Leader。这个新的 Leader 将首先从 Zookeeper 上同步全部还未消耗的日志,并在提交到本地的 LevelDB 中,而后就能够对外提供元数据服务了。
如今咱们总结一下 ZBS 中元数据服务实现的特色。
首先,这个原理很是容易理解,并且实现起来很是简单。由 Zookeeper 负责选主和 Log Replication,由 LevelDB 负责本地元数据的存储。背后的逻辑就是尽量的将逻辑进行拆分,并尽量的复用已有项目的实现。
其次,速度足够快。Zookeeper 和 LevelDB 自己的性能都不错,并且在生产中,咱们将 Zookeeper 和 LevelDB 运行在 SSD 上。在实际测试中,对于单次元数据的修改都是在毫秒级完成。在并发的场景下,咱们能够对元数据修改的日志作 Batch,以提升并发能力。
此外,这种方式支持 Failover,并且 Failover 的速度也很是快。Failover 的时间就是选主再加上 Log 同步的时间,能够作到秒级恢复元数据服务。
最后说一下部署。在线上部署的时候,咱们一般部署 3 个或 5 个 Zookeeper 服务的实例以及至少 3 个 Meta Server 服务的实例,以知足元数据可靠性的要求。元数据服务对资源消耗都很是小,能够作到和其余服务混合部署。
以上是一些基本的原理,咱们再来看一下 ZBS 内部的对于元数据服务的具体实现。
咱们将上述的 Log Replication 逻辑封装在了一个 Log Replication Engine 中,其中包含了选主、向 Zookeeper 提交 Log、向 LevelDB 同步数据等操做,进一步简化开发复杂度。
在 Log Replication Engine 的基础之上,咱们实现了整个 Meta Sever 的逻辑,其中包含了 Chunk Manager,NFS Manger,iSCSI Manager,Extent Manager 等等不少管理模块,他们均可以经过 Log Replication Engine,管理特定部分的元数据。RPC 模块是 Meta Server 对外暴露的接口,负责接收外部的命令,并转发给对应的 Manager。例如建立/删除文件,建立/删除虚拟卷等等。此外,Meta Server 中还包含了一个很是复杂的调度器模块,里面包含了各类复杂的分配策略,恢复策略,负载均衡策略,以及心跳,垃圾回收等功能。
以上就是关于 SmartX 超融合系统中 SMTX 分布式块存储元数据服务部分的介绍。
了解更多信息可访问 SmartX 官网:https://www.smartx.com