摘要:软件是一门艺术,交付是一门学问,持续交付(Continuous Delivery,CD)则是一种对卓越的追求。CD是一种软件工程方法,项目团队通过这种方法保持在较短周期内生产出有价值的产品,并且能够保证在任何时间发布可靠的软件。CD正在业内获得越来越多的关注与支持,CD的提倡者认为它能够让公司各个组织变得更加迅速和高效,并且可以快速可靠地为市场带来服务层的使用改善。但真正的去实现CD可能会是一个很有挑战性的过程,本文就一个上传FTP前端静态资源的Node Package(ne-delivery)为实例,以个人对CD的理解摸索实践Node Package以及前端应用持续交付流程,并且我们讨论的重点是后者。
基于对以上的认识,我以开发一个私有Node Package(ne-delivery)为例,实践Node Package的CD流程。下文就如何实现进行展开:
让CD在前端应用中得以实现,不止这一种的方法,场景不同方案不同,但对于该项目已经足够,并且也覆盖到了CD中提倡的主要实现环节。
引言:通常团队通过版本控制工具进行开发协作,所有的代码会在持续集成环境中进行构建、自动化测试、质量反馈等。持续交付则更先进一步,它将环境准备,持续集成,自动化部署等融为了一体。通过尽可能的全自动方式,使得软件可以一键发布。如果在上线后发现严重的缺陷,还具备迅速版本回滚的机制,由于整个发布流程已经经过了千锤百炼,所以软件发布本身就变得非常轻松和安全了。
1 ne-delivery的设计
1.1 需求分析
1.1.1 关于前端工程化
谈起前端工程化,首先需要谈下软件工程,wiki给的解释是软件工程是一门研究和应用如何以系统性的、规范化的、可定量的过程化方法去开发和维护软件,以及如何把经过时间考验而证明正确的管理技术和当前能够得到的最好的技术方法结合起来的学科。谈起软件工程化,通常有以下几个特点:
从以上的特点上可以看出,前端也属于软件工程的范畴,并且是非常肯定的。
在早年间前端甚至没有像Eclipse等为特定的开发语言量身打造的IDE。当时之所以有此现状,是因为针对前端项目的开发和调试即改即刷新已经足够方便,只需要在完成一定的代码修改后启动浏览器刷新看效果即可,所以当时的前端很少会扯到工程化这个概念。在很久的时间内,前端简单的不能再简单了,如一个可运行的前端应用只需要下面的几行代码即可:
图1.1 一个简单的前端应用
图1.2 webpack的模块打包原理
1.1.2 功能设计
ne-delivery旨在打通前端本地静态资源获得CDN地址这堵墙,这里借助运维搭建的FTP,核心功能也就是FTP上传。
1.2 关键设计
该包需要根据CommonJS规范+es6/7语法开发,提供CLI和Node.js API两种调用方式,主要分为建立FTP连接模块、FTP切换模块、文件过滤模块、CLI调用与提示模块、Node.js调用模块。通过两种方式调用时,根据资源后缀动态切换FTP,并对不可上传的文件进行过滤。有ne-delivery介入的资源发布序列图为:
图1.3 有ne-delivery介入的资源发布序列图
两种调用方式需要的参数:
对于上传成功的文件需要返回文件列表。
2 ne-delivery实现
2.1 创建项目仓库
关于代码提交,CD提倡在每次commit时,团队的开发者可以在流程中获得针对此次提交最初始的反馈。当有团队开发者往项目仓库中提交新变化时,立即会自动触发后续的管道。如果某个阶段出现错误,整个管道便会立即自动中止本次发布并及时的通知到开发者。团队根据随即修改的代码,对这次失败进行结对审查,然后提交新的代码。代码提交后,便会再次自动触发上述的流程。倘若本次从始至终的管道流程正常,会自动进入下一阶段,在一定程度上也增加了团队对发布流水线的信心。
图2.1 CD基础管道流程
创建并进入ne-delivery文件夹,依然使用npm进行包与依赖的管理,执行npm init初始package.json,并以此创建lib、bin、__tests__文件夹及README等一些配置,并在主干上直接开发。(详细代码不再赘述,贴代码并不是写本文的重点)
2.2 自动化测试
以往很少有在实践自动化测试,代码层面模拟各种分支情况便完成了。但随着接口逻辑复杂度的提升,以往的测试方式总会遗漏一些缺陷,也会使交付周期变长。对于有测试用例的项目,虽然不能保证百分百的无缺陷,但至少测试覆盖到的地方是没有问题的。自动化测试的另个重要的特点便是可以使团队快速的得到反馈,反馈的速度意味着开发效率的高低。自动化测试也要基于投入与产出比来做,通常来说写少量的测试用例,覆盖到百分之八十及以上的场景即可了,之后随着迭代,也应该有测试用例的沉淀,并且尽可能的要提高覆盖率。自动化测试主要包括测试框架、断言库、代码测试覆盖率工具以及覆盖率报告,主要分test-suite(unit-tests)和e2e-tests两个流程。
关于测试框架的选择目前比较热的有Mocha、Jasmine、Jest等,不同的测试框架支持的测试风格也不同,如基于对测试驱动开发(TDD)和行为驱动开发(BDD)支持的比较,Mocha和Jest两者都支持,而Jasmine只支持后者。
代码覆盖率在Node.js里首选istanbul,其原理就是为代码在语法层级上为分支打点,当运行了打点后的代码,它会根据运行结束后收集到的信息和打点是的信息进行对比来统计出当前的测试用例的对被测代码的的覆盖情况,同时产出覆盖率报告。
以Jest为例,执行jest --coverage就可以在coverage目录下生成测试报告,因为Jest已经集成了istanbul,所以可以直接使用--coverage参数,jest会自动的递归__test__目录下的用例,并返回最终的结果。关于测试报告可视化之后会在持续集成部分提到。
2.3 项目构建
构建产出将要发布的目录或文件,然后将需要发布内容传到管理的环境中,以供后续的部署或分发,之后的每个阶段在其运行中都会触碰到这次构建出的内容。构建阶段必须再次执行测试用例,随后便开始集成测试以及必要的静态代码分析,在以往的流程中,发布到线上环境的中的文件可能会与测试通过的的文件并不是同一份,因为在每个阶段中都有可能进行一次自我的构建,那么差异也就在此时产生了,修复这种缺陷是很有难度系数的。因为在团队内部能正常运行的软件,发布到线上却不能运行。而CD流程管道便可以消除这种缺陷,当某处发生了任何错误,管道便会立即中止并及时反馈到团队。
2.4 持续集成
持续交付的基础就是持续集成(Continuous Integration,CI)了,CI提倡团队内部每天都可以进行多次触发去集成他们的更新。并且每次的集成都是通过自动化的构建结构来验证,包括自动测试、自动编译、自动分发等,从而使团队尽快地发现并处理集成时的错误。
以Jenkins为例,需安装email-ext(邮件通知)、cobertura、htmlpublisher(测试覆盖率报告可视化)、embeddable-build-status(集成状态徽章)四个插件,构建一个自由风格的软件项目(Pipeline也可以)。配置项目如下:
图2.2 选择一个Node.js版本
图2.3 构建过程
图2.4 代码覆盖率报告及度量指标的阈值
以及其他的构建自定义,比如构建hook、Email Notification等,手动触发一次集成,整个过程会生成日志(某次集成log.txt),并且相应的报告也可视化。
图2.5 Cobertura Report
图2.6 JS Coverage HTML Report
图2.7 集成反馈邮件
2.5 手工验收测试
虽然在进行着自动化测试的完善,但在特定的场景下手工测试还是必须的。以往,测试人员需要自己搭建一个人工测试的环境,这让测试人员感觉十分痛苦,因为其中手动部分包含着太多容易出错的步骤。CD提倡管道应该自动搭建测试使用的基础环境,并通知测试人员可以访问已部署的应用相关信息。当测试人员确认质量通过后,便可将内容从待发布候选状态推进为发布候选状态。此时,软件已经做好了充足的准备,等待发布到流水线上。
2.6 自动部署
部署有时会因为脚本的失败或部署环境欠佳,部署过程花费了超过开发者一倍的时间。CD提倡尽可能自动的环境搭建,涉及到部署的配置以及和他相关的脚本,已经在真实的管道过了多次验证。正因此,从本地环境部署到线上环境流程中的绝大多数错误都已经被捕获到了。
私有Node包以发布到远程Sinopia为目标,为了快速产出Sinopia环境,这里选择Sinopia的一个docker镜像(keyvanfatehi/sinopia),接着添加用户并将自己的.npmrc复制到Jenkins的目录,此时构建步骤的npm publish生效。再次通过一个commit触发整个管道,所有阶段通过后会自动部署。
图2.8 ne-delivery发布至Sinopia
2.7 整合
经过以上的活动,只需要本地进行一个可靠的提交,便会触发一次CD,Jest进行自动化的测试并产出报告,Babel编译项目,npm部署最终产物至Sinopia,Jenkins会根据配置执行环境重建以及报告收集和集成状态反馈。并且所有的管道都应尽可能的是自动的可视的。
3 总结
3.1 实践成果
将CD实践于日常开发与维护的前端应用,使整个发布流程变得更加的可靠,降低了发布的风险。同时伴随着发布频率的提高,每次发布中所涉及到的代码更新量也在减少。因此使团队定位问题也变得没有以往的那么困难,从而节约了时间人力等成本。从识别原始的需求再到在生产环境中的产品部署,需求在以最小批量的形式顺畅流动在团队中的各个角落,能够在较短的周期内完成需求频繁的小粒度交付提供真实的用户来使用,在为用户带来价值的同时,也可以快速的得到反馈验证新发布中涉及的商业价值,并激励软件新的变化产生。持续的发现并对开发流程中的缺陷进行改进,从而促进了开发人员,测试人员,运维人员以及运营人员之间的协作,团队能够在更短的时间将有价值的变更发布到上线。
3.2 流程难处
一个交付流程牵连到公司内部的多个组织,每个组织都有已有自己的工作方式和利益。要想引入CD,以最大程度的帮助组织平稳的过渡到CD流程范围内,存在着极大的挑战。比如某一个已准备好进行发布的更新必须通过某个变更委员会的批准,这会导致它的发布时间延长,假如需求从提取到发布只需要几天的时间就能够顺利完成,那么批准需要的时间对于这次更新的整个周期来说也是是相对漫长的。再次就是针对现有的大型整体性的应用,我们需要使用很多不同的服务与技术栈作为依赖,在这样的场景下,需要在广泛被接受的标准之上,依赖于开放的私有服务进行建设,并创建一种灵活的生态系统,让更多的组织能够简单的从CD中受益,需要长时间的摸索。
参考文献:
[1] (英)亨布尔(Humble,J.),(英)法利(Farley,D.)著;乔梁译. 持续交付:发布可靠软件的系统方法. 北京:人民邮电出版社,2011.10.
[2] (美)马丁著;邓辉译,孟岩审. 敏捷软件开发:原则、模式与实践. 北京:清华大学出版社,2003.
[3] S. Neely and S. Stolt. Continuous delivery? easy! just change everything (well, maybe it is not that easy). In Agile Conference, 2013.
本文来自网易实践者社区,经作者王宁授权发布。