文章: 自动化构建:一致性关键之道
介绍
如果有那么一件事软件开发人员很在行(并非引述电影《黑客》),那一定是将通常需要人工完成的任务自动化。让计算机处理重复乏味的任务将使得大家生活得更轻松,这里我们讨论的是如何让大家专注于他们所关心的事情。然而,研发团队时常会忽略那个最有帮助的受众—他们自己。
相关厂商内容
深度剖析WebKit渲染机制:Chromium项目Committer确认QCon北京2013
QCon北京自动化运维专题:腾讯海量SNS社区网站高效运维探索
QCon北京会前培训:JavaEE开发企业应用,充分领悟JavaEE之内涵
国内最大的Python应用——Sohu邮箱之经验分享,尽在QCon北京企业开发专题
QCon北京持续集成与持续交付:百度复杂系统下的持续部署实践
相关赞助商
QCon全球软件开发大会2013,北京国际会议中心,4月25~27日,4月14日前9折优惠报名, 详情请点击!
在为数众多的中小型软件作坊中,不存在自动化构建和发布工具。构建、交付准备环境、代码发布全由手工完成,同样还有运行测试、备份旧版本、新版本打标签以及许多其他重复的事情。毕竟你可能认为这全是非常简单的工作,集成开发环境通过按钮或快捷键就可构建项目,你开启两个窗口拖放少许文件或文件夹即可完成网站发布。但当你在维护代码库和应用时所有这些事情加在一起,这里几分钟,那里几分钟,最终会浪费几个小时。
庆幸的是,很容易解决这个问题。基本的自动化构建方案易部署,可高度定制化,成本低廉。本文描述了组建自动化流程的一些动机,以及你将需要接触的一些概念。本系列的第二部分将描述针对.NET解决方案的具体实现,但这些技术在任何环境下都适用。
我们试图解决什么问题?
在我们深入了解之前,我们先看下一些我们试图解决的问题。并非所有这些问题在你的组织中都存在,但是如果你仔细观察你们团队,你将发现可能存在其中的一些问题,很可能你的构建流程中其余部分可能会耗费一些工作。
不一致的构建:用集成开发环境编写代码是件美妙的事情。然而这是有代价,诸如框架/运行时版本、输出结构、编译/调试版本、配置设置、环境变量以及编译选项等通常是由集成开发环境或操作系统处理。看似好事,但除非大家使用完全一样的开发机器,如果开发人员不注意这些细节,同一个代码库,不同开发人员会得到不同的输出。
不完整的构建:我们中的大部分人都曾经都对糟糕的源码控制实践感到内疚。我们可能忘了提交一个缺陷修复,或是编译前忘了更新最新代码以获取其他人的改动。当这些发生在人工构建环境下时,即将发布的代码便不是最新的。我们需要一个解决方案,它保证你发布的总是源码控制系统中存在的。
失败的单元测试:单元测试是任何优秀应用程序不可或缺的一部分,如果使用得当,它有助于避免在修复bug的同时引入新bug。然而,光写测试远远不够。你必须定期批量执行所有测试,当有些事需要你自己亲自处理时很容易会被遗忘。实际上没人执行这些测试,所以可能有些单元测试没通过而你却从来不知道。
人为错误:即使是精心策划的简单流程也容易出现人为错误。我们都有过手指在键盘上徘徊却意外敲错按键,或者意外删除了操作系统内核(这比你想象地要经常发生)。或是在凌晨1点钟,昏昏欲睡时,我们意外地把正式服务器当作测试机开启。这是不可避免的,因为人无完人,所以任何事情一旦需要人工交互都有可能出错。
安全:服务器和网络的安全总是由单独的软件团队处理。通常有两个极端,要么服务器连接有限制,达到这个限制任何人都访问不了,培养新员工的繁文缛节需要一个月之久;要么服务器对所有人开放,那么你们团队任何一个成员的恶意点击都可能搞垮系统。身为一名开发人员,我通常偏向于第二种方案,因为实际上我可以把事情做好,但我看到了这种做事方式的危险性。无论你的组织在该领域处于什么位置,自动化只会改进你的流程。
我们首先需要做什么?
一旦你确定了团队的主要痛点,你可以针对需求设计解决方案。没有放之四海而皆准的方案。只要你有自动构建,它能就减少人为操作并使事情趋于一致,你前进的方向就是对的。
然而,你真正还需要很多东西。
你必须开始接触优秀的源码控制实践。不管你是使用SVN、Mercurial、GIT或是TFS(请不要使用SourceSafe),你需要定义诸如分支策略,如何处理第三方以及内部库,如何在仓库中组织项目。当然了,你的团队已经启航。当有人在小项目里做事不遵守规范,他们能搞砸整个流程。
必须有一名开发人员担任自动构建工程师。这个人将负责编写构建脚本,搭建持续集成开发环境,很可能还负责源码控制系统的搭建和部署。除非你有个很大而且复杂的环境,否则这些工作应该只占用他一小部分的时间,所以他一周中大部分时间还能从事常规的开发工作。
即使你希望永远不要用到,流程中还是应该有个应急计划,就像组织中其他的任务关键型程序。编译服务器很可能成为一个单点故障,它很可能不具备负载均衡的能力,而且也不会有热备份以防服务器宕机。像这种情况,你想要确保能够快速搭建新的服务器,完成配置和权限的设定,还应该有个不使用构建服务器的B计划。尽管该实现的目标是再也不要由人工完成任何事情,却总应保证它是可行的。
构建脚本
构建流程的自动化依赖于简单的重复性任务。第一步是编写构建脚本。构建脚本可以是任何形式:批处理文件/shell脚本、基于xml的任务集合、自己写的可配置程序、或是他们中的任意组合。在.NET世界中,MSBuild是由微软提供的命令行功能,它使用基于xml的项目文件构建Visual Studio解决方案。NAnt是另一个常见.NET构建脚本工具,类似于流行的Java工具Ant。其他的包含开源社区中常见的Make,Ruby中的Rake等。
无论你选择如何编写构建脚本,你应该寻找适合你的方法并坚持下去。例如,你一旦找到构建web程序项目的最佳方式,为新的web应用程序创建构建脚本应该就很简单了,只要从其他项目中拷贝脚本、修改部分名称和路径即可。
操作系统和编程框架之间的实现显然有很大的区别,但理念基本一致。脚本应该完成你在构建/编译/交付准备环境时通常要做的所有事情。通常,这意味着编译代码,使用特定的编译选项,将输出文件和原始代码库分开保存,做部署准备。即便在无须编译的项目中,例如静态网站,项目中可能有不可动态更新的内容,例如测试页面或调试脚本,你想在源码控制系统中统一管理这些内容,但是在你确实想要部署时,构建脚本将文件交付准备环境时不会发布这些内容,所以你无须每一次都要考虑这些事情。
除了基本的构建/编译/交付准备环境等步骤,你其实可以做任何想做的。想压缩JavaScript文件?为它创建一个任务。编译之前代码中需要唯一时间戳?创建一个任务。为了不让网站落入对手囊中,需要分型数据加密算法?添加一个任务(请个人帮忙)。在构建流程中,你能在命令行做的都能在代码编译前后执行。
大多数软件项目都不止一个模块。例如,你可能有个web应用程序,但你还有个独立的数据库,它是该整体解决方案的一部分。对于这种场景,单个控制脚本是解决的方法。该脚本是控制器,每次调用一个单独脚本。在每个Visual Studio项目、Java包、抑或是自定义的代码结构中都有脚本。每个单独的脚本都有该项目特有的任务,而控制脚本包含了所有共享功能。
尽可能使脚本通用并且可重用。使用相对路径,不要使用绝对路径,具体项目的信息定义在一处,可重用的信息定义在控制脚本中。这么做便于维护,也有助于今后构建新项目。
持续集成
一旦编写了脚本,项目就可以通过一个命令完成编译和交付准备环境。这是个良好的开始,但还需要有人执行命令。我们的目标是去除人工介入,所以持续集成将为我们考虑这些。
至于构建脚本,供选择的有许多种不同的技术,项目的组织方式也多种多样。但再次强调,你将想要找到适合于你的解决方案,并坚持下去,以便在项目中保持一致性。
一些流行的选择是TeamCity、Jenkins、CruiseControl(或CruiseControl.NET),如果你喜欢多用途的应用程序, Microsoft Team Foundation Server除了源码控制和构建之外还能够完成持续集成。每个产品都有各自的目标受众,但是他们全都是设计用来监视源码并自动运行构建脚本,所以不需要手工完成这些。持续集成服务器的传统策略是监视源码控制仓库的变动,当有变动时自动下载最新代码,然后执行脚本,它依次编译应用程序并为发布做准备。然而,在构建脚本中,你能够添加任何你需要的任务或行为。
你可以设置单元测试或任何其他你编写的自动化测试作为该流程的一部分执行。一旦有人提交代码,测试将立即运行。至于测试不通过,更有甚者编译不通过,你会被告知出错了,所以这些问题能够被快速处理。就本人而言,我建议用飞镖射击责任人(戴高帽是另一个不错的选择),但是如何实现当然是由你决定。
发布
到目前为止所有的一切都是为了这一刻。此刻我们只是提交了代码,自动编译了,运行了测试,交付到准备环境和做好了发布的准备。最后一步是将交付准备环境的代码发布到任何需要的地方。
发布自己托管的web应用程序通常是将文件从准备环境拷贝到web服务器。这可能意味着人工拷贝文件,或整理到一个简单的批处理/shell脚本中。因为我们仍在努力使生活变得简单并且自动化,你将寻求更佳的解决方案。取决于你们单位的安全策略,你也许能够将持续集成流程中的任务组织在一起,以便通过文件系统或FTP拷贝文件。至于一个纯HTTP的解决方案,你可以看看DubDubDeploy等产品,它不受域安全或文件系统访问的限制,能够服务器之间拷贝文件。
如果你有个包装产品,最后一步只要简单地将产品打包。构建脚本可能已经处理了创建安装包,组织文档,以及其他与发布文件相关的东西。现在有了可部署的产品,剩下的就是将产品拷贝到3.5英寸软盘、打包、发布。
总结
组建自动化构建环境可能会花些时间,让它按你想要的方式工作至少需要几天时间,也可能是几个礼拜,可能还要按照你自己的意愿做调整。最后,你需要权衡后续每天将节省的时间,以及那些平时大家很容易掉入而在采用一致的过程后可以有效避免的陷阱。
当每个人都做他们最擅长事情的时候,你的组织会是最高效的。开发人员编写代码,构建工程师处理构建和部署配置,构建服务器完成所有重复性任务,它的确做得很出色。运转流畅的软件过程通常会直接提升产品质量和缩短发布周期,两者都将极大地影响着你公司的财务状况。
关于作者
Joe Enos是一位软件工程师,同时还是位企业家,在.NET软件领域拥有近十年的工作经验。他主要关注自动化和过程改进,涉及软件领域的方方面面。他在全美各类软件大会上给小型软件团队做过关于自动化构建的演讲,介绍构建脚本以及持续集成等话题。
他公司的首款软件产品 DubDubDeploy已于近期发布,是帮助改善软件团队管理构建和部署流程的系列产品中的第一款。他的团队目前致力于.NET构建流程的全自动化。
查看英文原文: Automated Builds: The Key to Consistency