相信你们都据说过Amazon的AWS。做为业内最为成熟的云服务提供商,其运行规模,稳定性,安全性都已经通过了市场的考验。时至今日,愈来愈多的应用被部署在了AWS之上。这其中不乏Zynga及Netflix这样著名的服务。html
然而这一切并无停滞不前:AWS根据市场的变化提供了愈来愈多的内建服务,在下降了用户成本的同时更是提升了用户开发的效率。并且随着各个企业对云的兴趣的不断增长,网络上也出现了愈来愈多的有关如何正确高效地使用AWS的讨论。数据库
在这里,本文将会介绍一系列在云上建立服务所经常使用的一些方法及设计思路。固然,它们并不局限于AWS。您一样也能够将这里介绍的各类思路和准则借鉴到您部署在Azure上,阿里云上,或者其它云上的应用中。安全
另外,本文不限于公有云,Openstack等私有云上的应用一样也能够借鉴本文中所提到的思路和部分功能。服务器
认识云所带来的不一样网络
在介绍这些准则和最佳实践以前,咱们首先要坐下来好好想想,咱们部署在云上的应用须要是什么样子的。在云的早期阶段,用户只能从它那里获得一系列虚拟机,以及一些很是有限的附加服务。在那个年代,在云上承载一个服务和经过物理机承载一个服务的确没有什么太大的不一样。时至今日,云已经不限于为用户提供虚拟机这样单一的服务了。各个云所提供的众多附加功能使得用户能够很是灵活地对这些虚拟机进行操做及管理。运维
这致使了最根本的两点不一样:对虚拟机的处理方式以及对自动化功能的支持。tcp
在以往的基于物理机的服务中,每台物理机都是弥足珍贵的。所以服务中的每一个服务实例都须要运维人员的细致照顾。一旦其中一个服务实例出现了问题,那么运维人员就须要经过各类方式尝试对该服务实例进行修复,并将从新恢复到健康状态的服务实例从新添加到整个服务中。ide
基于云的解决方案则不须要这么麻烦。用户彻底能够经过云在几分钟内从新部署一台具备完整功能的虚拟机,所以相应的解决方案也就变成了经过从新建立一台具备完整功能的虚拟机来替换出现问题的虚拟机。函数
一样的,若是咱们要增长服务实例,那么基于物理机的服务经常须要几个月的准备才能完成。而在云上,这个过程经常只须要几分钟。工具
因此在这里,咱们再强调一遍您在为云设计服务时须要紧紧记住的第一条准则,那就是:在云上建立虚拟机很是快速,咱们要尽可能利用这种特性。
如今咱们再来看第二点不一样:对自动化功能的支持。咱们知道,管理一个基于物理机的服务集群经常是一件很是麻烦的事情,尤以一系列常见的运维操做为甚。这些运维操做包括服务实例失效处理,添加/移除服务实例,更改服务实例设置等。反过来,市面上常见的云平台经常容许用户标示在特定事件发生时须要被触发的自动化脚本。若是用户可以经过这些脚原本控制在特定状况下须要执行哪些运维操做,那么对云上应用的管理就会简单得多,并且对各类情况的响应也会快速得多。
好,让咱们来强调第二点:尽可能经过云平台所提供的自动化支持来完成咱们所须要的功能。
固然,这只是开发云上服务的两个最基本的准则。因为它们是如此常见,所以不少云服务提供商都已经将它们以附加功能的形式直接暴露给了用户。例如各公有云中常见的一个附加功能就是Auto Scaling。亚马逊的AWS和微软的Azure都提供了该功能,而私有云解决方案Openstack也有了一个提案。该功能会根据当前Auto Scaling所管理的各虚拟机实例的负载来自动调整其所包含的虚拟机数量,从而保证系统不会产生过载或系统不会有过多的冗余容量:
而这一切对容量的控制都是自动完成的,基本没必要由咱们写任何的监控和控制逻辑。
固然,这种功能还有不少,让咱们在后面的章节中逐渐为您展现。
该好好使用的特点功能
在当前业界内,不管是公有云平台仍是私有云平台,都会提供一系列特点功能。对这些特点功能的使用经常须要咱们在设计及实现服务时采起和以往不一样的视角。所以在在本节中,咱们将会介绍这些常见的特点功能以及有关它们的一系列使用经验。
AMI
第一个要讨论的就是AMI。能够说,这是用户所最为熟知可是也最不容易被使用好的一个功能。AMI的全称是Amazon Machine Image,也便是在AWS上建立新的虚拟机时所须要使用的模板。其不只仅能够包含虚拟机运行时所须要的操做系统,更能够包含一系列已经配置完毕的各个组件。一旦用户经过某个AMI建立了一台虚拟机,那么该虚拟机在建立完毕以后将直接拥有这些AMI上所预先配置好的各个组成。
Openstack的Glance也提供了相似的功能。
那么这些预先配置好的组成都有哪些呢?答案就是,它能够是您正常运行的虚拟机上所拥有的部分甚至全部组成。试想一下,要让一个虚拟机实例可以在集群中正常工做,其经常须要包含操做系统,Servlet Container,服务运行所须要的类库,服务的源码以及服务的配置等众多组成。一个AMI所包含的组成越全面,那么经由它所建立的虚拟机实例所须要配置,安装等工做的工做量就越少:
从上图中能够看到,若是一个AMI中所包含的组成越全面,那么经过它来建立一个虚拟机实例所须要的时间就越短。熟悉高可用性等非功能性需求的读者可能会知道,这就意味着越短的恢复时间以及更高的安全系数。
反过来,一个AMI所包含的组成越全面,那么它的更改就越为频繁。设想一下,若是一个AMI包括了服务运行所须要的源码,那么每次对源码的修改都须要咱们从新制做AMI。这对于一个拥有几十我的甚至上百我的的开发团队来讲就是一个灾难。由于制做AMI也是须要时间和精力的。
那么一个AMI到底应该包含哪些组成呢?相信读者们本身也能估计得出:在服务的开发过程当中,AMI所包含的内容将频繁地发生变化,所以此时AMI所包含的组成应该尽可能的少。而在开发完成以后,AMI的变化将再也不那么频繁了,所以为它建立一个包含较多固定组成的AMI则能够减小整个系统对各类状况的响应时间。
Instance Monitoring & Lifecycle Hooks
在知道了这些AMI应该包含哪些组成以后,接下来咱们要考虑的就是如何使用这些AMI了。在系统发生异常或者须要经过建立新的虚拟机实例来提升系统容量时,咱们首先须要经过AMI来建立一个虚拟机实例,而后再在虚拟机实例建立完毕之后执行必要的安装及配置。仅仅拥有AMI的支持是不够的,咱们还须要可以监控到这些事件并在虚拟机实例的各个生存期事件发生时对其进行处理,不是么?
在以往的服务开发过程当中,监控系统的设计不多被软件开发人员所重视。毕竟这绝大部分是运维人员所须要负责的事情。而在云上的应用中,全部这些事件都须要自动化起来。也就是说,与软件开发人员的距离更近了。
AWS提供的监控系统是CloudWatch(Openstack彷佛也有了一个提案)。其主要的工做原理就是:在AWS上的每种资源都会将其指标定时地保存在相应的Repository中。用户能够经过一系列API来读取这些指标,也能够经过这些API来保存一系列自定义指标。接下来用户就能够经过建立一个Alarm来对这些指标进行侦听。一旦发现这些指标达到了Alarm所标示的条件,那么CloudWatch就会将该Alarm发送到Amazon SNS或Auto Scaling Group之上:
讲到这里估计您已经看出来了,监控是云应用的核心。若是没有将监控系统放在云应用的核心位置来考虑,那么咱们就没有办法遵照本文刚开始时候所提到的“尽可能经过云平台所提供的自动化支持来完成咱们所须要的功能”这样一条准则。而某些AWS的附加功能已经为某些经常使用的组成抽象出了一系列生存期事件。
例如在AWS的Auto Scaling功能中,咱们能够经过其所包含的Lifecycle Hook来指定建立或移除一台虚拟机实例时所须要执行的用户自定义逻辑。让咱们首先考虑一下Auto Scaling是如何与Cloud Watch协做处理以下图所示的负载峰值应对方案的:
对于上图所示的容量变化过程,Auto Scaling与CloudWatch之间的互动将以下所示:
整个运行流程大体以下所示:
并且在其它一些附加功能中,咱们也经常会看到这种对自动化脚本的支持。有些附加功能直接提供了对自动化脚本的支持,如用户能够直接在OpsWorks中标示针对特定事件的执行逻辑。而在另外一些组成中,就好比咱们刚刚提到的Auto Scaling对自动化脚本的支持,则是须要多个组成协同配合来完成的。
若是实在没有找到一个合适的解决您所须要的Hook,那么最终极的办法就是在制做您本身的AMI时在里面放一个Agent,以经过它来执行您的自定义逻辑,不是么?
虚拟机实例管理
如今咱们已经知道如何经过AMI快速地建立一台虚拟机,以及如何经过脚原本协调这些虚拟机之间的协同工做。可是这里还有一个问题,那就是,难道须要咱们手动地一台台部署虚拟机,并逐个配置它们么?
其实并没必要要。针对这个需求,AWS为咱们提供了三种不一样的工具:CloudFormation,Beanstalk以及OpsWorks。
先来看看最为简单可是灵活度也最高的CloudFormation。简单地说,软件开发人员只须要经过一个JSON格式的模板来描述所须要的全部种类的AWS资源,并将其推送到CloudFormation上便可。在接收到该模板以后,CloudFormation就会根据其所包含的内容来分配并配置资源。例以下面就是一段CloudFormation模板(来自于Amazon官方文档):
1 { 2 "Resources": { 3 "Ec2Instance": { 4 "Type": "AWS::EC2::Instance", 5 "Properties": { 6 "SecurityGroups": [{ 7 "Ref" : "InstanceSecurityGroup" 8 }], 9 "KeyName": "mykey", 10 "ImageId": "" 11 } 12 }, 13 14 "InstanceSecurityGroup" : { 15 "Type": "AWS::EC2::SecurityGroup", 16 "Properties": { 17 "GroupDescription": "Enable SSH access via port 22", 18 "SecurityGroupIngress": [{ 19 "IpProtocol": "tcp", 20 "FromPort": "22", 21 "ToPort": "22", 22 "CidrIp": "0.0.0.0/0" 23 }] 24 } 25 } 26 } 27 }
略为熟悉AWS的读者可能已经可以读懂上面的代码所描述的资源组合:建立一个名称为“Ec2Instance”的虚拟机实例,以及一个名称为“InstanceSecurityGroup”的Security Group。Ec2Instance实例将被置于InstanceSecurityGroup这个Security Group中。
然而咱们能作的不仅是经过JSON文件来描述一些静态资源,更能够经过Conditions来指定条件,Fn:FindInMap等函数来执行特定逻辑,更能够经过cfn-init等helper script来完成软件安装,虚拟机配置等一系列动做。只不过CloudFormation更多地关注资源管理这一层面,所以用它对大型服务中的实例进行管理则会略显吃力。
另外一个工具,Beanstalk,则最适合于在项目的初期使用。在使用Beanstalk建立服务时,咱们只须要上传该应用的Source Bundle,如WAR包,并提供一系列部署的信息便可。Beanstalk会帮助咱们完成资源分配,服务部署,负载平衡,伸缩性以及服务实例的监控等一系列操做。若是须要对服务进行更新,咱们只须要上传新版本的Source Bundle并指定新的配置便可。在部署完成之后,咱们还能够经过一系列管理工具,如AWS Management Console,对这些应用进行管理。
随着服务的规模逐渐增大,咱们就须要使用更复杂一些的工具了,那就是OpsWorks。在OpsWorks中包含一个被称为Layer的概念。每一个Layer包含一系列用于某一特定用途的EC2实例,如一系列数据库实例。而每一个Layer则依赖于一系列Chef recipe来在特定生存期事件发生时执行相应的逻辑,如安装软件包,执行脚本等(没错,配置管理软件Chef)。这些事件有Setup,Configure,Deploy,Undeploy以及Shutdown等。
在Openstack中,您可能须要考虑Heat。它在Openstack中是负责Cloud Orchestration的。
其它工具
好,剩下的就是一些经常使用且容易用对,或者并不很是经常使用或适用于特定领域的功能了。例如咱们能够经过Route 53所提供的功能实现基于DNS的负载平衡及灾难恢复解决方案,经过CloudFront为咱们的应用添加一个CDN,经过EBS,S3,Glacier等不一样种类的存储记录不一样种类的数据等。
鉴于本文的定位是一篇综述性质的文章,所以咱们就再也不花较大精力对它们进行介绍了。毕竟本文的目标就是让你们意识到云上服务和基于物理机上的服务之间的不一样,并可以根据这些不一样来以正确的方式思考如何搭建一个云上的服务。
在个人计划中,后面还会有几篇和Amazon相关的文章。这些文章抑或是如何以更适合的方式知足服务的非功能性需求,要么就是对Amazon中的一些较为类似的功能的概括总结,所以咱们还有机会谈到它们。
建立云服务的最佳实践
这部分是从我笔记中抽出来的。这些笔记不少都是你们在网络上讨论的总结,并且我也没有在笔记中逐个记明出处是在哪里,所以可能没法按照标准作法给出这些观点的原始出处。
好,那咱们开始。
考虑全部可能的失效
咱们知道,云下面有一层是虚拟化层。因此相较于直接运行于物理机上的服务而言,运行在云上的服务不只仅须要面对物理机的失效,更须要面对虚拟化层的失效,甚至有时云平台上某些功能的失效也可能影响咱们服务的运行。所以就云上的单个虚拟机而言,其发生失效的几率将远大于物理机。所以在云上应用所须要遵照的第一条守则就是:要假设全部的组成都有可能失效。
这些失效可能存在于云上服务的任何地方,甚至咱们都须要考虑云平台的数据中心失效的状况。就以AWS为例,其所提供的最基本的资源就是虚拟机。虚拟机之间彼此相互隔离。所以在一台虚拟机出现了问题的时候,其它虚拟机的运行也不会受到任何影响。而在虚拟机之上则是Availability Zone。Availability Zone在Region以内彼此隔离,所以若是其中的一个Availability Zone出现了问题,那么其它的Availability Zone中的虚拟机仍可以正常工做。而Region则是世界范围内的相互隔离。若是一个Region出现了问题,那么其它Region不会受到任何影响。在一般状况下,整个Region发生宕机的几率其实是微乎其微的。
可是AWS自身在今年内也出现过整个Region失效的状况。若是软件开发人员在实现部署在AWS上的服务时心存侥幸,认为Region宕机的几率很小,那么在相应的Region宕机时,该服务将没法为用户提供服务。有时候,这种服务中断是致命的。
因此在实现一个须要承载于AWS上的服务时,咱们必需要考虑:若是虚拟机出现了问题,咱们的应用应该如何处理;若是Availability Zone出现了问题,咱们又应该如何处理;若是整个Region都不能正常工做,那么咱们又该如何处理?这些问题发生时,咱们应该提供什么样的服务?又须要在多少时间内恢复?
做为一个可选的解决方案,咱们能够将一个服务部署在多个Availability Zone中,并且在不一样的Region中拥有一个拷贝。这样在整个Region失效的状况下,用户仍然能够经过其它Region访问服务,只不过因为用户所访问的是离他较远的Region,所以整个服务的响应速度会显得有些慢。
而在Region中的某个Availability Zone失效的状况下,其它Availability Zone中的拷贝将仍可以提供服务。所以对于用户而言,其基本不会受到很大的影响:
除此以外,有些存储也可能达不到您的要求。例如,若是我没有记错的话,EBS存储的可靠率是99.99%,而S3的可靠率则是11个9。所以一种误用就是用EBS当作持久化存储,那么结果可想而知:数据丢失。
其实不只仅是针对于AWS。在其它云上运行的应用,不管是公有云,私有云,仍是混合云,咱们都须要在实现时就考虑如何避免这些层次上的失效。除非云平台自身已经为某些组成提供了高可用性保证。例如AWS的Route 53就是一个具备高可用性的DNS服务。
除了这些可能的失效,咱们还须要考虑如何处理这些失效。这经常和咱们所提供服务的自身特性有关。若是在某些组成失效的时候,咱们仍须要可以提供服务,那么咱们就须要建立一个具备容错性的系统;若是某些关键组成失效,那么咱们须要多少时间可以恢复到正常服务状态,又可能出现多少数据丢失等,都是由SLA来规定的。咱们要作的,就是根据SLA的要求设计基于云上的具备容错性,高可用性的服务,以及数据的备份及恢复等方案。
关于云上如何设计一个具备容错性的系统,以及如何执行数据的备份和恢复,我都会在其它文章中加以讲解。毕竟不一样的需求会致使不一样的解决方案。这也不是一句两句就能说清楚的。
而咱们只须要记住一点:假设云上服务的全部组成都有可能失效,除非云服务提供商声明了该组成的高可用性。
经过较小的服务换取较高的伸缩性
对于一个在云上运行的服务而言,良好的伸缩性是它可以成功运行的一个基本条件。因为在云上建立一个服务实例经常只须要几分钟的时间,所以其所包含的服务实例个数经常会根据当前负载变化,甚至一天会变化不少次:
市面上常见的云基本上都是根据服务所占用的资源数量来计费的。若是云上的服务被设计为一个不可分割的总体,那么咱们就须要在某部分组成负载太重时对服务进行总体扩容。这使得其它的并不须要扩容的组成也同时进行了扩容,进而增长了对资源的没必要要的占用:
并且若是一个服务包含了太多的组成,那么它的启动时间也会受到必定的影响。反过来,若是云上应用的各个组成彼此相互独立,并可以独立地进行扩展,那么这个问题就将迎刃而解:
除此以外,这些小服务之间的较好的隔离性也会将错误隔离在较小的范围内,进而提升了整个系统的稳定性。
若是您须要更多地了解如何建立一个具备高扩展性的应用,请查看《服务的扩展性》一文。若是您更但愿能了解如何在云上对服务进行切割,并有效地组织这些子服务的开发,请查看文章《Microservice简介》及《Microservice Anti-patterns》。
注意服务的切割粒度和方式
咱们刚刚提到,若是但愿咱们的服务可以在云上具备良好的伸缩性,那么咱们就须要将它划分为较小的子服务。可是这也容易让一些读者走到另外一个极端,那就是子服务的分割粒度过小,或者是在不适合的地方对服务进行了分割。
服务的分割粒度过小经常会致使服务对单一请求的响应速度变慢。这在某些系统中将会变成很是严重的问题。试想一下,若是一个请求须要由多个服务实例处理,那么对该请求进行处理的过程就须要屡次的信息传递:
而若是将两个频繁交互的组成切割到了不一样的子服务中,那么对请求进行响应的过程也经常须要更多的信息传递:
从上面两个示例中能够看到,过细粒度的分割以及不合适的分割都会致使单次消息处理的流程变得更为复杂,也便是消息的处理时间变长。这对于那些对单一请求处理时间较为敏感的服务来讲是很是很差的设计。
而另外一个与之类似的状况就是两个频繁沟通的子服务。若是在通过正常分割以后,两个子服务须要进行频繁地通讯,那么咱们就须要想办法让它们之间可以更高效地通讯,如经过在AWS上购买Dedicated Host实例让两个子服务在同一台物理机上运行。
也就是说,为了能让云上的服务能更为高效地响应用户的请求,咱们同时须要考虑数据流的拓扑结构,并根据该拓扑结构优化云上服务的部署。
固然了,对于一个基于消息的服务,每一个子服务的划分主要是经过考察是否可以增长整个系统的吞吐量来决定的。若是您对基于消息的服务感兴趣,请查看《Enterprise Integration Pattern – 组成简介》一文。
可配置的自动化解决方案
勿需质疑,一个云上的服务基本上都拥有自定义的负载周期。例如一个主要面向国内客户的服务,其负载高峰经常是在白天,而自凌晨以后其负载将一直处于一个较低的水平。并且该服务的负载可能会在某一个时间结点上迅速地增长,所以直到负载到达阈值才开始建立新的虚拟机实例是来不及的:
除此以外,这些服务可能还须要在特定时期内做为某些服务的平台。在活动时间内,其负载将可能较其它时期的负载重得多。
针对第一个问题,咱们须要为这种负载定义一个周期性的伸缩计划。也就是说,对于一个AWS上的具备周期性负载变化的服务,咱们经常须要使用Auto Scaling Group中的Scheduled Scaling计划。
而在面对举办活动这种打破周期性的负载时,咱们就须要一些额外的逻辑以保证咱们的服务拥有宽裕的容量。也就是说,此时咱们的Auto Scaling Group所使用的Scaling Plan处于一个特殊的状态之中。
而这仅仅是处理有规律的负载所须要考虑的一些状况。而对于那些具备非规律负载的服务,对服务容量的管理会变得更为复杂。
全部这一切都有一个前提,那就是咱们须要可以将服务实例的伸缩自动化起来。在AWS中,这并非太大的问题。Auto Scaling已经提供了足够的灵活度,以容许咱们经过Scaling Plan等组成提供自定义的伸缩逻辑。可是对于某些平台,您可能就须要自行实现这些功能了。在此之上,您还须要让它可以经过用户所指定的配置以及负载规律进行伸缩。所以设计一个良好的解决方案并非一个简单的事情。而这也是我在这里提起这些的缘由。
为何咱们要作这些呢?节省资金。其实将服务放到云上的最终极目标无非就是为了节省资金。在云上,咱们能够快速地获取想要数量的服务实例,而不须要预先指定硬件的购置计划;在云上,咱们的容量能够随时根据负载进行伸缩,而不须要购买远远超过平常需求的足以应付负载峰值的服务器。
甚至在有些云平台中,同一种服务实例有多种不一样的购买方式。这些购买方式的价格有时会相差不少。若是咱们可以在自动化伸缩功能的支持下充分利用这些购买方式所提供的优惠,那么咱们的云应用的运营成本将大大下降。
例如在AWS中,咱们有以下种类的虚拟机实例购买方式:
那么对于上面所展现的周期性的负载,咱们就能够经过如下方式的购买组合来下降整个服务的运行成本:
这种购买方法经常能够帮您节省1/3甚至以上的服务运行成本。
除了可以完成服务容量的伸缩,更重要的是,这些自动化功能还能够在其它一系列解决方案中使用,如灾难恢复,服务的升级等。而咱们的目标就是,您的服务基本上再也不须要经过人为干预就能完成自动伸缩,灾难恢复等Day 2 Operation。这能够显著地下降服务的运营成本。
可是这里有一点须要注意,那就是对日志的保护,尤为是发生故障的服务实例中的日志。由于这些日志经常记录了服务产生故障的缘由,所以它们对于服务的开发人员很是有价值。所以在某些云上(我忘记了是哪一个云仍是哪一个解决方案提供商)提供了对云上服务的日志进行分析并将异常日志进行保留的功能。若是您但愿您的服务可以持续地改进,那么对日志的保护必不可少。
总结起来,那就是,让您的服务的平常操做变成一个自动化流程,并基于这些自动化流程搭建您的平常运维解决方案,并经过它们来帮助您节省服务运行的开销。并且在自动化过程当中,咱们要注意日志的保护。
好了,本文要讲的就只有这些了。对于文中所提到的一系列技术,我会在后续的文章中对它们进行较为详细地介绍。您只须要理解咱们为何要这样搭建云服务,为何要遵照这些守则便可。这是后面一系列云服务解决方案文章的理论基础。
转载请注明原文地址并标明转载:http://www.cnblogs.com/loveis715/p/5327584.html
商业转载请事先与我联系:silverfox715@sina.com
公众号必定帮忙别标成原创,由于协调起来太麻烦了。。。