持续交付概述Word格式文档下载.docx
《持续交付概述Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《持续交付概述Word格式文档下载.docx(15页珍藏版)》请在冰豆网上搜索。
因此,部署可以很频繁,然而实际交付给用户使用则可能根据计划,比部署的频率低。
当然持续部署并非没有投入和成本,产品的基础和特点不同,获得这种状态所需要的投入就越大。
对于缺乏自动化测试覆盖的遗留系统,以及对安全性要求特别
高的产品,它们要实现持续部署(甚至频繁部署)都会需要巨大的投入。
但是如果产品所处的市场环境要求它必须及时相应变化,不断改进创新服务的话,这种持续部署的能力,就成了值得投入的目标。
持续部署,依赖于整个团队对所写代码的信心,这种放心,不仅是开发这段代码
的人对自己写的代码的自信,也不是少数人的主观感觉,必须是团队或者组织的
所有成员都抱有的基于客观事实的信心。
因此,如何能够让任何新的修改都能够
迅速地、有信心地被部署到生产环境,就成了一个值得解决的问题。
后面我们仔细讨论,自动化测试是建立这种信心的根本保证。
在团队具备信心的基础上,要实现产品的持续部署,还需要有自动化构建流水线
(buildpipeline)。
以自动化生产线作比,自动化测试只是其中一道质量保证工
序,而要将产品从原料(需求)转变为最终交付给客户的产品,自动化的生产线是中枢一般的存在。
特别对于软件产品,多个产品往往要集成在一起才能为客户提供服务。
多个产品的自动化构建流水线的设计也就成了一个很重要的问题。
产品在从需求到部署的过程中,会经历若干种不同的环境,例如QA环境、各种自
动化测试运行环境、生产环境等。
这些环境的搭建、配置、管理,产品在不同环
境中的具体部署,都需要完善的工具支持。
缺乏这些工具,生产流水线就不可能
做到完全自动化和高效。
因此,持续部署靠自动化测试建立信心,以构建流水线贯穿始终,靠各种工具实现高效自动化和保持低成本。
在下面我将详细谈论一下这几部分的内容。
2自动化测试如何能够保证我们写出来的代码既能准确实现我们的新功能,又能够不破坏既有的功能?
唯有靠完善的测试。
而当我们开始追求频繁地甚至是持续地部署的时候,自动化测试是唯一的能够让我们持续反复地验证软件的方法。
如果一个产品具有完备的自动化测试用例,那么任何一次对软件的修改都能够得到自动化地回归验证,如果验证通过,我们就具备了将这些修改部署到生产环境中的信心。
自动化测试的质量直接决定了我们能否具有持续部署的信心。
关于自动化测试,有很多著作详细地讲述了自动化测试的设计、实现技巧等,这里不再尝试重复这些内容。
我想以一个web应用为例,举例分析一下到底需要些什
么样的测试才能够让我们建立对这个产品的信心。
作为例子的web应用
这个简化的例子里包含了三个软件开发团队(A、B、C),这
几个团队各自有自己
的一些产品,最终他们的产品组合起来给用户提供完整的体验。
A团队的产品包含
了3个主要模块。
其中fetcher集成了B和C的产品,定时从其中获取数据,经过分
析后存入store;
frontend则会从store中获取数据,以web界面与用户进行交互。
frontend同时还和其他一些组织外的第三方服务集成(例如
twitter,weibo等)。
这里不妨再对A的产品所采用的技术进行进一步限定,以方便我们之后的有些讨论。
我们不妨假定frontend和fetcher都基于java平台,都是标准的j2ee应用,store
采用mysqlofrontend和fetcher都通过webapi(可能是RESTfulwebapi)于第
三方应用集成。
在生产环境中,frontend和fetcher都会部署在tomcat+apache服务器上。
我们现在可以以这个web应用为对象,考察一下如果我们在fetcher或者frontend
里修改了代码,诸如添加了新的功能,或者修复了bug,我
们怎么样能使这些修改有信心地从开发人员的机器流入到产品环境中去。
虽然我们以这个简化的web产品作为讨论对象,但是我们接下来讨论的大部分内容并不局限于web应用。
简单地讲,A产品的测试大概包含两个方面:
功能方面的测试。
包含A产品自身功能的测试,A产品和B、C产品以及twitter等外部应用的集成测试。
部署测试。
将A产品顺利部署到各种环境中,需要大量的部署脚本和产品包等的支持。
部署测试验证这些脚本和产品包的正确性。
性能测试。
在功能正确实现的基础上,产品的性能也必须满足预期。
2.1功能测试
根据分层自动化测试的理念,功能方面的测试又可以分成如下几层。
最上层的是A和B、C以及外部应用的集成功能测试3。
测试模拟真实用户和产品的交互,和真实
的B、C以及外部应用通信,验证A系统功能的正确和完备。
这种测试通常需要完整
部署A以及相关的所有应用(如B、C),在真实的网络环境下执行。
集成测试涉及的应用多,测试基础环境准备复杂,运行时间也通常较长,是最为昂贵的测试。
在A产品和其他产品接口确定的情况下,如果我们将A的外
部依赖应用全部打桩
(stub),只关注于A产品自身的功能实现情况,这种测试我
们估且称之为A的功能
测试(有时候也叫A的验收测试)。
以A为例,这意味着我们会将fetcher、db还有
frontend都部署起来,将所有外部应用如B、C、weibo都进
行打桩。
大家可能会觉
得其上层的集成测试已经可以测到A的功能了,何必搞这么复杂又引入打桩?
但是这一层测试相比于上层的集成测试有这么几个好处:
成本更低。
单纯部署A比部署整个产品族的成本更低;
而且因为A的外部依赖都
是stub,因此执行速度也会更快;
而且因为打桩了外部依赖,
不再需要考虑其
他产品的测试数据(fixture)准备,功能测试的测试数据准备的
工作量相对也会减少。
大家可能认为打桩本身是个很高的成本,但是实际上有很多工具和库以
及让打桩变的很容易,例如对于webapi,采用嵌入式web
服务器可以很容易实
现这些api的模拟,这部分的成本很低
更稳定。
一般来讲,牵涉的应用越多,测试越不稳定。
在B、
C等都是真实应用的情况下,任何应用中的问题都可能导致测试失败,甚至网络、部署上的问题也可能导致测试失败。
因此A的功能测试相对来讲更加稳定。
覆盖率高。
因为成本更低,因此可以以同样成本编写和维护更多测试。
以web应用为例,目前有很多功能测试工具可以针对各种web交互进行测试。
而且在外部
依赖打桩的情况,可以简单操纵stub模拟外部依赖接口的各种特殊情况,达到
对A在各种接口异常情况下功能的测试覆盖。
测试组织更良好。
假设A、B、C都能够以这种方式对自身的功能进行完整验证,
那么A、B、C组成的整个系统的集成验证就可以只验证他们之间接口假设的正确
性,因此集成测试就可以只依靠贯穿3个产品功能的少量的测试,就可以保证
整个产品族的功能正确。
A产品的功能测试通常需要将A产品部署后才能进行。
例如fetcher和frontend需要部署到tomcat里,store需要准备好mysql,还要将各自的配置文件写好,然后运行测试。
非但如此,为了确信这个产品部署到生产环境能运行地和跑功能测试时一样,我们还要确保功能测试运行的环境和实际生产环境尽量保持一致,例如运行在同样的操作系统上,同样版本的tomcat、mysql服务器等。
这种分层测试的思想在整个自动化测试的设计和组织上都有体现。
下层的测试相对于上层的测试,覆盖的范围更小,但是对功能的覆盖更全面。
按照这个思路,
A产品的功能测试下,又可能有fetcher和frontend两个组件自己的功能测试,而
在fetcher内部,又可能有各层各模块的测试,再有针对每个类、函数或者方法的
单元测试。
最外围的功能测试将A产品当作一个黑盒,这样的测试是浅层次的,不能完全覆盖所有场景,而且通常编写和维护成本高,运行时间长,并且受环境因素影响大4。
如果一个产品的测试多数是这样的,很容易形成头重脚轻的冰激凌型结构5。
如果我们注意丰富底层的单元测试和小模块的功能测试,那么上层只需要较少的
测试就可以达到较高的覆盖率,所有这些测试,以一个金字塔的形式,组合在一
起确保A以及整个产品族的功能正确和完备,这样的测试组合稳定性和覆盖率高,而且开发成本较前一种冰激凌型低。
如果这些测试都能够通过,那么团队就有信心将自己的代码修改部署到产品环境中去,这些自动化测试,就构成了产品的验
证和功能防护网。
这里不得不提一下测试驱动开发(TDD)。
前面提到了这么多的测试,如果系统在设计上对测试不友好,以致很难甚至无法写自动化测试,那么自然无法谈用自动化测试来保障功能。
如果尝试先写功能后补测试,甚至希望另一个团队来写自动化测试,实践证明,想拥有完善、组织良好的测试用例也只是一个美好的愿望。
测试驱动开发不仅能够很大程度上驱动出对测试友好的软件设计,也从一开始就保障了高测试覆盖率,以及组织良好、干净的测试代码。
2.2部署测试
将产品部署到生产环境中与只是在开发环境下测试有很大的区别。
我们都知道把
A产品部署到一群tomcat服务器上和把A在jetty或者IDE中跑起来有很大区别,仅仅保证后一点完全不能让我们有信心我们的产品能够在生产环境下成功部署运行。
为了能够让我们的产品能够自动化地部署到生产环境中去,就需要有自动化的部署工具和脚本。
配置同样是部署过程中的一个重要环节。
数据库等各种服务器的地址、账号密码,
所有第三方依赖的地址(endpoint)、key文件等。
这些配置可能会在不同的环境
下有些许的变化。
不同的网络环境例如DNS、防火墙也可能对产品的正确运行产生影响。
确保这些配置在对应环境下能够正常工作是持续部署的关键。
统一环境而不是维护多种配置能够让产品在不同环境下的配置一致从而简化了部署脚本,但是仍然需要测试这些环境确实能够和统一的配置良好地工作。
如果我们用这些部署脚本将产品部署到环境中然后运行自动化测试,那么这些自动化测试实际上能够帮我们间接验证部署脚本的正确性。
然而这也可能会导致产
品的功能bug和部署脚本的问题的反馈夹杂在一起,让识别问题更加麻烦;
同时也会让部署脚本的反馈周期变得更长。
无论部署测试的方法如何,部署工具和脚本的测试与产品的功能测试一样,都是
确保产品能够持续部署的要素。
后面我们专门讨论工具的时候,会详细讨论如何对环境、部署相关的工具和脚本进行自动化测试。
2.3性能测试
性能测试也是产品交付之前的一道重要保障。
在实际交付到用户手中之前,必须
保证现有的系统能够有足够的容量支撑预期的用户量。
性能测试的设计和实现同样已经有很多资源可以参考,这里也不再尝试重复已有内容。
性能测试和功能测试的最大不同在于,很大一部分性能测试是需要运行在和实际产品环境完全相同的环境,很多时候甚至直接用生产环境作为性能测试的环境。
从准备这个环境以及自动化整个测试过程来讲,和功能测试并没有本质上的不同,而只有简单与复杂的区别,我们会在之后的工具和环境中详细讨论这些内容。
3环境(environment)环境是一个比较宽泛的概念。
这里要说的环境,特指我们的应用所部署并运行的环境。
一个环境包含了产品所涉及的从服务器(硬件或者虚拟机)、网络(DNS、
proxy、firewalletc.)到操作系统、应用软件等所有内容。
软件的开发到部署,所涉及到的环境至少有如下几种。
首先是开发环境,这里狭义地指开发者的单机开发环境。
开发环境是任何应用首
先运行的环境,任何代码都会首先在开发环境中首先得到一
些手工或者自动的验证。
自动化测试首先也会在开发环境上运行。
开发环境未必和生产环境高度相似,
例如A产品可能部署在linux平台上,而开发却用windows或者mac;
生产环境中用
的是tomcat,而开发环境中用jetty来作为j2ee容器。
然后是生产环境(production环境)。
这是最为重要的环境,配备有最高级的硬
件设备,部署着所有的应用,集成在一起为其客户提供服务。
为了保证性能和稳
定性,多半会运行loadbalance软硬件,拥有良好的安全配置。
服务器们被安置在良好的物理环境中,并被时刻监控着运行状态。
总之,这是最为复杂、重要的
环境。
在开发环境和生产环境之间有很多环境,这些环境的复杂程度介于开发和生产环境之间。
A的环境
例如QA环境。
顾名思义这是给大家进行功能测试的环境,大家未必只是QA们,而这里功能测试多半是手工。
这个环境通常和产品环境具有一定的相似度,会部署
一些真实的第三方应用。
这个QA环境有时候也会兼用作演示(showcase)环境,抑
或将演示环境独立出来。
3.1自动化测试环境
除了这个QA环境,还有一系列用于自动化测试的环境。
这些环境和自动化以及持续集成紧密关联。
比如运行A的功能测试时A所部署的环境(通常被称作staging环境)。
这个环境和
A的生产环境极度类似,因为我们希望这些功能测试好像就是在测真实部署的A产品,这样一旦测试通过,我们就可以放心地将A部署。
这种类似体现在:
相同的服务器、网络环境。
两个环境下的服务器操作系统、服务器软件首先要
完全相同,用同样的软件包安装,系统的配置也要完全相同。
可以说,
staging环境中的机器要和生产环境中的机器几乎完全一样。
网络环境也要相似。
相似度越高,因为环境不同而引起的潜在问题就越少。
如果产品部署到云
计算环境中(例如amazon、heroku等),我们很容易建立任意个配置相同的机
器。
相似的拓扑结构。
生产环境中为了提升系统性能和容量通常会采用负载均衡进
行水平扩展。
例如我们可能部署多个frontend,store也可能是一个mysql集群。
staging环境不需要这么多服务器,但是A产品部署的基本拓扑结构应该保持相
同。
相同的部署方法。
如果生产环境中会部署A的rpm包,那么staging环境中也必
须采用rpm包形式部署;
反之如果采用脚本或者chef、puppet等工具,staging环境也必须用同样的方法。
否则部署方法不同,无法保证在生产环境中部署的
结果和staging环境中一样,也就增加了出问题的风险。
A的staging环境
staging环境之所以有这个称谓,就在于它和生产环境的相似。
而这种相似,正是我们进行持续部署的信心所在。
单个产品例如A、B的staging环境,可能只包
含A产品自己的模块,而对它所依赖的B以及其他应用进行打桩,打桩的范围也可
能根据所依赖应用的特点以及成本、效率等考虑而或多或少。
运行A、B、C的集成功能测试时A、B、C所部署的环境,和上面说到的A的staging
环境很相似,不过范围更大,部署了更多的产品,因此常常也叫端到端(endto
end,e2e)测试环境。
这个环境,也是和要尽量和生产环境类似,如果说A的
staging环境模拟的是生产环境中A的那部分,e2e环境就是
模拟的整个组织的生产
环境,可以看作是更大范围的,整个组织级别的staging环境。
生产环境和staging环境及e2e测试环境的最大区别可能在于容量上。
通常生产环
境需要有能力给大量的用户提供服务,因此通常会有很多服务器,而功能测试环
境只是验证功能正确,并不需要同等数量的服务器来实现这一目的。
生产环境往往有复杂的安全规则设置,这些规则有时候会影响产品的功能(例如防火墙设置可能会影响多个应用之间的通信);
生产环境中诸
如数据服务器的密
码等信息必须保密;
生产环境中可能借助于代理才能访问互
联网资源,等等。
这
些因素,在我们设计构造staging环境的时候,都必需纳入考
最后还有持续集成(CI,continuousintegration)环境。
这是持续集成服务器用
来运行它自己(包括它的agents)以及进行产品的自动化构建的环境。
CI服务器
就相当于一个开发人员,自动地监控代码库的变化,一旦有变化就自动运行自动
化构建。
CI服务器会在这个环境中运行自动化构建的所有内容,作为持续部署的
中枢,像流水线一样贯穿整个开发、测试、部署过程。
3.2自动化环境和生产环境的相似度
不难看出,自动化测试环境和生产环境的相似度影响我们对
产品的信心。
在越接
近实际生产环境的环境中验证,我们越能够有信心将验证过
的东西直接交付给用
户;
而验证环境的相似度越低,可信度越低。
比如说我们如果只在开发环境下用
jetty和内存数据库来进行A的功能测试,我们肯定会对它是否能够在复杂的生产
环境下部署产生怀疑。
因为所有关于A的部署脚本、产品包都没有经过验证过。
然而理想和现实之间总要做出一些实际的取舍。
成本和效率都允许的条件下,如
果所有功能测试都在一个生产环境的副本下执行,那么我们
可以在交付前验证所
有的因素。
然而现实是给所有团队创建完整的生产环境用作
测试成本首先会相当
高昂,生产环境往往有很多服务器集群,这些集群通常都已经是组织的巨大投入。
并且很多时候由于技术和其他方面的原因根本无法做到。
例如如果生产环境中采
用netscaler作为负载均衡器(loadbalancer),而团队采用amazon之类的云计算
平台构建测试环境,目前技术上就很难将netscaler放到云中
去运行
从另一方面来讲,自动化环境和生产环境的高仿真度所来的好处呈边际效应递减。
如果说staging环境相对于开发环境让我们能够有机会测试所有的部署脚本,并且能够测试产品在一个简化的生产环境中的实际运行情况,从而给了我们更多的信
心,那么在staging环境中加入负载均衡器并且多用几台服务器给我带来的好处就
远没有那么大了。
我们还可以考虑一下另一个类似的问题:
staging环境是部署真的第三方依赖应用,还是应该将它们无一例外全部打桩呢?
如果打桩的话,我们也许丧失了一些真实的反馈,漏掉了少数的测试用例,但是带来的好处却是测试稳定程度、执行速度
以及对A产品自身测试覆盖率的提升。
另外,是否需要在测试环境中实现某些生产环境中的要素也取决于我们想测试的
点究竟是什么。
如果我们希望测试环境的安全性,或者我们希望测试负载均衡器
的某些设置,那么我们可能就需要包含这些设备的环境来测试它们。
修改的频度也是其中一个考虑因素,如果防火墙、负载均衡器、缓存等的设置经
常处于变动状态,那么可能在staging环境中复制这些内容就会有较大的价值。
否则如果需要花很大的代价去频繁测试几乎不变动的内容,其价值相对来讲就会
很小。
速度、成本、稳定等,都是我们在现实项目中可能考虑的因素,并非环境越和实
际生产环境相似,效果就越好。
在团队达成共识的前提下,选择当前合适自己情
况的方案,是比较实际的做法。
如果有少数的情况可能没有
被测试覆盖到,也可
以持续改进它。
3.3自动化构建过程的优化
很多时候,如果我们必须在A的staging环境下开发和调试功能测试的话,在日常开发过程,尤其是TDD过程中,效率往往让开发人员无法忍受。
开发阶段的反馈周期往往必须保持在数秒的级别,超过10分钟就让人无法忍受。
例如junit单元测试,每个函数的编写过程中可能都要修改和运行n次,超过几秒就让人无法接受。
而功能测试虽然天生就更复杂些,但是如果整套测试如果需要超过10分钟甚至更久,作为开发人员就不太会频繁地运行这部分测试。
在这样的背景下,就产生了很多优化手段,它们的目的都是为了缩短自动化测试以及整个自动化构建过程的运行时间。
目前已经有很多优化手段6
。
例如,不再将A的各个组件部署到staging环境中,而是部署到开发环境中,采
用轻量级容器如jetty来代替tomcat,采用内存数据库代替mysql等。
也可以采用
诸如htmlunit的框架代替selenium来编写web功能测试。
这些手段的最终目