持续交付概念的流行反映了业务部门对于更快交付速度的渴望和技术团队对交付这一老大难问题的重视。从持续集成到持续交付,ThoughtWorks 一直在积极的实践、思考、总结。这篇文章中我将分享 ThoughtWorks 一支开发团队在这个领域的收获和总结。
背景
我们的客户是澳洲的房地产搜索门户,每年独立访问者近 300 万 (澳洲人口 2000 万),年营收近 3 亿美金,澳洲 61%的房产都在此网站展示。我们的团队工作在商业地产、住宅、土地购置等与盈利息息相关的全产品线的核心系统上。
成就
2 年前,与大多数“常规”项目一样,部署和发布是一件费时费力、加班加点的活动。作为一家历史超过 10 年的互联网公司,已经有大量复杂的业务系统支持着线上业务的正常运转,部署和发布任何一个功能都是牵一发而动全身的复杂活动。对我们的客户来说快起来不是一件容易的事情,但它不得不快,因为竞争对手在频繁的进行商业活动,不快速的求变、求新它将难以保持领先的市场地位。持续交付以及持续交付所支撑的持续创新能力是它必须建设的核心竞争力。
对于 ThoughtWorks,持续交付是增加项目进度的可见性,降低项目“最后一公里”风险的重要工具。这样,投资建设持续交付能力成了合作双方一拍即合的决定。经过了两年的摸索与实践,总接下来, 收获主要有以下四点:
一键部署:目前的部署流水线从提交开始,到自动运行各种验证,再到最终的安装、配置软件可以做到全程无须人员干预,最终的部署过程可以在几分钟内自动完成。具备了快速部署,持续部署的能力,一天多次部署,一周多次发布就成了顺利成章的事情。
由离岸团队执行部署和发布:中国团队的成员大多是开发人员,对产品环境了解不足;分布式环境让中国的团队没有太多的机会从澳洲团队身上学习如何部署和发布,这样的内忧外困下,得出“由离岸团队来执行部署和发布风险太高”这个结论似乎是很自然的,然而随着持续部署过程越来越熟练和成熟,我们发现上面的结论是与部署手段原始落后的现状息息相关的,一旦发布手段变的高明,由离岸团队部署和发布的风险是完全可以接受的。
业务人员变得更加敏捷:随着部署肌肉的越来越发达,业务人员开始习惯了这样一种对话方式,“唔,这个功能很不错,我们发布出去看看用户是什么反映吧?”,和以往仔细计划、严格执行的风格相比,业务人员的决策过程变成了:
- 尝试某功能
- 通过对响应数据的分析,理解功能的市场反应
- 决定继续开发还是改变方向,不断循环和迭代
通过功能频繁上线,业务人员的思路和做事风格也变的更加敏捷。
业务团队与技术团队理解了部署和发布软件是两个不同概念、两件独立的活动:2 年前,项目部署和发布的时间点从来都是高度统一的,部署结束发布就结束。团队一直没有意识到软件的部署和发布是不同的概念和活动。事实上软件部署是技术概念,包括了:
- 创建和管理应用所依赖的硬件软件环境
- 把正确的软件安装到上述环境中
- 正确的配置产品,使其按照预想的方式运行
软件发布则是业务概念,它包括了决定:
- 哪些业务功能应该让用户使用?
- 在什么时间让用户使用?
- 让哪些用户使用?
- 必要的市场推广,客户、支持团队培训是否就绪?
通过技术改造,大家明确了部署是一周进行多次,甚至一天进行多次的技术活动,而发布则严格与业务目标对齐,通常以几周为频率。
最佳实践
团队所取得的成就来自于对方法、工具和流程的摸索和应用,我们认为有三个实践对于实施持续交付是至关重要的:
功能开关 (Feature Toggle):持续交付不仅仅意味着使用新工具、新方法。它还意味着从设计和编码阶段就需要作出改变,功能开关是其中一项重要的设计实践,它的目的是方便的打开和关闭某个特性。为什么需要功能开关? 因为在持续交付的过程中,团队会遇到下面的情况:
- 功能没有就绪,还不能让用户使用
- 功能就绪,但是相关的业务活动(培训,市场活动等)没有就绪,不能让用户使用
- 业务人员要求功能只开放给部分用户
- 测试不充分导致的产品缺陷需要关闭功能
功能开关可以用于解决上述问题。它的具体实现多种多样,我们在不同的场景中使用过:
- 灰度上线:功能部署上线但是把用户可见的操作都屏蔽掉
- 文件配置:通过配置文件决定某个功能是否对用户可见或关闭
- 状态数据:通过数据库中的状态值决定某个功能是否对特定用户可见或关闭
- 编译参数:再编译过程中通过编译参数决定某些特性打开或者关闭
自动化 (Automation):要想频繁的部署、发布而且每次部署都不能出错,是难以离开自动化过程的,我们团队的自动化部署流水线是这样设计的:
- 本地测试通过后,开发人员提交代码。
- 触发 package: 执行静态检查,单元测试和功能测试,并生成编号的二进制包供后面的阶段进一步测试。
- 触发 acceptance_standalone:在 EC2 上搭建出单机产品环境,运行集成测试。
- 触发 acceptance_staging:在 EC2 上搭建出最小集群环境,运行另外一些集成测试。
- 触发 publish:将 package 阶段生成的二进制包发布到公司内部的软件仓库中
- 利用命令,测试人员可以自动化地取出第二步所生成的二进制包,并自动搭建出产品环境进行手工测试。
- 手工触发 deploy:相关发布包被安装在产品环境上,完成部署。
我们使用了很多软件来支持上面的自动化部署过程,比如 :
- Buildr: 用于编写自动化构建脚本
- JUnit:用于编写与运行自动化单元测试
- Cucumber:用于编写与运行自动化验收测试
- Amazon EC2: 用于创建和管理测试环境的虚拟服务器
- VMware vSphere: 用于创建和管理产品环境的虚拟服务器
- Chef:用于基础设施 (操作系统,应用服务器) 的搭建和配置
- Go: 用于设计和实现部署流水线
虚拟化:虚拟化提供了充分测试所需的平台。能够高度自动化的测试和发布并不意味着高质量的部署和发布。在测试过程中能不能发现问题是非常关键的。不难理解,测试环境和产品环境越相近越容易发现问题,但产品环境通常复杂而昂贵,传统方式根本无法有效的向每个开发人员提供一套环境用于测试,而虚拟化则可以做到这一点。
我们目前在 EC2 上搭建出了 300 多套环境供 300 多澳洲和中国开发人对软件进行充分的测试,它的优势有:
- 容易复制,并且可以根据需求弹性的增加。
- 容易同步,产品环境的变化可以通过脚本自动的同步到测试环境,减少差异。
- 容易自动化,有大量的开源软件支持
教训
当然在实施持续交付的过程中,我们也犯过很多错误、走了一些弯路,比如:
没有设计防呆机制,开过自动档汽车的人都知道,不踩下刹车无法挂前进档,这就是一种防呆机制。一个高度自动化的部署过程也需要设计防呆机制,防止出现重大错误。我们曾经因为错误的修改了脚本,高度自动化的流水线很快将测试数据刷入了产品环境,结果巴掌大一块空白出现在百万级访问量的产品首页正中间,没有防呆机制的高度自动化环境很带给团队的可能是更快的部署过程,也可能是更多的线上严重错误。
没有精确复制产品环境:我们的产品曾经通过部署流水线的验证并成功上线,然后很快客户发现产品的背景和布局出现了明显的错误。紧急排查后发现错误来自于一段排序算法,这个算法在测试环境使用的 ree-1.8.7 中没有问题,但在产品环境的 ruby-1.8.7 会出现异常。它教会我们不能想当然,产品环境和测试环境的任何一个差异点都可能是潜在的问题点。
脆弱的部署流水线带来的高维护成本:环境越接近真实,测试就越复杂脆弱,相应的维护成本也越高。自动化程度最高的团队目前有 50% - 70% 的构建失败是由环境问题引起的,比如:搜索引擎不稳定,会出现清理和更新数据失败的问题、从第三方获取的广告不稳定导致导致超时失败等。这些失败带来的直接成本是修复构建的时间,间接成本是团队需要花时间设计更高明、更稳定测试的精力和时间。这些成本是任何一个需要实施持续交付的团队都得做好心理准备的。
未完成的实践
我们目前还无法基于真实数据设计整个测试、迁移、部署、发布的过程,使用真实数据对于稳定、高质量的验证、部署过程至关重要,但有些棘手的问题让我们无法采用真实数据,比如:
- 数据库很“大”:10 多年积累下来的数据以 T 记,要想测试必须对数据进行裁剪,如何有效裁剪?因为裁剪不当,我们多次发现因为数据引发的线上问题。
- 数据库很“敏感”:相对于产品环境,产品数据更加敏感,它往往是一个公司的核心竞争力,各种安全策略保证无法每人都有可以使用这些数据。
- 数据变化难以“自动化”:开发团队和数据库团队使用不同的工具,对于变化的态度也非常不同,结果是开发团队成熟运用的工具和实践并不能使用到产品环境的数据迁移中,整个审查、验证、应用变化的周期依然漫长,拖慢了产品发布的速度和节奏。
对于其它方面,我们已经有了一套成熟的理论和实践,但对于数据库层面我们还在摸索,也希望看到和听到这方面更多的意见和建议。
感谢张凯峰对本文的审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。
评论