@gaoxiaoyunwei2017
2017-09-29T09:54:42.000000Z
字数 7277
阅读 669
于济萌
作者 | 杨柳
编辑 | 济萌
杨柳
Tabcorp 高级工程师现任Tabcorp(澳大利亚)高级工程师,一线程序员,负责大型项目核心计算系统的开发运维工作。多年敏捷DevOps经验,负责端到端的持续集成,持续部署环境搭建和维护。曾任TW高级咨询师,武汉技术社区发起人,译有《Gradle实战》一书。
传统软件项目交付中,各个角色分工明确,也暴露了很多软件交付中的很多问题。 DevOps的工作方式的恰如其分的解决了其中一些问题,那么如何从传统交付流程迁移到具有伸缩性,灵活性,以及快速响应的持续交付中来,这种新的交付部署方式会给团队带来哪些变革,以及如何在大规模团队中落地。大规模团队将如何标准化的进行持续交付,以及它带来的便利和下一阶段的挑战是什么?
本文的五个部分:
首先,有这样一个案例:客户的IT部门刚刚成立,公司将这个部门交给了一个非常资深的产品经理(他原来是业务经理)。他负责整个IT部门的IT化构建,手下有一批业务人员,同时也是将来会是这批软件的使用者。他在收集一些反馈信息后给出了一些大型的需求列表,然后交给了我们研发团队。我们研发团队和设计师沟通了一下,得到了一个设计版的结果进行了封闭式的开发,大概持续了6个月。产品做出来然后,研发团队将它交给了测试团队,并进行了大量的测试和所有的分支覆盖。最后的结果是上线成功。
上述就是一个传统瀑布式的开发。有很多的公司曾经经历过类似的开发模式。这种传统瀑布式开发有以下的特点:第一,交付周期非常长,从产品的需求和最后的部署大概经历一年的时间;第二,这整个过程中没有任何的交付,交付不多,也就是产品的需求到最后的开发的整个过程中只有衔接部分有一定的交流,并没有太多需求上的修改、调整和反馈。
然而,第二种开发模式是无持续集成,有以下特点:第一,它的代码集成会很困难,因为所有人都在同一个仓库上工作,那么它每天就会有很多的提交和冲突。
第二,测试难,依赖性高。当我们的软件部署到测试环节的时候,测试会将所有的需求进行一一测试,也就是测试会经历漫长的时间和所有的需求进行对比,这对测试的压力和测试难度也是非常高的。
最后,手动发布的使用会造成:当我们产品做好以后发布频率非常低,也就只有一、两次成功发,这个软件就算是完成了交付的一部分,达到了可用的状态。
随着现在敏捷宣言还有持续集成(持续集成是由2014年提出的概念)的提出,我们在开发过程中进行大量的,并且每天都要进行集成,这样我们能减少集成所面临的压力。
上图是Jef所写的关于持续交付的书,它采用方法和理论是以迭代的方式将软件交到用户手上。那么,再加上硬件条件的提升,按持续集成在云计算的发展下将变得触手可及,即:虚拟是非常容易的。这样就有了硬件和软件条件,所以大部分人就开始享用持续集成的交付模式。
持续集成的交付模式的特点:第一,交付周期短。持续集成一般是迭代的方式交付,我们一般两周会有一个版本从端到端的构建、测试到发布。并且,沟通非常频繁,它的构建测试和沟通发布流程中都会有不同的交互,比如说需求和开发人员的沟通,测试和需求人员一起讨论我们的测试文档和测试策略。
第二,测试完备。我们开发过程中会有测试金字塔的概念,它的底部是大量的单元测试,开发会把所有的功能进行单元测试,金字塔的中间层是集成测试,大家从端到端,系统和系统的测试,还有端到系统的测试。手工成本非常高,它的数量也会比较少。
然而,测试的回归,也就是每次构建一个软件时,我们怎样保证原有的功能是正常工作的,那就有大量的回归测试。而在持续集成的环境中,因为有大量的自动化进行覆盖,所以我们回归测试的次数就会大大降低。
最后,自动化程度高。由于我们进行每两周都会有一墙之隔迭代,就会有一个部署,那么这些当中我们哪些东西是要重复做重复做,我们怎样避免这样重复的事情?我们就要自动化这些事情,采用自动化构建、自动化测和自动化部署一套脚本步骤做下来,这样可以避免大量重复的工作。
下来我们来对比一下持续集成和瀑布式这二种开发模式,可以看到持续集成是以4小步快跑的方式来进行迭代式交付,瀑布式是以时间为轴的线性交付。
另外,持续集成和瀑布式相比还有些不同之处。第一,快速发布。持续集成发布更加快速,而且它能够及时得到用户的反馈,也就是我部署以后用户能够提出一些反馈交给业务人员,业务人员跟开发沟通后,进行下一个版本的迭代,调整优先集,调整我们的需求。然后,它的速度更加频繁,也保证软件的质量更高。
第二,因为在整个的测试过程中,包括自动化测试和人工测试。测试介入也是比传统的测试介入提前很久,所以它能保持测试的稳定性。
第三,对于进度把控来讲,持续集成模式,由于我们能够持续地看到我们所产生的产品是什么样的,这样它对项目的管控可能更有直观的感受。
最后,团队协作的提高。因为在持续之前的环境中,不同的角色沟通更加频繁,所以持续集成的团队协作相对于瀑布式的任务交接的方式会有更好地提高。
首先讲一下微服务,微服务为什么诞生?为什么会有这样的架构演进?
微服务是单体架构和面向服务的架构,也就是传统的单体架构。一个服务器接受所有的请求和处理,那么它就会面临一些问题。第一业务复杂的时候代码量变得非常大,我们可能通过一些模块的方式进行划分,但是还是难以具体去分功能的模块。
第二就是如果面临大量的访问请求的话,传统单体架构会面临一些瓶颈,业务会出现卡死。面临这样的问题,我们需要进行延缓加载。这就是微服务的出现,同事也部分解决了这样的问题。
下面我来讲讲微服务架构的一些特点。微服务有5个特点:
1. 领域建模;
2. 去中心化;
3. 服务隔离;
4. 独立部署;
5. 高度可监控。
采用微服务功能时,每一个功能,原来对于单体架构而言,每一个模块可以以划分模块的方式,把用户作为一个模块,订单作为一个模块,支付作为一个模块。而对于微服务我们可以讲一些不同的模块做得更极致,可以将它成为一个单体的运行服务,可以通过协议的方式进行沟通。
这种服务的分割方式可以采用DSL,不同服务的性能要求和不同服务的需求要求,我们会采用不同的领域语言。然后我们这种拆分更加贴近业务原型,比如说支付模块面临支付有漏洞安全性的问题,我们怎样解决,之后回滚,失败时怎样处理,对于这种领域性的问题,会有更加贴切的模型。
再来讲讲去中心化。原来单体架构一个中心,它需要处理所有的请求,每一个服务以线性的方式构建我们的软件。所以每一个服务高度自治,这样的结构使得软件更加健壮,因为一个服务包宕掉不影响其他的工作,这样的服务架构能够代表组织结构。因为目前而言,每5个人可以管理4—5个服务,所以咱们公司一百个服务,那就有十几个Team,每个Team都是扁平的结构,这种团队的架构就跟微服务是一样的。
接下来谈谈服务隔离。因为每一个服务都有自己运行自己的服务,所以服务通过契约,即我们是通过描述每一个结构来进行沟通。另外就开发隔离而言,不同的服务采用不同的编程语言。每一个服务都分离开发,失败也能隔离,当一个服务宕掉其他的能够正常工作。
独立部署就是我们可以想要选择自己需要的版本部署,也可以选择什么时候部署。所以微服务的部署是独立的,可以自主性也可以灵活性地部署,而且不同的部署不会相互干扰。我们在部署一个支付模块时,不用部署从主页获取信息的服务,所以主页部署也是隔离的。
最后说一下高度可监控。高度可监控与原来的监控性能情况不同。
对于微服务来讲,我们要对每一个服务进行性能监控,这样带来的好处是能准确地进行监控服务的运行时间的消耗。
这是我们自己公司的一个页面,这是监控其他微服务的健康状况,来查看不同服务是否能够正常工作。而且你能独立地监控每一个服务的时间点,这时我们就能更加有效地进行扩展,因为我们知道哪些服务是瓶颈,哪些服务的性能CPU瓶颈高,我们就会根据数据进行容量扩深。
接下来我们分享一下澳洲的IT公司,为什么会有这样的标准化,统一化的持续交付模式。当我们谈论标准化时,我们谈论的是怎样将它规模化,所以当我们把服务从10个上升到100个,我们怎样解决这些重复,这里面可能存在的一些问题。
我来介绍一下我们公司的业务场景,可能接下来讲实践时更有一些感受。我们公司是一家澳洲最大的博彩公司,当欧洲和澳洲有这些体育赛事时就会有大量的访问,尤其是比赛前半个小时,一分钟的访问量高达十几万的下注率,性能要求非常高。
我们之前开发了一个软件是单体架构,花了两年的时间,最后投放到市场上,当这种大型比赛出现时,比如世界杯、欧冠,性能瓶颈和服务器响应能力非常慢,不能满足市场需求,导致了其他的竞争对手,本来我们是澳洲最大线下的,现在线上我们并未做到。
因为那个时候我们用了单体架构的软件,并没有有效地占领市场,用户的体验非常差就选择了其他的品牌进行下注。公司痛定思痛,决定花重金重新开发一套全新的IT系统,也就是我要给大家介绍的微服务下持续交付的软件。每当这种大型的体育赛事,它的处理响应能力还有Devops对于监控我们的性能问题,是完全没有任何性能瓶颈,也就是现在是比较成功的案例。
接下来我要给大家讲一下持续交付流水线的构建图。上图中每一个小方块就是一个服务,每个服务都是一个不同的阶段,UAT就是给开发用的集成平台。
然后,还有一个给测试用的集成平台,就是自动化地triger一下,就可以把代码分离部署到下一个环境,这是一个产品的部署。所以这些都是自动化的,每一个服务都是独立部署。
标准化的持续集成的建设包含:
首先讲的是前后端分离架构。因为我们的业务需求是要有网站、PC端、手机端和Pad端;换句话说全放位让大家接触到业务。同时这也比较尴尬,因为在大多数国家是不允许这项业务存在的;但是在澳洲、欧洲博彩是税收的大头。
所以我们要将前后端分离开来。前后的框架使用的语言都不一致,也是分离让合适的人做更合适的事情。前后端通过Server描述自己的沟通,也可以通过Rist Api来沟通。这种分离方式可以降低压力。
我们前端大概是有两个Engs服务器。访问Server前端的页面,也就是每个人只是访问了静态服务器页面。这个页面加载时会发很多的请求来获取数据。
在这个静态服务器被访问之前,我们会有Node-balance来决定哪个服务器Server里的静态页面被访问。在这之前,我们有CDN来给前端页面做缓存。也就是说一个区域的人访问前端页面,是由缓存提供给的。所以,前端服务器的压力会减少,并且大部分都是缓存起来了。
但是,缓存的前端并未减少压力。举个例子,比如说10万人要访问赛事信息,这是非常大的性能要求。10万人同时访问赛事信息和赔率时,10万人是平均数字,到了峰值最高,那么怎样解决呢?下面的小节来揭晓。
对于上述的问题,我们首先采用Docker的部署和开发方式。Docker的好处是水平扩展性好,所有的容器都是可以随机调整个数和数量。
第二,它的服务是隔离的。关于Docker开发,我刚才提到了它和微服务的架构是互相不干扰,服务的健康状况不会影响到其他的服务。在构建服务的时候,我们需要重复构建100多个服务。从0到1需要花很多的时间,从1到100先需要想到怎样构建通用的模板,因为每一个服务我们都要构建。
首先,我们构建Docker Image;然后打包怎样做单元测试;最后上传到Docker HUB中去。这些工具都成一个模板,只要把这些模板应用到Docker,Docker会自动打包上传到Docker商铺里面去。我们采用的一个SaaS的平台工具帮助来做这个事情。
Docker是涉及不同服务之间的通讯。因为某一个服务的变化可能影响到其他的服务,所以你们在交付过程中可能有一些规范不一样,这就需要的数据不一样。我们通过Swagger定义这些规范。
当一个服务的Swagger发生变化,它的信息发生变化,就会通知起来的服务,看哪些服务需要的请求与这个是否一样。如果一致,就通过。如果不一致,就会及时告诉你两个服务之间有变化,并要求你必须解决。
服务之间可以通过Stub进行测试。但是,有这么多的依赖我怎样进行集成测试?这个需要用户的信息。可是这些模块没有信息,就要通过Stub进行交互。
最后是不同的服务之间得通信。其中最重要的一个模块就是下注的模块;就是说怎样把一个订单最后支付然后让它成功地下单。
之前,我们用JS。它是一个单线程的动态语言,性能不是特别好。然后,我们也是参考了Uber,采用大量的微服务架构,并且它有很多不同服务转型的案例。接下来,我们采用了Google出来的一款语言,它有大量的指针概念,你不用创建很多的对象,以内存消耗的方式来提高语言的性能。
所以,我们微服务90%的是用JS,有些消耗更大的采用性能更高的语言。另外,我们还采用Lambda,这不是一种语言是一种架构方式,这都是并列在一起。
当前端有大量的请求访问到服务器端时,由于我们服务器端的性能不是很好,那么服务器端怎样处理压力?以及,怎样做这些请求?
API Gateway是解决这一方法的重要手段。当有大量的请求时,它会把请求导到我们信息服务中去。但是,导量之前会有一个缓存,即访问同一个赛事信息,就直接把这个缓存给到前端。那么这些信息没有直接进行查询,而是缓存返回给了前端。所以,缓存是用来解决大部分静态信息获取压力的方法。
再则就是做统一的认证。在不同的服务间,80%的服务是需要登录信息,就是说登录以后才能操作。怎样解决不同信息之间的登录问题?难道需要每个服务在访问时必须访问服务器?解决方式就是你的访问不是访问服务,而是网关。因为它也信息,也有token过期时间。
API Gateway所有请求访问网关时,我们就可以记录下所有的访问信息和所有的日志。在不同的服务之间的接口是不规范并且不一致,API可以帮助我们做请求格式的转换。
上面我们提到有一些服务需要进行大量的扩容。这个扩容过程中我们怎样确定大量的端口?我们采用的是Consol的服务发现的工具。
这个服务工具的大概工作原理是:在所有的1字节上都有一个Consol的service。因为所有的Docker都部署在ECS上,ECS上不仅部署一个服务,还可以同时扩容3个、5个、8个。但是,这些服务都是随机端口,你不知道地址在哪里。
这个时候,每一个ECS上都有一个Console的实例。这个Console实例会将ECS上所有的服务的地址、IP和端口记录下来上传到中央控制服务发现区。然后,API Gateway直接访问Console Master来知道哪个服务布置在哪个端口上、哪个IP上。这样就解决扩容的时候,服务器在随机端口的应用情况。
监控日志对于产品性能是非常重要的。对于传统单体结构,可能是所有的CPU都在对应的服务器上,一旦扩容所有的都扩容了。但是,我们想精确地知道这些服务的瓶颈,并实现定向地扩容来有效地解决瓶颈问题。对于日志而言,它知道我们产品的问题,可以进行准确地定位。所以,日志是直接的手段也是经常用的手段。因而对不同的服务我们会采用统一规范的日志,比如说记录用户的IP,记录用户访问的时间,记录请求的数据以及记录一些错误。
Lambda的特性是:
1. 计算服务
2. 无服务器
3. 自动扩展
4. 日志和监控
由于我们的服务不需要任何存储,只需要资源。比如说一个线性存机数,我们下周给用户这样一个功能。这样的话,你可以每天有一个权利来得一个随机翻倍的次数。这个只需要一个随机数发送给下注的,所以随机数没必要单独开一个ECIS来单独服务这个服务。
Lambda就是无服务器的功能。然后,它可以自动扩容。上图是Lambda的情况,蓝色的是次数,红色的是规模,所以是非常吻合的。它对于business是非常适合的,而且这种场景是很好的。这是我们的一个小的微服务的架构:用户访问一下Gateway,然后访问你的服务,服务和性能记录到不同的云平台上,Ops就可以监控现在的云平台的情况。
最后,讲一下面临的挑战,主要是人的挑战,还有一些技术的挑战。
人的挑战是我们怎样将我们的组织。因为大型的传统金融保险公司都是金字塔式的结构,就是经理、职员、小组。这种结构就会对传统的人员架构产生冲击。每一个小组都是一个微服务,有一百个就是二十个、二十个小的团队。这些小的团队怎样组织在一起。不同的服务之间都有不同的小组,那这些小组之间的沟通怎样解决他们之间面临的问题的。这就产生了孤岛效应,大家不知道对方在做什么,也不知道对方面临的问题是什么。
由于不同小组之间有自己的优先集,每一个都认为自己做的优先集是最高的,这就面临资源竞争。我们是不是要招最好的程序员,用最好的资源。
还有技术债,不同团队面临的技术债不一样,怎样解决一些别人已经解决过的问题。这就需要团队信任。虽然,不同的团队之间进行交互的频率非常少,但是可能会存在信任问题,即:我怎样使用,你的API是不是不稳定,前端获取数据是否跟你不一致等问题。
总之,微服务只是为了解决问题的手段,没必要把所有的事例都以微服务实现。只要解决自己的问题,它就是一个正确的方式。