@gaoxiaoyunwei2017
2017-11-10T10:59:27.000000Z
字数 22270
阅读 512
大熊
开始之前我先做个简短的介绍,我叫王磊,目前在华为/2012技术专家,在此之前我在ThoughtWorks任首席咨询师,过去四五年里工作的范围都是聚焦在持续交付、微服务、DevOps领域。在过去几年我在社区做了一些个人的贡献,包括2015年写了一本书叫《微服务架构与实践》,这个是讲我们在ThoughtWorks当时的一个案例,这个案例在《Building Microservices》那本书里被Sam Newman提到,讲了我们过去一些好的微服务的实践,同时我现在也是《DevOps Handbook》这本书的译者。这本书还有其他几位译者,像马博文、刘征。之前一段时间我在GitBook上发布了《Pact契约测试中文参考指南》,这个是讲微服务一些服务间的契约测试。后面是在社区里的一些贡献,我目前在西安也举办了一个DevOps Meetup,主要是在这个社区里定期做一些关于DevOps微服务的分享。
这是这本书几个部分,第一部分是三步工作法,花了四章讲三步工作法到底是什么东西。第二部分讲一开始如果企业去落地DevOps应该从哪里开始入手,花了四个章节讲选择价值流,从当前价值流可视化,依据康威定律组织架构。今天我会跟大家聚焦第三部分,讲的是三步工作法里的第一步,我们如何去打造流动原则流动,这里面包括五个章节,对于整个流动原则而言,第一我们可能需要一个非常坚持的流水线,来帮助我们把代码提交,然后去做持续集成、自动化测试。第二步是讲自动化测试机制,对于代码提交而言,希望在整个流水线里能够通过自动化的工具的方式帮助我们做验证,这里面涉及到如何选择自动化测试策略以及如何引进一些好的自动化测试的实践。后面分别是讲有了前面的持续集成、流水线、自动化测试之后,怎么样能够强化持续集成实践,里面有一些注意的要点。有了前面几个步骤后,如何通过拆分架构帮助我们低风险做发布。
开始第三部分之前再回顾一下三步工作法的核心。《DevOps Handbook》这本书里非常好的一个理论基石就是这三步工作法,非常容易理解,在整个DevOps演进过程中、落地过程中也是非常重要的。第一步主要构建的是从Business,当我有业务需求,我怎么样通过我的分析、开发、测试、部署,让它尽快上线,这是第一步工作法要干的。这里面涉及到一些细节,可能会涉及到需求分析可能会涉及到开发、测试,同时在开发过程中是不是需要引入一些敏捷、Scrum、看板这种时间来帮助我们做可视化,从而减少工作过程中一些浪费。另外在这过程中需要不断提升质量,来构建一个非常稳固的正向流动机制,帮助我们把需求一点一点从分析设计开始,一直到最后上线和运营。这是三步工作法里第一步主要干的事情。当我们有了第一步正向的流之后,第二步主要是来构建逆向的反馈闭环,这里希望能够知道当我把特性布到生产环境之后,我们需要监控什么样的数据,我们需要收集什么样的日志,来帮助企业了解到我上线的这些应用的健康性什么样,会不会对用户带来一些影响,比如他的SLA是几个9,当它出错之后怎么样能够快速通过某些机制修复这些缺陷。除了技术之外,讲了可用性,讲了SLA,讲了监控告警,其实在业务上也非常重要,当我们上线一个特性之后,我们希望能够跟用户产生互动,从用户那里获取反馈,从而构建三步工作法里的第二个反馈闭环。当这个环建立好之后,第三步是循序渐进在这个环内部不断优化实践,这里涉及到组织上、文化上以及员工能力提升,我们现在讲全功能团队、全栈工程师,这都是在整个闭环的演进过程中不断优化局部的细节,最终经过很多轮的尝试,比如一年两年、三年五年,经过很多轮尝试之后,像大家今年在业界这些报告看到的,比如交付周期从过去的几个月降低到几周甚至几天,质量、员工满意度都会做提升。这其实是一个循序渐进的过程,并不是我们一开始就把所有工具都采用了,然后把架构都拆成微服务了,就实现了DevOps。
流动原则里主要包括五个章节,第一是讲构建流水线,流水线是代码提交的基础。第二是讲自动化测试,后面是讲持续集成、低风险发布和对应的架构。
这个书一开始就给了一个例子,来说明在这个例子过程中演进所需要的一些关于流水线的事情。这是澳洲某电信公司的案例,当时这家公司想做一个企业数据仓库项目,投资2亿元。在这个过程中分了十几个工作子系统,同时他的开发流程是传统的瀑布模型。过了一年后发现这十几个系统只有一个能够达到他们自己内部的验收标准,当他们把这个能够勉强验收的系统交给用户后,用户又花了6个月来完成测试用例验收。虽然时间花得很长,但是质量并不尽如人意。在这过程中,作者也分析了一些潜在的因素,为什么系统在这么长时间,交给客户需要花6个月完成验收,到底什么因素会导致这里面的问题存在。第一,开发团队需要花将近8周时间来准备环境。当我们需要做测试或者验证时,首先你的环境准备就成问题,当环境准备成问题的时候,带来的因素可能就是大家不愿意去准备环境,我更多的是想利用现有的环境,现有的环境下经过修修补补,可能你的环境已经千疮百孔,所以会带来很多不可预知的问题。第二,团队协作与集成成本高,这个过程中因为涉及到本身是使用瀑布开发流程,大家的工作都是前后交接,设计完了交给开发,开发完了测试,这里面的反馈周期就比较长。第三,每次在环境中部署代码,都会存在各种环境问题。因为准备环境比较困难,所以在这个环境中每次去部署代码或者部署他们所实现的特性的时候,都会存在各种各样的环境问题,比如环境的版本不一致,或者环境里的某些配置信息设置的有问题。第四,在环境里不断修复各种问题,但是这些变更没有被版本控制系统记录。这其实是这家企业当时花巨资完成这个项目所面临的困境以及作者所识别的一些现象。
在2014年DevOps企业峰会时,当时这个项目的负责人去DevOps企业峰会上讲了这个实践,构建持续部署流水线,那样讲了中间过程中经过他们努力之后所达成的效果,第一,生产力接近翻番,这个很重要,员工的交付能力变强了。第二,交付周期超出预期,刚才讲的例子里,交付周期非常长,将近一年时间,他们其实希望能够把交付周期缩短到2、3个月。第三,环境准备的等待时间从8周缩短为1天。第四,交付成本和缺陷数量等指标超出预期。意味着在整个过程中通过引入一系列的实践,包括建立持续部署流水线,除了生产力翻番、交付周期缩短以外,同时对于交付过程中的质量也达到了很高的要求。怎么做到的,他列了四点,第一点,环境创建方式自动化。在整个演进过程中,其中有很重要的痛点,环境准备时间需要从8周缩短为1天,回到刚才讲的数据,如果环境的准备时间是8周,很多团队当我们想去验证上线的特性或者修复的缺陷的时候,可能是等不了这么长时间的。如果没有环境,那就意味着第一,我要么用现有的环境,现有的环境可能不是一个干净的或者新的环境,布上去可能会有另外新的问题。第二是我不验证,我认为我在开发人员的机器上已经验证完了,所以我可以直接部署。这都是非常有风险的事情。第二点,统一的版本管理机制。刚才讲到在他们演进过程中,包括在环境里做的一些缺陷的修复以及环境的配置,都没有记录到统一的版本管理机制里,这样就会导致我对这个环境的修改其实是不控的,因为完全依赖于个人的能力和经验,可能运维人员他的能力比较强,这次我在环境里发现一些问题,改了之后可能这次的版本能够运行起来,但是如果下次遇到别的问题或者同样的问题,换了另外一个运维人员,他可能不具备这么丰富的经验,他不一定能解决这个问题,所以这种修复其实是随机的。第三点,不可变基础设施,这点是讲我们在整个演进过程中,其实是希望我的基础设施是一种不可变的状态,主要是讲我并不会去改变这个基础设施,更多的是在需要的时候创建一套新的基础设施,因为通过创建新的基础设施,所有的流程、所有的环境都是可以保障的,比如现在在AWS上,它里面提供了非常方便的像EC2、VPN这种机制,包括像S3,我们可以很方便的通过脚本的机制创建一套新的环境。第四点,关于“完成”的定义,到底什么样的概念叫“完成”,可能开发人员认为我的代码写完了就叫“完成”,其实不是,只有当我的代码写完了,能够在生产环境上运行之后,这才是真正的完整,它是一个端到端的概念,而不是一个在局部的概念。这里面对于这种瀑布模型团队而言,他可能一开始很难达到最后上线才计划完成。这里面作者给了一个例子,至少在类生产环境中验证,表明虽然我的特性做完了,可能由于流程的原因或者公司的机制原因,可能经过测试环境和开发环境验证没问题了,但是我至少应该在类生产环境中做验证,所谓类生产环境主要是指理想情况下我是希望能够搭建一套与生产环境同样机制的环境,里面包括我的基础设施,包括我的数据,从而能够尽早在缺陷修复或者特性上线之前,能够在生产环境之前做一个更真实的模拟。
这里作者提了好几种方式,对于我们现在环境创建其实选择很多,公有云、私有云或者是基于VM,都是能够帮助我们创建环境的方式。第一,通过对“裸机”执行开机后的自动化环境创建,这个相对比较老,叫PXE,PXE运行在计算机主板上,当它启动之后,它可以通过PXE环境,起来后能够通过它自己的网络去远程获取一个镜像,把镜像驱动起来之后再挂载一些硬盘,这就是最早工作站的原理。早期在2004、2005年,我们也做过一个项目,使用PXE安装集群,原理也是通过主板上PXE环境起来之后,通过DXCP,给它分一个IP地址,启动之后再去把真正的镜像拷到本地硬盘,然后完成环境创建。这是相对老一点的方式。第二,使用操作系统配置工具。现在很多操作系统都有相关的配置工具,比如Jumpstart、Kickstart,它能帮助我们在操作系统层面去做一些预配置。通过这种方式,可以很快用这些工具帮助我们在裸机上创建一些新的操作系统,这种方式可以通过自动化的方式来完成。第三,云上创建环境。这个大家不陌生,现在云发展非常快,像亚马逊的AWS、微软的Azure,都能帮助我们在公有云上创建我们自己的环境,同时他也提供一些脚本或者他们自己的描述语言,我们能够在里面定义我需要几个节点或者需要什么样的网络环境,来帮助创建这类基础设施环境。第四,使用“基础设施即代码”的配置管理工具。这个有一些限制,通常不会去涉及操作系统,主要指的是应用层的配置。在2013年、2014年时,我们当时是使用亚马逊的EC2,当时是用Chef帮助我们完成管理工具包括应用环境的安装,它能做的是当我去新起一个节点的时候,比如我起一个裸机的节点,它可以是VM、EC2,可以是云上的节点,这时候我用这类工具,比如Chef、Puppet,它能够帮助我们安装裸机上的一些运行环境,比如像Tomcat、GRE,同时它也可以安装我的应用,来帮助我们简化环境的安装。最后一个是目前这几年比较流行的方式,使用镜像,比如像Docker,我在Docker里包括操作系统包括应用本身,当我在运行的时候,通过Docker的image就能启动一个Container,来完成环境创建。这些方式具体的场景可以来具体选择,它表示了一个核心意义,我们可以通过自动化的方式优化环境创建,错而使得当我们环境创建容易后,会给开发人员或者测试人员更多的方式,能够让他们迅速创建一套新的环境,在环境里验证提交的代码、BUG修复或者新的特性。第二是讲自动化,刚才这些方式大部分都是可以自动化的,自动化的根本原因就是为了消除过程中人为带来的一些不确定因素。搭建环境过程中,依赖因素非常多,第一是依赖于我这个人的经验,第二是依赖于过去的一些文档,比如有没有文档,可以照文档一步一步来。第三,通常也会收到一些情趣的影响,比如我可能今天状态不太好,我在执行过程中可能会发现一些错误。虽然是同样的文档,同样的步骤,我可能会出一些错误。他们都会导致在整个环境创建过程中会带来一些额外的成本。因此通过自动化能够确保环境本身部署的一致性。所谓一致性就是有了这个脚本这个工具之后,大部分情况我用工具去执行可能结果都是一样的,当然这个工具也是经过不断的演进,而不是说第一天做好它里面就没有BUG,它是经过不断演进后能够保证整个环境部署的一致性和可靠性。第二,有了这种机制之后,开发人员或者测试人员或者运维人员,他能使用现在我们这种环境自动化的方式去间他所需要的环境。回到刚才的例子,如果我们创建的周期是8周,很多事情时间是被浪费掉了,比如我作为开发人员,修复了一个缺陷之后,我想去验证它。现在的环境可能有人正在使用,我需要去做很多申请或者等8周时间来验证这样一个缺陷修复,这是非常不可接受的。但有了这种机制之后,我可以按需,可以很快的按这些脚本,用这些工具,能够帮我创建开发环境,从而迅速做隔离环境里去重现或者定位和修复缺陷,来提升效率。另外一点,整个机制是持续演进的,这个过程中可能很多朋友在担心我的自动化的脚本覆盖率不足,这个过程是一个循序渐进的过程,一开始我能把5%的工作自动化,然后发现这5%的过程没有问题了,那我们可以增加到10%、20%、30%,逐渐优化之后可能经过一段时间你会发现我这个脚本已经能够完全自动化帮我创建环境。这是个过程,第一点讲的核心是我们要改善环境创建的方式,从而提高开发人员或者测试人员在运行他们所需要的环境的成本。
这个大家不会陌生,从CVS到SVN再到GIT,都是我们常用的版本管理机制。这里面作者想表达的意思是,在整个产品或者应约交付过程中,我们应该把应用交付过程中所涉及到的很多环节的产出,包括我们所依赖的库,包括我们用到创建Scheme的脚本以及参考数据,包括我们搭建这个环境的工具脚本,像puppet、Chef,脚本文件以及构建容器的文件,所有这些文件都应该作为我们应用不可分割的一部分,把它提交到我们代码库里,这样做的核心的目的,当开发人员或者相关的测试人员需要这里的信息的时候,它是一个自包含的,通过版本管理机制就能够获取到我想要的数据和信息。第二点是因为在过去的版本管理过程中,其实是一个可以追溯的过程,我每次提交都会有相应的记录,不需要像以前保存word文档,比如v0.1、v0.2、v0.3,通过这种版本管理机制我们可以方便看到它过去版本的演进过程。还包括代码打包、部署、数据库迁移和环境配置脚步,这些脚本都应该放在版本化的管理机制里,帮助我们做日后的优化。在刚才的例子里有一点讲到,每次部署都会在环境上产生不同的问题或者各种各样的问题,但是对于运维人员,他修复了这些问题之后并没有把它记录下来,他是修复完了之后就保存在那个机器上。意味着如果这个时候那个机器出问题了,当我们换一套新的环境可能会出现同样的问题,如果当时的过程没有被记录下来,可能又需要花一定的时间和成本去定位、修复这个问题。应该把这些过程中所涉及到的信息都放在版本化管理机制里。
有一个故事,比如说对一个宠物而言,你会给它起名字,会精心照顾它,经常给它洗澡,喂一些好吃的,宠物在你心里非常重要,不可替代。但是还一种机制叫CATTLE,英文翻译过来比如像牲畜或者羊群。对于牲畜而言,其实每一个承担的作用都一样,比如对于奶牛而言,你不需要记住每一个奶牛的姓名,你也不会去特别照顾某一个奶牛。它的作用就是当我需要去获取牛奶时,我从奶牛身上获取牛奶。当奶牛生病的时候我就把它杀掉或者抛弃掉,这时候我可能会去买一头新的奶牛,或者从过去养的小牛里等它长大以后作为替补的奶牛。这里隐含的含义,对于奶牛这个场景,其实你并不关心,它们每一个都是一样的机制,当一个出现问题的时候我就立刻把它替换掉。回到我们这个例子里,作为不可变基础设施讲的也是这个,希望我们在环境搭建过程中,每一个环境其实都是一个CATTLE,意味着当我这个环境出现问题的时候,我做的事情就是把它shut down或者把它close,然后我用我们现在的这个脚本或者刚才讲的一些自动化的机制,迅速去重建一个新的,你会发现每次如果我的脚本足够强大、足够可靠,经过很多次验证,我每一次都可以去创建一个跟原来出现问题的环境一模一样的环境,这个就叫不可变基础设施,背后隐含的含义是杜绝手工导致的差异化,通过自动化机制重新创建来保证一致性。在2013年时,我们那时候刚迁移到AWS上,当时我的做法是,如果我们要去做一次新的部署,我不会去动现有的已经运行的环境,而是通过AWS提供的脚本,因为这个脚本里写明了对于运行这个应用我需要几个节点,需要一些用户,使用这个脚本在AWS上重新新建一套环境,这套环境跟以前是一模一样的,除了数据还没有迁移过来,环境本身是一模一样的。布上去之后,通过一些核心功能的验证发现这个版本没有问题,做的事情就是把流量迁到新的版本上,把以前老的基础设施close掉。应该保证基础设施是不可变的,需要的时候就通过现有的这种机制或者脚本,重新去建一套新的。
最后是有效定义“完成”,“完成”不是指开发人员完工,我们如果想构建这样一个有效的流动原则,它是指至少这个功能或者这个缺陷的修复在类生产含量中按照预期运行。这个例子是澳洲电信的负责人给的例子,尤其是现在Scrum或者精益看板所提的理论里,更多是讲完成是指真正的上线,当我们做的事情上线之后用户使用了才叫完成。在多个环境使用统一的工具(监控工具、日志记录工具和部署工具等),比如说我们现在有了开发环境、测试环境、类生产环境和生产环境,每套环境里可能有些企业受限于License或者是一些环境安装的成本,比如需要集群,但是我在测试环境里可能没有这个集群,它可能会采用不一样的工具。为了构建有效的流动原则过程中,如果你使用同一种工具,比如监控、告警或者日志记录工具,在这个过程中也是一个学习和熟悉的过程,有问题的时候能够尽早在上线之前就能够发现,所以工具本身最好能做到统一。尽早频繁地、小批量实施代码部署,降低生产环境的部署风险。每次提交代码时,我的代码量越小,我对现有生产环境所带来的风险就越小。
作者通过这样的例子来引申出通过构建这样一个部署流水线的基础,比如环境创建方式自动化,需要统一的版本管理机制以及不可变基础设施,同时定义什么是“完成”,以及至少应该在类生产环境上做验证,来构建这样一个持续部署流水线。
对于这一章节作者也给了一个例子,谷歌GWS团队,最早负责谷歌搜索主页和其他网页相关的应用。2005年时这个团队面临的网站跟很多企业在今天面临的挑战是类似的,包括构建和测试花费的时间长,变更成本高,交付周期长,这个环节其实是相辅相成的,如果构建的机制不完善、自动化测试的机制不完善,很明显你在后续需要花费更多的人力或者工具来做功能验证,为了保证回归测试,所以会导致整个交付周期变长。每次变更所有团队都会很紧张,开发也紧张,测试也紧张,担心这个功能到底有没有完成客户的需求,中间有没有BUG,测试担心的是上线之前有没有一些潜在的风险,会不会上线之后带来一些负面的影响。第三点是讲团队大批量且低频地提交代码,如果我们的自动化机制不完善,如果我们的持续集成不是很稳定,如果每次提交代码,作为开发人员,提交完之后,Build都会挂,很明显我不愿意频繁的去提交,更多的时候我希望能够做完一块代码后,非到逼不得已我才做一次提交,它其实会导致团队大批量的提交,频率也会比较低。代码未经有效测试直接投产,原因也是因为我的环境准备不完善,我的自动化测试机制不好,所以会导致测试没有经过完全的测试之后就直接投产。
在这种情况下这个团队制定了一系列举措,包括这四电。第一,构建快速、可靠的自动化测试套件,来保证当我有代码提交之后能够通过自动化的方式验证,而不是通过人为手动方式验证,这样能够提升测试效率。第二,构建持续集成、持续测试的机制。这点表示的是当开发人员提交完代码后,需要有这样一条流水线,来帮助我把代码合到我的分支里,去运行这些自动化测试,或者通过这种机制之后去把代码生成我们所需要的发布包。第三,不接受任何没有通过自动化测试的变更。当团队有了这个原则之后,团队成员提交代码时就要去完成这个自动化测试,就会循序渐进的去提升代码提升的质量,因为我提交业务功能代码或者是缺陷修复或者实现新的功能,就必须相应的自动化测试机制。第四,加强测试规范和测试指南。这个是一些意识,比如测试规范以及测试过程中所需要的一些过程和步骤。通过采取这样一系列措施他们获得了什么样的收益,通过这一年时间,这个团队成为公司里面最具效率的一流团队,同时他们能够非常频繁的发布,而且每次发布过程中会有很多来自团队不同成员的代码变更,那就意味着代码变更的频率增加了,每次当我提交完代码之后都会有可信的机制帮助我去做一些质量保障。新人能快速对复杂系统做出贡献,这点在现在微服务架构里谈得也比较多,当我们的服务拆分,当我们有足够的测试机制后,当我们把架构解耦后,新人能很快熟悉这个系统,它是由一系列机制保障的。谷歌首页能够迅速扩展新功能,这是2006年发生的事情,十年前谷歌所面临的问题跟我们今天很多组织、很多团队面临的问题是类似的。这里面作者提到一个原则,如果没有自动化测试,代码越多,测试代码所花费的时间和金钱成本也会越高。当这个成本增加以后会带来的效果是不利组织或者团队的规模化扩张。因为我的人越多,自动化机制不健全,提交代码的频率越高或者代码量越大,风险越大。在谷歌GWS团队里非常注意自动化测试,通过自动化测试作为入口来提升效率和质量。
这个团队在谷歌里又做了一些关于自动化测试的推广,建立更多的影响力,因为他们认为这个机制非常好。在2006年时,谷歌当时并没有很强大的自动化测试团队,也没有很好的文化来去表明我的代码是需要自动化测试的。当时做了一些小的活动,比如成立测试兴趣小组(Testing Grouplet),成立这样一个小的虚拟组,帮助大家培训测试计划,定义你的测试计划,同时贴“厕所小报”,有些公司可能会有这种文化,会把一些很重要的宣传海报放到厕所里,这样大家每天进出的时候有意无意的就能扫两眼,就能知道一些关键字,形成一种意识上潜移默化的作用。宣传测试认证路线图,除了测试小组测试培训计划,他们在内部也实现一些测试人员的认证路线。举办fix-it活动,同时帮助其他团队优化自动化测试流程,这个团队除了自己做得好,也花了一些精力去做推广。带来的效果很明显,到2013年时,在谷歌内部每天有4万次代码提交,每天有5万次代码构建,有时候代码提交会触发好几个流水线的构建,会有12万个自动化测试套件,同时每天会运行7500万个测试用例。在2005、2006到2013年,在过去的六七年时间里,通过一系列的机制包括工具上的文化上的活动上的,谷歌对整个测试机制已经做得非常好。还有一个例子,通过自动化测试和持续集成,使谷歌的四千个小团队保持高生产力,同时所有的代码都存储在一个共享代码库中。之前在网上有一些文章,讲谷歌的单一代码库,里面大概有十几亿个文件,非常多的代码,每一次提交之后都会有自己的一个构建工具,他会去做增量的构建,去做增量的测试,来保证每一次开发人员所提交的代码都是经过验证,也会有一些review机制,来保障代码提交的质量。从源头上提升这个代码的质量和交付质量。谷歌大概有5%,当时的5%是100多名工程师,专门做测试工程、持续集成和工具的工作,提升开发人员生产效率。
刚讲的谷歌的例子里抽象了几个非常重要的实践,第一是持续构建、测试和集成,我们总是使代码处于可部署和可交付状态。这句话是来自于Jez Humble的《持续交付》那本书,他所反映的思想,如何时候当我们提交了代码,都是能够通过一个流水线和通过我们的测试流程,保证这个代码经过验证之后,能够在类生产环境、测试环境或生产环境能运行起来的。第二点,在整个构建过程中我们需要有一个清晰的依赖管理或者独立的构建和测试流程,这点其实是从可维护性来考虑,我的代码在构建的时候需要有哪些依赖包,我才能够让他去做独立的构建,以及在这个过程中它的测试流程是什么样的,比如单元测试、集成测试、UI测试,大概的比率是什么样的。第三,将可执行文件与配置打包,能够自安装。做JAVA的同学不会陌生,习惯把生成的代码比如打成一个war包,war包当然能够解决一部分问题,但这里面有一个点,如果我们希望配置文件跟应用一块发布,war包肯定是不可能了,除非我们把这个配置文件放在这个war的class pass里,这样其实并不是很方便去升级。一般作为是我们可以通过RPM或者yum包,把我们所需要的代码文件或者应用包以及包括一些配置文件,把它打包到一起,作为一个RPM或作为一个yum发布出去。应用打包到可部署的镜像中,这个是可选的,也是因为Docker或者AMI,我们也可以选择把这种包放在镜像里面,让镜像作为我们交付的产物,有了镜像之后我们可以通过这些镜像去兴起一个新的容器也好节点也好,来完成环境的搭建。最后一点是能够以可重复的方式进行类生产环境的搭建,这跟我们上一章里讲的类似,要通过自动化的机制能够去完成类生产环境搭建。另外一点,要把这种自动化的机制还要集成到持续流水线里,来帮助开发人员或者测试人员在需要的时候能够通过流水线,来帮助我们生成这样一个环境。
这里还提到集成测试的最佳实践。集成测试这个概念大家可能并不陌生,目标是希望每次代码的提交能够触发这样一个集成,并不是在最终,比如说在瀑布模型里可能需要团队几个月的开发之后,最后有一个专门的集成阶段来做集成,在这个阶段里通常很多风险也是不可控的,比如我们团队独立开发完功能,我认为我的功能已经开发完了,但发现最后在集成阶段会出现各种各样的作用,可能会导致项目延期。持续集成希望的是尽快集成。在运行持续集成过程中,这章里提了一些最佳实践,第一,需要全面可靠的用于验证可部署状态的自动化测试套件,通过这样一个测试讨论,当我们的代码提交了,我就能去跑单元测试也好、功能测试也好、UI测试也好,来完成自动化的验证过程,从而证明当我跑完了这些自动化测试套件之后,我的这个包发布是核心的。第二点是测试验证失败时会停止整条生产线的文化,这个在后面也会讲到,有一个故事叫“安灯拉绳”,当我把代码提交到流水线上之后,如果流水线失败了,之后怎么办,后面的人是不允许提交代码的,做的事情应该是通过一些比如说Dashboard,或者通过一些机制,来通知到整个团队,让大家意识到当前了流水线已经被破坏了,我们需要做的事情是有人能够停下来去修复流水线,从而不会阻碍后面的人继续提交代码。里面还隐含了一个,对于流水线的提交而言,当这个流水线正在运行的过程中,通常是不允许另外一个人再提交代码的,所以就意味着作为开发人员,当我提交完代码之后,持续集成正在运行,另外一个一个人这时候不应该提交代码,因为它会导致一些不可预知的状态,比如如果你提交完成后,代码发生错误了,到底是我们俩谁导致的错误。第三点,开发人员工作在主干上,小批量提交变更,而不是长时间工作在功能分支。在很多公司内部当我们做大的特性开发时,大家通常也会去拉一个分支,拉一个Feature branch,来实现这个Feature的特性,当你这个Feature branch拉出来之后,开发的时间越长,当你合入的时候风险就越高。当我拉出来之后,两条分支都会有代码提交,最后合的时候会有很多冲突。在持续集成最佳实践里提倡的是你可以拉功能分支,但是不应该长期工作在这个分支上,它其实会带来你在最后集成时一个风险。另外有一些场景也会提到单主干分支,这个是讲通过一条分支来控制包括代码缺陷修复,包括功能特性开发,这里面就会有一些别的机制,比如像当我的一个功能可能需要花三个月来开发,但是为了保证持续交付,修复完某些缺陷,比如按天或者按周就能发布,这时候我就需要引入一些开关的机制,来帮我工作,在我的特性没有开发完成之前,我在单主干分支里,虽然能够发布,但是我的特性并没有被用户可见,来保证不会影响到用户的功能和行为。
在整个流水线里,自动化有一些策略和机制,第一点是拒绝Night Build,这个跟构建是有关系的。我在2012年时,那时我们每天万说都会做Night Build,每天晚上会有一个自动化的验收平台,它每天晚上会跑一次,第二天早上开发人员来的时候就能看到这个跑的结果。听起来比较好,其实在实际过程中会遇到很多问题,当我在晚上跑完之后,理想情况下测试用例都通过,没有问题,但是如果这个时候这个Build挂了,第一是没有人去触发它,第二点,当大家早上来的时候,第一件事情是做今天要开发的功能,比如修复缺陷或者实现新的特性,并不会去管这个Build,只有等到第二天晚上的时候才会重新去触发这次构建。因此这里面的反馈周期非常长,而且不利于我们发现问题,我们希望能够通过构建一个快速的机制,比如每次代码提交都会有一个构建的机制,能够告诉我反馈的结果。另外一点,需要梳理自动化测试策略,典型的测试策略是左边这副图,ideal理想的,其实是这样一个金字塔。金字塔最底端是UT,依次是组件测试、集成测试、API测试,越往上越偏向于手动,比如最顶层是Manual Testing或者探索测试。左边这个图有个非常明显的规律,越往上走,测试所带来的价值会越大,因为对一个功能而言,一定是你用户流程的测试的价值是最大的,作为开发人员我写的单元测试,其实离真正用户那个场景可能还比较远,因为它是由很多小的零碎的部分组成。在这个金字塔里越往顶层走,测试的价值会越大,但是越往顶层走,测试的反馈周期会越长,比如说我们用手动测试或者通过自动化的UI测试,它的周期也会比我跑单元测试周期要长得多,这是理想情况下测试金字塔单元测试占得最多,在最底层,依次有组件测试、集成测试、API测试,越往上走越耗时,越重要的测试会越少。但是在现实过程中通常是用冰淇凌型,右边这副图,像一个冰淇凌甜筒,我们会把很多测试放在最上面,这类测试比较简单,可以根据用户场景就把这个测试用例写完,写完之后可以有专门的测试部门,他们也不需要等代码,他们就可以按照我的测试用例去运行这个测试,这是听起来比较容易的。越往下走,开发人员很忙,都忙着交付特性,所以开发人员写的单元测试就比较少,而且也并不会在整个交付流水线里起很大作用,所以通常是右边这种测试策略。对于这种测试策略而言,每次发布成本非常大,原因是因为最上面的测试太多了,当我每做一次回归测试可能都需要几天甚至几周的时间,代码提交之后怎么能快速发布。在整个应用开发过程中,尽量满足理想情况下测试金字塔的策略,把我们很多测试放在UT或者是Component测试里,让他们通过自动化的方式来做验证,从而保证整个代码的交付是个可靠的。
另外一点,我们要想办法加快测试执行的速度,同时要尽可能多的将手工测试自动化。个作者也推荐了测试驱动开,这个在敏捷里提得比较多,TDD/ATDD,这里有一个数据,采用了TDD的团队与不使用TDD的团队相比,虽然多花15%-30%的时间,但是整个代码的缺陷率恢复降低60%-90%。这里是大家可以尝试的一个出发点,你也可以尝试拿你的数据跟您的领导做一些沟通,加强在UT级别或者是更底层级别的测试,一方面是保证车时覆盖率,保证正确性,第二是提升测试效率。
第三,当部署流水线失败时拉下“安灯拉绳”,“安灯拉绳”是从丰田的精益里来的故事,左边这副图,这是在丰田的一个车间里,上面有一个绳子,当这个车在流水线上如果出现什么问题,这个操作工人就拉一下这个拉升,拉了之后当前这个流水线会停下来,目的是告诉整个流水线上的工人当前这个车的交付过程中遇到一些问题,大家需要有人去检查到底哪一步出现问题,而不会将错误流入到下一个环节,这就是这个拉绳起的作用。在软件领域尤其是持续集成领域,刚才我们讲了有一个文化是当我的流水线出现失败后,需要立刻停止流水线,需要让团队里面有人去检测到底哪一步失败了,不会把错误蔓延到更大的范围。当构建失败之后,问题被解决之前,不允许其他任何人将新的变更提交到变本控制系统里,他的解释系,有的代码提交造成构建失败,但没有人修复,如果其他人在已经失败的构建上又提交一份代码变更,会带来一些负面的影响,比如现有的测试都不能可靠的运行了,新提交的代码这时候如果导致的问题,怎么知道哪里出现的问题,到底是以前遗留的问题还是这次提交导致的问题,所以会带来很多成本,需要去检测和修复这个缺陷。因此当流水线失败时需要通知到整个团队,要有人能够解决这个问题,大家可能会想,这个问题有可能是环境问题,有可能是一些不可知因素的问题,这种场景也会出现,这个时候如果我们在10中、15分钟解决不了,那就回滚,把这次这个人的提交恢复到上一状态,目的是不影响后面人继续提交,同时不会带来质量风险。
这里面包括几个核心点,第一是小批量开发与频繁提交,在第一个例子里已经提到,当我们去做小批量提交时,所带来的一些优势,我的每次提交都会因为它的代码量比较小,所以不管是代码review也好或者是测试验证也好,或者是对其他周边模块的影响也好,相对都会比较小,概率会小一些。这时候即便我们把它上到生产环境,如果发现问题,很多时候回滚的成本也比较低,可能很容易回滚到上一版本,同时很容易定位问题。但是对于大批量代码,因为代码多了,里面的风险就多了,这是很以理解的。同时我去定位问题、发现问题的成本都会相应增加。另外一点是流水线的周期,当我小批量频繁提交时,我们会更在意这个流水线的效率。比如我们是一周或者一个迭代去提交一次代码,跑一次持续集成,那个时候这个流水线跑得快和慢你可能不太关心,因为你每周每个迭代都得提交一次。但是如果你每天都提交,你会发现如果这个流水线比较慢,我们会把这个流水线的效率想办法提升。通常建议,他会把流水线分成好几个阶段,第一个阶段他会做一些快速的验证,通常是10到15分钟,做完验证后再进入第二个阶段,来做一些功能性的验证,来保证如果在第一阶段有错误,比如第一阶段里我作为开发人员提交完代码后,可能非常重要的某些功能失败了,我立刻就能获取反馈,而不需要等整个流水线跑完之后才能获取反馈,这也是持续集成里用到的一些实践,能够帮助我们降低反馈周期。
刚才讲到基于主干的开发,这里面能够促使这个团队频繁向主干提交代码,减少批量尺寸。在合并问题上,如果只是用主干开发,我每次提交代码都会去合并。所以不会出现我们一开始讲的,特性分支拉出来之后,因为这个特性比较大,导致这个特性的开发周期很长,而最终在合并时出现风险。因为我每次提交都要合并到主干里,所以每次都会去不断的合并,不断的集成,从而降低最终集成的风险。保持主干处于可发布的状态,这一点非常依赖于自动化测试机制。修订“完成”的定义。最下面有一个报告,这个报告是Puppet在2015年发布的DevOps状态报告里显示,基于主干的开发能够带来更高的吞吐量、更好的稳定性、更好的满意度和更低的职场倦怠率。这个结果大家可能觉得有点抽象,但实际真的是这样,如果你用一个基于主干的开发实现之后,它其实对于这个团队的协作和测试要求非常高,第一点,我每次提交都要保证测试能够测到,不管是新的特性开发还是老的bug fix。第二,我需要有一种机制能识别我是在特性开发过程中还是说我的特性已经可以交付了。刚才我也提到,对于某些比如说三个月里开发的特性,当我们做一次现有功能的bug修复的时候,我不能等到那三个月特性做完之后再去发布这个bug,我应该会采取一些开关或者一些别的机制,来控制这些正在开发但是还没有开发完成的特性,应该怎么样在主干上去管理。看似是基于主干开发,背后隐含了很多团队里做持续交付的工程实践,来帮助我们提升整个流程的效率和质量。它也会带来更好的稳定性、更高的满意度和更低的职场倦怠率。
Bazaarvoice,这个公司是给这些电商,包括给百思买、好事多这些电商提供点评和社交功能的平台,他们通过持续集成的实践,帮他们解决了在交付过程中的一些挑战。当时的背景,营收每年1.2亿美元,当时准备IPO,整个应用是单体应用,将近500万行代码,涉及到4个数据中心以及公有云,一共1200台服务器,当时是希望能够使用敏捷流程,将迭代周期算到两周,同时把这个单体应用解耦成微服务架构,另外是实现一些有效A/B测试,加快新功能上线周期,这是期望值。
但是他们在运行过程中发现三个核心问题,第一,缺乏自动化及如果我们把迭代周期从以前的几个月里降低到两周后,会发现在两周里因为测试的自动化不足,所以会导致你的两周过程中需要花将近一周甚至时间去做功能性验证,这样就会把开发的时间缩短了,因此它是不利于产品发布。但是如果你要不做充分的测试验证,他又不会发现一些故障,因此这个机制是需要去解决的。第二个是分支策略,开发人员可以直接把代码提交到发布版本,这就意味着我对发布版本的控制会存在一些风险,开发人员因为他会直接把代码上传到发布版本里,发布版本由于上面的机制,测试不足,所以有些故障不容易识别出来。第三点,团队缺乏微服务相关的经验,当去做了一些服务拆分后,涉及到一些服务间的协作包括架构的解耦,包括发布过程中,因为服务化之后每一个服务都是可以被独立发布的产物,所以发布过程中会带来一些额外的问题。他们怎么应对,第一,开发人员停止功能开发,六个星期投入到编写自动化测试的工作中,提升测试力度和自动化的机制。第二,强化JUnit、Selenium回归测试,透视使用TeamCity搭建流水线。第三采用主干开发/分支模型做发布,这个其实对开发人员代码提交做了质量保证,当我提交到开发分支之后,可能会有一个专门的check点,到了这个点之后会拉出来一个发布分支,可能中间过程中就不允许再提交代码了。最后一点,持续构建,持续运行测试,持续集成。
这是Bazaarvoice在整个演进过程中通过持续集成实践带来的效果。2012年1月份,上线之后遇到了44个客户事件,到3月份延迟了5天,遇到了5个客户事件,到3月22日,准时,两周发布,上线之后有一个1个客户事件。到4月5日,零客户事件。通过这种机制,能够显著提升交付过程中的质量和效率。
首先是自动化部署流程,有了流水线,通过之前讲的持续集成以及一些可靠的自动化测试套件之后,接下来我们面临的问题是怎么做部署。这里面给了一些场景,第一,我们可以把代码打包成更方便部署的格式,比如不仅仅采用war包的方式,而是采用RPBM、yum的机制,或者是基于Docker的image或者基于AMI的方式来完成部署,这样能够简化在部署过程中的流程。第二,创建预配置的虚拟机镜像或容器,可以把我的环境包括我的操作系统,包括我的包,包括我的配置信息,都放在这个虚拟镜像或者容器里,有了这点后当我再去做部署,只需要基于现有的镜像去启动一个新的节点,或者基于容器镜像去启动一个新的容器,这样会方便很多。第三,自动化中间件的部署和配置,可以使用像Ansible或者puppet这些工具。复制安装包或者文件到生产服务器,重启服务器、应用或者服务,或者是基于模板生成配置文件,这一系列的机制都是希望我们能够优化我们的自动化部署流程,让这个部署变得更一更简洁。最后一点,脚本化和自动化数据库迁移,因为数据库其实不像应用,应用通常可以是无状态的,里面不包含任何状态,所以我们可以直接把它shut down掉,但是数据库通常不能这样做。数据库升级的时候,有时候会停机一段很小的时间,同时要很清楚的数据库升级过程中如果出错了我们怎么会,如果需要自动化的升级过程中,我们对数据库的变更一定是要兼容现有的应用,我可以通过比如一些数据库升级工具,如果发现错误能够帮助我们很方便做版本管理做回滚。有了这种机制之后你可以把它集成到流水线里,通过这些工具所提供的版本管理,比如我去升级到新一个版本的数据库的变化上,如果发现问题可以再迅速回滚,可以帮助我们某种程度上解决自动化数据库升级。但是对于数据可能需要额外的一些机制,因为当我的数据库之后,我的数据可能需要做一些格式或者一些数据上的调整,这里涉及到一些程序或者开发人员需要预先准备好,完成数据的导入。
什么叫标准化,跟我们刚才讲的不可变基础设施非常类似,我们应该能够用相同的机制来处理所有环境的部署,不管是开发环境、测试环境还是生产环境,当我用同一种机制去完成部署的时候,带来的效果是什么,这种机制可能一开始并不是很完善,但是随着几千次、几万次的实验之后,它已经在很多环境里尝试过了,已经部署过很多次了,所以它是可靠的可信的。他不像依赖于某一个有经验的工程师,他是自动化的,同时他又经历过很多次的铅锤百里,所以我们能够认为他部署的时候是不会产生任何异常的。第二点,部署以后的冒烟测试,当我们把应用部署到生产环境或者类生产环境之后,应该有一些很简短的非常重要的测试用例,来帮我们做验证。这些测试用例不用很多,主要做的事情就是当我部署完一次新的应用之后,他能够通过发一些请求,比如从最外端给UI发请求,或者模拟点击UI的某一个button,能够跑完整个应用里背后相关的组件,比如数据库、消息总线或者外部的服务,来确保这次部署端到端是通的。第三点是建设和维护一致的环境,这个其实跟第一点是非常类似的,环境的一致性来保证每次创建时都是一个统一的环境。最后一点,部署环境如果发生任何问题,我们也要拉下安灯拉绳,通过这个方式来保证流水线上的这些部署机制能够标准化,同时它也有一套标准的流程,当我们出现问题的时候,开发人员或者是运维人员能够停下来去修复出现的问题,而不是把问题留到下一个阶段,会导致呈现更多的错误。
所谓自服务,更多讲的是通过一系列的实验或者尝试之后,我们应该把这种能力开放出去,比如以前这个脚本执行需要运维人员SSH到某台机器上完成部署,能不能把它集成到CI上或者提供一些管理的终端,让开发人员或者测试人员能够通过这种机制去自服务自部署,这时候我就可以作为开发人员、测试人员,按需完成我的部署。比如我需要创建一套新的环境或者需要建立一套测试环境,我就可以通过这种自服务的机制,通过点一些button或者通过调用某一个接口,来完成第一构建我的软件包,第二做冒烟测试,第三把这个包部署到我想要的环境上。
当我们的自动化程度越来越高的时候,当我们的部署脚本变得越来越完善的时候,就可以考虑把它集成到我们的流水线里,最大的好处在于团队,当然也可以设置一些权限来定义用户群。通过Jenkins一个button就能完成对某些环境的部署。我们过去在一些实践中,通常情况下当我的代码打完包及运行完单元测试、组件测试以及运行完自动化功能测试之后,它可以被一键部署到QA的环境里,它可以被开发人员、测试人员一键部署到类生产环境上,同时它也可以由最终的更小一部分人群,比如产品经理,他可以选择把它一键部署到生产环境上。通过这种方式,虽然我们对人的权限做了限制,但是整个的机制和流程都是一样的,当你有了这个权限之后,你就可以通过一键的机制完成部署。它能够提升整个团队的效率,让相关的人员能够按需去完成环境创建和部署。
这里面讲一个例子,Etsy的一键部署,Etsy是美国的一家电商公司,主要卖一些手工的艺术品。这个是Etsy在2013年还是2014年的一键部署流程。包含三块,第一是Deploy to QA,修改个button叫Push to QA,第二是Save the Princess,这个讲的是做一些冒烟测试。第三个是Production。可能有一些权限的人或者所有人都能去点这个button,当我做完前面两步之后,后面这个Production就可以按需部署。下面这句话描述的是在Etsy,任何想要执行部署的人都能去部署,包括开发、运维、信息安全人员,Etsy部署流程已经变得相当安全和普及了,新入职的工程师会在第一个工作日里就去执行生产环境部署,包括Etsy的董事会成员,甚至连狗都能部署。这个是在2012年,在持续交付大会上,当时Etsy讲了他的持续交付实践及其中一幅图,对于button是由谁去出发已经不重要了,可以是一个小动物也可以是一个Ops或者董事会成员。到这个程度之后,整个流程已经非常牢靠非常可信,任何人包括开发人员、测试人员,我有都信心去发布我的代码,去发布新的特性,从而提交整个交付流程的效率。
当我们去做发布的时候也会考虑对于环境而言到底采用什么样的模式。对于现在业界讨论比较多的,第一是蓝绿部署,所谓蓝绿部署指的是我在发布时需要有两套一样的环境,比如对于这个例子而言,蓝色的是旧环境,灰色是新环境,它指的就是在现有的环境里,它已经运行着我们生产环境的应用,这时候我做的事情去布一套新的环境,可以通过刚才讲的部署脚本、不流程或者是不可变基础设施的机制,去创建一套新的环境,里面包括Web Server、Application Server或者是Database Server,当我布上去之后他也一个内部的URL,我们可以通过人为去验证我新部署上的这套环境有没有问题,如果验证完后没有问题,那么做的事情就是可以把那个路由从以前现有的蓝色环境里迁移到新的绿色环境里。这样带来一个好处,也叫零停机部署,因为中间的宕机时间非常小,我只是做了一个路由切换。这其实是蓝绿部署。
第二个叫金丝雀发布,指的是滚动升级。当我们在做升级的过程中,其实我并不是对所有的服务器都去做升级,而是先挑选一部分节点,把他们做了升级之后再来验证我已经升级的这些节点有没有出现问题,比如当我有了用户流量进来之后,迅速就能检测到我新部署的这些节点是不是能够在生产环境上,按照我们期望的和运行。如果发现没有问题,在扩大我的不范围,比如一开始我可能只部两台服务器,发现验证完成之后没有问题,那我可以布十台服务器,没有问题,那我就可以布剩下的一百台或者是更多的服务器。
这个是Facebook的一个例子,Facebook他在内部有三个虚拟的逻辑范围,第一叫A1,指的是Facebook自己的员工,A2指的是自己选择的一部分用户,比如可能根据地域划分,也可能根据用户的VIP或者level来划分,第三个才是所有的用户。所以在Facebook里当他们做发布的时候,他是先在A1环境里做验证,做完验证没有问题之后,四在去A2,再去做A3,来保证我的这个发布是安全的,如果出现问题的时候,我可以尽早去回滚或者去修复,而不至于在所有的用户群内产生一些不好的影响。这种发布叫金丝雀发布。
这就是关于自动化并确保低风险发布的机制,第一我们需要自动化了部署流程,第二我们需要把这个部署流程标准化,同时要将部署的机制以服务的方式提供出去,这个服务并不是指我们开发过程中API服务,而是指能够让开发人员或者测试人员他们按需去点一个button或者触发机制,就能完成这个部署。提供一键式部署的机制,最后是基于环境的发布模式。
首先对于架构演进的原则,在这本书里提到,在著名的DevOps案例中,如Linkedin、谷歌、eBay、亚马逊和Etsy,曾经他们的架构都存在很严重的问题,因为随着业务的增长、组织的扩张,我的架构无法适应这个组织的快速演进或者功能的快速交付,他们都需要在DevOps演进过程中,除了去构建流水线,除了构建自动化测试机制以外,我们也需要能够架构层面做解耦,让他们能够支持,更高效的去做交付,从而满足业务发展需求。
这里面谈到单体架构与微服务架构,这是这个书里列的一个表格,关于单体应用和微服务应用的一些比较,讲了单体应用的唯一,所有功能都在一个应用里,那个时候当然开始比较简单,所有的代码都是近身内部的调用。所以我在调用过程中效率比较高,开发人员在调试或者发现问题的成本都比较低。同时在部署的时候,我只要把这个EXE拷到某一个机器上就ok了。v2阶段讲是三层结构,这时候可能会有一些层与层之间的调用关系,但通常大部分的三层应用还是在一个里面。比如像我们十年以前讲JAVA的SSH,我开发出的应用他竟然也是一个独立的精神。所虽然我在逻辑上把它分了几层之后,那也是一个独立的精神。第三个阶段,微服务,这里不展开讲了,本身微服务的话题非常大,业界也有一些微服务的书籍,大家感兴趣可以下来读一下。微服务的核心主要讲的是每一个服务能够被独立的开发、测试、构建和发布的这样一个功能单元,意味着我们在微服务架构下或者当我们需要能够快交付的情况下,我们需要对每一个服务都去构建这样一条修,同时对每一个服务都会去定义它自己的单元测试、集成测试、契约测试或者是几个服务之间的端到端测试,来保证这个服务当我有了代码变更之后,不会影响现有的功能,也不会破坏现有功能。
这里讲一级亚马逊的架构演进,提了亚马逊的三个阶段。第一个在1996年,亚马逊当时是一个典型的单体应用,因为本身在一开始单体应用成本最低所以我可以让开发人员很快在单机上开发一个程序,比如用GSP、Tomcat去部署这样的应用,里面包含了所有的逻辑,包括了我的业务、产品显示、商品推荐、评论等。但是2001年到2005年的时候,亚马逊随着业务的快速增长,单体应用维护成本非常高,第二它是可伸缩性非常差,原因是因为我每次伸缩时都要把整个应用全部拷贝克隆,放到另外一个服务器上,这样成本比较高,同时它并不利比如有些电商的平台而言,我的产品可能访问量更高。我更多时候是去伸缩产品的服务或者产品相关的功能,对于一些别的功能,比如用户或者一些评论,我可能没有必要每次都按需伸缩,所以这个时候他就会把它演变成慢慢垂直的,按照这种分布式的机制去把这个功能化小。有了中间这个阶段之后,再往后走到了2015年,或者是2006年到2015年期间,亚马逊才有了更多的今天我们所描述的微服务架构,在他内部并不是这么叫的,但是做法遵循这种做法,我可以把这个应用拆分成很多更细粒度的功能单元,同时因为这些细粒度的功能单元,所以他能够帮助我们以更平凡更低风险的方式发布,只要保证发布过程中我的服务接口不发生变化,那么我就可以不断的上线。这有一组数据,在2001年的时候,亚马逊每天执行1.5万次部署,到2015年大概是136万次,我看到2017年,6、7月份我看一个数据,应该是在3000万次部署。一方面也是因为他的服务越来越多,另外一方面也是因为这个团队分得越来越细,所以会导致每一次提交代码或者修复bug,都可以去完成这样一个构建、测试、部署。
这里给了一个亚马逊演进过程中的经验,第一,严格遵从面向服务的架构设计。第二,禁止直接访问数据库。这个指的不能访问别的服务背后的数据库,而是需要通过调用服务之间的接口去访问相关的数据。第三,切换到面向服务架构后,我的组织需要九做相应的变化。亚马逊在他的微服务案例分享过程中多次提到,他的每一个服务的维护都是由非常小的团队,自己来分析,自己来做代码实践,自己来做上线。所以也保证了在整个架构演进过程中的灵活性。
这本书里其实讲了一个绞杀者模式,这个模式在今天我讨论微服务,大家可能都听过。当我们有一个遗留系统之后,这个时候我们做的事情是不断有一些新的业务或者是抽取现有的,也通过这样的机制来不断退化原有的机制。这个隐喻来源于热点雨林的树,发现对于过去的一些老树而言,生产过程中怎么慢慢退化,我发现旁边有一些新的枝叶长出来,它不会慢慢缠着这个老树,当我新的职业越来越多的时候,最终有一天老树会退化死亡,对于这个模式而言,当我们逐渐把应用拆成一些服务之后,当我们逐渐把架构解耦之后,循序渐进的用新的架构,可能一开始的数量比较少,逐渐去替换现有的架构,最终经过三年五年,夸张一点,一年两年,你会发现很多功能已经在新的服务里实现了,而老的那个习惯慢慢就可以退休了,这是绞杀者模式。另外一种叫修缮者模式,讲的是对于一些新的服务、新的功能,比如现在我的业务系统需要增加一些新的Feature,这个时候我就不往现有的代码里合了,用一些新的服务的机制去定制我的功能实现,这样就让老的系统跟新的服务一块来完善,实现这个功能和产品的特性。慢慢的随着我的新功能越来越多,也会形成一套新的服务体系,这个时候我也可以考虑用以前的绞杀者模式,慢慢去退化原有的系统,来保证整个架构是演进的过程,而且是安全演进的过程,不是我们说的一次性替换。
今天的内容就是这些,再回顾一下,这五章里我们讲的核心是如何去构建工作法的第一步:正向流动原则,里面涉及的内容包括,第一,我要去构建流水线,第二,我要去构建可靠的自动化测试,有了这个测试机制同时我们还要注意持续集成的一些实践,比如像安灯拉绳。另外,在过程中我们可以采用像蓝绿部署、金丝雀发布这种机制,来帮助我们低风险发布。最后一点,在正向流动原则过程中,除了我们的流水线、架构、测试机制以外,我们的架构也要随着演进过程中去逐渐解耦、逐渐细化,也谈到了微服务架构和以前单体应用的区别。基本上这些内容就是第三部分流动原则的重要点,它并不是这个书里所有的细节,我把它抽象成一些重要的点,接下来如果你再去看书就会有一些印象,对于一些主要的案例、实践都会有一些印象。