开始时,Picnik使用了一个开源项目,Mogilefs,用于文件存储。我们的大部分服务器都有几个空闲的驱动器插槽,我们在这些插槽上接入大容量的SATA驱动器,用于 Mogilefs文件存储。大部分后台服务都是CPU密集型的,所以与这些I/O密集型的存储配合得相当好。这个策略工作得很好,但存储需求超过CPU需求之后,就不行了,这时候,Amazon的S3服务看起来好像是我们扩展存储的最容易也最便宜的方法。
在测试S3之前,我们实际上并没有对费用用做多少评估,一方面是当时并没有太多的云计算可供选择,另外就是一些令人尊敬的工程师也极力推荐S3,最后,我们从来就没指望会大量使用。
因为开发者的机器没有使用Mogile,所以已经有一个框架用于接口不同的文件存储系统,这样一来,增加S3的支持就相对容易些。事实上,仅用了大约一天就实现了S3支持,又测试了一两天,然后就将其打包到我们的每周例行发布中了。这种实现的简易性也是我们选择S3的另一个关键因素。
最初,我们计划只将最早的文件迁移到S3,这些文件是从2007年12月份开始的,因为这些文件访问频率比较低,不用怎么担心性能和可用性方面的问题。这个模式非常棒,S3看起来性能也很好。
唯一的不足,我们从Mogilefs中迁移文件的速度不够快,没有跟上文件增长的速度。而且 Mogilefs也开始出现一些性能问题了。我们的解决方案跟其他几家大型网站一样:将文件直接存储到S3。开始时,只将一小部分新文件直接存储到S3,然后逐渐增加,一直到绝大部分新文件都流向Amazon。这样,事情又搞定了,我们就转去解决其他问题了。
虽然S3已经相当可靠了,仍然出现了一些值得注意的问题。我们遇到的第一个问题是最终一致性(eventual consistency)问题,基本上,这意味着不能保证立即读取刚写入的文件,在写入到西雅图的S3集群后,再试图从EC2中读,这个问问题会更严重。通过将所有的文件访问都从我们在西雅图的数据中心代理而使这个问题有所缓和,但这样一来,我们的带宽就增加了。
我们遇到的第二个问题是Amazon?返回HTTP500错误,我们的代码能够对不成功的请求进行重试,这在大多数情况下工作良好。每一两周,我们都会遇到错误突然爆发,以至于重试逻辑都不起作用了。这种爆发会持续一小时左右。一天,我正在查看发生错误的关键字(keys),注意到这些关键字都有相同的前缀!结果证明,S3是基于关键字的范围对数据进行分区的,这意味着维护(如增减某个分区容量)会导致某个范围内的关键字大量出错。Amazon这样做是为了保持S3的高性能。对我们而言,这种突发性错误更大程度上是种烦恼,因为我们还有Mogilefs在起作用,如果写到S3失败,将其写到Mogile:就是了。随着增长率趋于稳定,这个问题现在已经很少出现了,但Mogile仍然在发挥作用。
其实我们遇到的这些问题是构建大规模系统必然会发生的,所以 Amazon也用不着掩饰什么。人们很容易忘记,这其实是一个有着很多用户的规模巨大的分布式系统。
随着流量的增长,我们越来越依赖于S3。要是S3宕掉了,一天的大部分时间里,我们的Mogl都无法处理庞大的请求。幸运的是,S3大部分问题都不是发生在我我们网站的高峰时间,所以 Mogilev还能够应付。我也应该提到的是,Mogile在两种情况下会宕机几个小时,种情况是修改 MYSQL的表结构,还有就是调试Mogilev的Perl代码。这种时候,1009%的流量都会压到S3上,而我们的用户则从来不知道发生了什么。
“无限”存储的一个危险是很容易造成浪费。对我们来说,我并没怎么注意删除无用文件的后台作业业,对于创建的文件,最终会删除掉近75%,而无用文件增长起来是很快的。
即使我们曾经注意到了这个问题,我们事实上还是决定忽略它。Picnik的每一个人都很忙,而且这看起来也不是什么大不了的问题,再说了,还有更棒的新功能或其他的伸缩性问题需要我们去关注。有趣的是,S3让我们选择或者雇用和训练更多的人,或者更简单,写张支票就行了。在我们的信用卡月度额度快用光的时候,一切都变了。
经过几个月的调整、分析和重写代码,我们最后拿出了一份清理无用文件的可伸缩方案。首先是数据库对无用文件记录进行清理,然后在数据库的文件记录和S3上的关键字列表之间做一个大型的连接操作(a large merge-join),以执行实际的删除。
在实现更好的清理系统的过程中间,我们开始意识到,S3对我们的工作负荷(workload)来说,实际上是非常昂贵的。先前的成本分析完全没有考虑PUT操作的成本。很多S3的负荷中,存储成本占了大头,因为文件上载以后,在随后的一个很长时段内,只是偶尔访问下。正如前面所提到的,我们的负荷是创建大量文件,然后在随后的几天里就删掉了这意味着PUT操作的成本上升了。
意识到这点以后,我们开始努力优化Mogilefsl的性能,并且研究高性能的NAS产品。最后,我们实现了一个基于Linux的NFS概念系统作为前端存储,这意味着只需要在S3上存储超过1周的大约25%的文件,这些留下来的文件也有了一个对S3来说更加友好的存取模式。
有很长一段时间,我们都不清楚S3是不是仍然合适。尽管更为传统的NAS硬件看起来贵了点,但如果你对长期存储需求有信心的话,可以在一年或两年内分期付款。而另一方面,许多创业公司的CFO(包括我们自己的)都会告诉你,为了保持灵活性和一定程度的自由,多花点儿钱也值得一一这种灵活与自由就是S3提供的。当然,这种灵活性比将此花费算做运维费用还是资本费用更为重要。至于我们所关心的,就只是运维费用了,因为这直接与流量和功能有关。
混合计算
Picnik主要的服务端组件之一是我们的渲染场(render farm)。用户在Picnik上保存图片时,经常需要在服务端重建这个图片。这时,客户端会向服务器发送一大段XML文本描述用户的编辑操作。Web服务器收到后,会将所需要的图片连同XML文本一起打包,并将其加入到渲染作业队列中。渲染服务器获取该作业,重建图片,然后将结果图片返回给Web服务器。此时,客户端处于阻塞状态,等待服务器的响应。大多数时间,客户只需要等待几秒钟。
虽然这是可伸缩系统的典型架构,我们在设计时仍然考虑到了未来对云计算的需求。这时的渲染服务器不需要访问任何内部服务,如数据库或存储服务器。简言之,它们非常适合于运行在EC2上,另外,我们已有了一个自己开发的配置管理和代码部署系统,称为Server manager。
像S3一样,实际实现起来既简单又快速。内部的渲染场已经考虑到了运行在Xen之上的WM了,所以我需要做的就是一些简单修改,使渲染服务器的VM映像适合于EC2的Xen找,然后将其打包为AMI。在映像启启动时,首先连接Server manager?获取需要安装和运行的组件列表,其中之一是Renderserver,Renderserver用于连接渲染队列以获取渲染作业。我要做的第一件事就是激活两个实例运行一下看看怎么样一一棒极了!
第二阶段就是去实现云操作的终极目标(loly Grail)了:自动伸缩(auto-scaling)。我们的自动伸缩实现起来还是很容易的,因为所有处理都是通过队列实现的。由于用户在等待渲染结果,所以自动伸缩代码的目标是维护一个空队列5。每分钟都会唤醒Server manager的一个线程,轮询队列的统计信息(上一分钟已做过平衡),然后进行计算,看为了维持闲者和忙者的比例需要做些什么。当然,由于流量和网络延迟会有小幅波动,为避免不必要的振荡而对闲忙比例的修正会出现迟滞现象,如EC2实例有的时候需要几分钟才能启动,我们的代码也考虑到了这些问题。所有这些经验性的调整经历了一两周的时间,系统运行起来以后,就非常简单啦。
自动伸缩并不仅仅是典型的容量需求问题,我们也遇到了诸如到EC2的网络延迟加大了,或发布了一个代码修正而使得渲染速度变慢了。遇到这些情况时,我们先停止自动伸缩,直到找出背后真正的原因并加以改正为止。我们还修正了一个错误,这个错误使一小部分用户的保存操作失败,这一修正使渲染负载増加了20%正好在圣诞节之前。
这种设置也很适合批处理作业。一段时间以前,我们要重建一批缩略图,我就写了一些代码,将作业提交给渲染队列,然后用新的缩略图文件更新数据库记录。我不需要做任何特别的事情来分配空间或将作业设置在晚上负载较轻的时候处理,Server manager只是增加新的实例以适应新的负载需求。
从财务方面来说,使用EC2比使用S3更清楚。我们试图扩建内部渲染以满足平均的容量需求,同时,将做渲染的CPU转换到做Web服务的CPU也容易。这意味着将云作为渲染服务器给Web服务器带来了一些动态特性,这让我们易于适应负载模式的变化,而且,通过逐渐购买硬件的方式,这也让我们更有效地使用现有的硬件。例如,可以在数据中心订购个新的机柜,然后把服务器上架,而不用担心浪费大部分的机柜电力。
一般与EC2有关的问题主要集中在连接性上。虽然互联网作为一个整体是可靠的,但任何两点之间的连接却并非如此。通常,如果问题出现在网络和数据中心之间,只有一小部分用户受影响,但是,假如网络恰好是云计算提供者,则所有用户都会受影响。这种类型的宕机可能非常严重,因为问题可能出在这样的区域,就是不论是你还是云计算提供者都没有为之付费,即双不管的区域。
在发生严重问题时(而且是在繁忙时段),唯一的选择就是甩掉负载。过去,我们只有种办法控制让多少用户进来,现在我们按优先级将用户分类(游客、免费用户、合作伙伴、金牌用户)。可情的是,大多数情况下,你不得不等待宕机恢复。不论哪种情况,我们做的第一件事情就是更新 Twitter信息(fecd),这些消息也显示在我们的It's raining onour Picnik”页面上。我们并不指责任何人一个用户才不关心这些呢。
我们并不像对内部服务器那样监控EC2实例。Nagiosi通过 Servermanager自动获取EC2实例的信息,Nagiost也监控队列深度,因为这是很多问题的预警器(early indicator)。
Caci以图示方式显示运行实例数(通过EC2API)及集群层层面上的性能数据。我们不需要在 Cacti上增加单个实例的信息,因为它并不实际处理集群,何况实例还是动态变化的。
事实上,我们并不关心单个实例的性能,已经知道这些实例比本地机器上的要慢一点。没关系的,因为自动伸缩系统总能在现有资源的条件下找到平衡。
由于实例是从队列中获取作业的,EC2实例稍微慢一点,只是少干点活儿而已,不会躺倒不干。这使我能够集中精力关注高层的性能数据,如一天中使用EC2实例的比例是多少。在一天结束时,只需要针对web服务器做容量规划,从而决定硬件购买决策,而渲染服务器只是从未使用的容量中获益。
要想有效使用网站建设云计算资源,需要对应用架构和配置管理/自动化有一个合理的“增长”态度。我们将渲染服务器设计为可分解的,以及我们手边已经具备配置管理系统等,这些事实使得自动伸缩实现起来既容易又可靠。
本文地址://hailanjianghuncun.com//article/3306.html