于2012年3月份开始接触OpenStack项目,刚开始之处主要是与同事合作共同部署公司内部的云平台,使得公司内部服务器能更好的得到资源利用。在部署的过程中遇到各种从未遇到过的问题,即使是按照官方文档一步一步的操作,由于某些硬件的不同,也会产生一些莫名其妙的问题,不是数据库因为配置不妥导致无法连接,就是swift的认证无法通过,再者就是上传虚拟机镜像时各种的不可用,申请虚拟机经常无法自动分配外部访问IP,导致无法连接等等。解决这些问题真是让我们费力不少劲儿,那时候基本上都是天天加班,看程序运行的日志,各种网上搜索,再加上那时OpenStack在国内并没有太多的研究者(或许有很多我不知道),没有太多的中文资料,很多错误和问题只能到国外的网站上去搜索,去问。大多数时候我们直接就把错误的日志截取一些重要的内容放到Google上搜。那时天天加班倒也不觉得辛苦,不觉得累,每每问题得到解决,大家都十分的高兴,这也是进步最快的一段时间,自己得到了很大的提升。我们几个人从刚接触OpenStack,到成功搭建好公司内部的第一个可广泛使用的云平台大概花了一个半月的时间,看到其他同事用着我们部署的云平台虚拟机,心里还是蛮爽的。(哈哈哈)


    到了2012年四月末,这段时间里我们项目组基本上每天是大小会议不断,为什么呢?因为公司把做分布式文件系统的任务分到我们部门了(这个时候还不能叫部门,确切的说应该是小组。因为在五月底我们才独立出来,组建独立的部门,专门负责为公司研发大数据存储引擎),于是各种前期预研,知识分享,方案讨论,设计评审,任务分工的会议就冒了出来。由于公司先前已开发出了一套数据存储的文件系统(公司先前一直靠它来做产品挣钱),但不是分布式的,不能适应即将到来的大数据。我们新部门的任务就是在公司现有理论基础以及技术积淀下设计一新的分布式文件系统,以便现有项目能迅速的迁移过来。经过一段时间的研究(其实,我们的架构师在2012年年初就已经开始研究并着手设计了)最终的数据存储这一块我们选用了OpenStack中的Swift。因为Swift本身就是一个很好的分布式对象存储系统(“站在巨人的肩膀上,让我们省了很多事情,加速了我们项目原型诞生”),其无单点
故障,多副本,高性能的小文件存储,跨平台的RESTful对外接口设计,易于水平扩容等特点非常适合我们。在其基础上做二次开发,对于我们来说再好不过了。(现在回想,当时的选择也并非完全正确,因为Swift采用HTTP协议进行数据传输,上层把数据传输到proxy服务——Swift分布式核心服务,在由proxy服务通过计算出数据真正的所在节点位置,然后再通过HTTP协议将数据传输到指定的节点上,在这个过程中很耗时,同时也很耗CPU,所以对于大量数据的容灾备份,其并不是一个很好的选择——个人愚见。还有就是其若一致性的分布式存储系统,由于前期的预研做得不够充分,我们的系统设计在这一点上吃了大亏,为了使系统没有单点的问题,并且在前期版本中尽量不适用数据库(因为那时还没有设计数据库中间件模块),刚开始我们将元数据设计存储到了Swift中。在Demo实现完成之后,在大压力下,上层常常出现不一致的问题,这对上层的APP来说是不能容忍的,后来我将Swift代码的读取副本数改为最少要返回两个,并且再请最新的数据予以返回,这样一致性提高了,但是性能又太挫了,满足不了项目的需求。经过一段时间的折腾,Swift最好只用于存储静态数据,经常变更或者一致性要求比较高的数据应该使用强一致的系统——Swift适合存储静态数据,这一点在官方文档中有叙述,当时没好好领悟,害大家多花了两个月的时间才改过来,把元数据上提,提前使用分布式数据库。这是多么痛的领悟啊!!!!!)。


    有了之前的OpenStack云平台部署的经验,Swift的研究并设计对外的C++数据访问,数据传输以及Swift管理接口的任务就由我来做了。在管理方面,我们设计了Thrift接口,为集群管理中心提供统一的Thrift服务化管理入口。这些接口主要包括对Swift Ring文件的管理(对Swift存储节点的增删,对磁盘的增删等)。关于数据接口的设计这里多提一点:我们在OpenStack中使用的Swift是带有认证的,为什么要用认证呢?因为Swift的设计是可以对外访问的,使用认证是为了安全考虑。而在我们的项目里,Swift只做存储,其对终端用户是透明不可知的,安全性由集群的其他模块保证。在剔除认证模块之后,对外访问接口的设计就简单得多了(现在想起来到是蛮简单的,无外乎就是Account,Container,Object的创建,对Metadata的修改,删除,列取对应的数据,下载Object的内容等等)。可在那时就没那么简单了,官方提供了Java,Python,C#的客户端,就是没有C++的,况且官方所提供的也不太适合我们项目的需求,最后只能根据实际情况自己设计并实现一套。经过不断的读官方的API文档,不断的实现验证,一个星期左右第一个版本的API被设计出来了,于是召集大家一起讨论,一起Review。由于大家都刚刚接触Swift,所以对第一个版本的接口也没什么太大的意见,只要符合接口设计规范就可以了,同时大家也一直认为先开发出来,把整个项目的Demo做先做出来是头等任务,在开发过程中遇到问题再提新需求。从后来的开发过程来看,当时的决定是正确的,虽然,我们后来有些接口一直没有使用,同时还添加了一两个接口以用于提高性能,但是我们并没有花太多的时间在接口的设计和讨论上,没有过度的设计,把大多数时间用于开发和模块整合上了,这样做加速了整个项目的Demo开发。


    虽然Swift的对外API很快完成了设计,但是在开发过程中我却遇到了一些困难,同时也走了不少的弯路。刚开始我们选用了boost的asio作为写HTTP客户端的库,在开发过程中并没有发现不妥之处,等开发完成,测试人员对整个项目的数据模块进行压力测试时才发现,潜在很多的问题。性能有点挫先不说了,就是常报读到文件尾,断开的管道问题就让我头痛了好一段时间。关于为什么会出现这两个问题:主要是因为客户端采用了HTTP长连接,并且使用了连接池,话说这些对提高性能是非常有用的,可是也正是因为这些技术的采用在服务端突然停止的时候问题就暴露了,这时客户端及容易拿到一个已经断开的连接来进行数据传输,还有就是数据传输时,连接已断开,客户端并没有发现,还在继续读取数据。后来在连接池上加入了对服务器端的心跳,问题得到了缓解,但是并没有根本上的解决。后来仔细想想,如果肯花时间去完善客户端的HTTP处理的代码,问题是可以解决的,可是那样不太值得。首先时间上也不允许,出现新的问题也不太好解决。于是,毅然将asio抛弃了,采用了稳定且使用广泛的libcurl,将数据传输的逻辑交个libcurl来处理,把更多的时间用于对返回数据的处理上。经过一段时间的折腾,也经过了一年多的使用和测试,到现在Swift客户端已不再更改了,而且也能稳定的运行。


    接触Swift马上快有两年的时间了,可以说已基本上掌握了其的工作原理,遇到问题也能快速定位,期间也给公司的同事做过Swift培训。回想起来,这两年里在Swift上遇到很多问题,也解决了很多问题。刚开始没有人指导,也没有很好的资料(除了源码),遇到问题只能把代码和错误日志拿来分析,看得多了,遇到得多了,很多问题也能够快速解决了。随着很多大公司和大牛们的加入,OpenStack项目也已经越来越好,越来越稳定,同时Swift也得到了很快的发展,记得刚开始接触的时候是1.4.8版,到现在都1.12.0版了。功能也新增了很多,但是其核心架构,核心思想并没有改变,代码量也并没有很大的增加,不过随着几个版本的发布,其代码结构是越来越清晰,让人读起来很舒服了。


    虽然说Swift有很多优点,但是个人认为其还是有一些缺点的:在用了一年多之后,我们发现随着文件数量的不断增多,其副本修复进程非常的占用CPU,而且是长时间的占用,这样就导致了整个服务器的压力很大,Linux中top load 持续保持在20%以上,对于其他程序来说就变得响应迟钝了。与此同时,replicator进程对于已经被从Ring文件中删除的磁盘,只要磁盘还被挂载在/srv/node/下还会去扫描,这样就导致改磁盘一直会有文件被打开,无法将其从系统中umount掉。Swift的副本修复功能强烈依赖于xinetd服务中的rsync,这样在xinetd服务被关闭之后数据就无法同步了。Swift也没有对所使用的磁盘进行空间监控,由于XFS文件系统会在磁盘被写满的情况下,导致写数据的进程会被其的崩溃而附带杀死(对于这个问题,我们已经无解了,只能期待Linux内核能解决),所以在磁盘写满的情况下Object Server和xinetd服务很容易被杀死。Object Server死了,对应用层直接返回503,这样很难辨别到底是什么原因导致的。同时,xinetd服务崩了,如果副本出错就很难得到及时的修复。以上问题一时半会儿我们是无法解决的,在集群中我们只能通过某些办法避免它们的发生。比如说xinetd和Object Server的崩溃,通过实时监控服务来确保,如果发现服务死了就重新启动。XFS磁盘满了就对外发出告警,让管理人员及时知晓等等。


 。。。未完,待续 。。。(后面将会有Swift的配置,memcached,leveldb,本地缓存设计,mongodb等)




Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐