写点什么

自动化构建的那些事儿:金融网站虚拟案例

2013 年 7 月 26 日

介绍

本系列的第一部分讨论了将你的构建和部署流程自动化的部分好处。促使你这样做的理由有很多:让你的开发者更关注于核心业务而不是流程管理、减少人为因素导致的错误、减少部署花费的时间以及其它很多原因。无论你的动机如何,自动化你的构建流程永远是正确的回答。

本篇文章中,我们将以一个虚拟的金融机构的企业网站作为示例,将其构建过程一步步完全自动化。

案例描述

我们的公司叫做第三国民银行(3rd National Bank),是一家本地商业机构。我们的在线银行应用包括前端 web 应用层(ASP.NET),可接入移动应用的 RESTful 服务层(WebAPI),一系列使用了传统的领域驱动设计方式来分离业务逻辑、领域对象及数据访问的内部服务层(WCF),以及一台 SQL Server 数据库。

软件团队使用 Mercurial 作为源代码控制系统,并使用特性分支的策略定期交付新特性。该策略为每个新特性或 bug 创建一个新分支,分支代码在测试完成后就会合并入主分支等待发布。

在现阶段,所有的构建和部署过程都是由软件团队手动完成的,导致开发者每周都得花费几个小时,停下手上的编码工作去维护他们的资源库和服务器。我们在尝试改变现状,尽可能多的将过程自动化。

构建脚本

构建脚本是自动化构建过程的第一步。脚本的形式可以是多种多样的:可以是 shell 或者批处理文件、基于 XML 的文件,或者由现有或定制的编程语言所写;可以手写或自动生成;也可以完全从 IDE 环境中隐藏。你的流程甚至可以组合使用多种技术。在这篇示例中,我们使用 NAnt 作为构建脚本引擎。

在我们的环境中,前端 web 应用、暴露给外部的服务以及内部服务各是一个独立的 Visual Studio 解决方案,另外 SQL 数据库也是一个独立的 database 解决方案。我们将创建一个主构建脚本文件 3rdNational.Master.build,看起来差不多是这样:

复制代码
<project name="3rd National Bank" default="build">
<target name="build">
<call target="createStagingEnvironment" />
<nant buildfile="BankDB/BankDB.build" target="build" />
<nant buildfile="ServiceLayer/ServiceLayer.build" target="build" />
<nant buildfile="OnlineBanking/WebUI.build" target="build" />
<nant buildfile="ExternalServices/ExternalServices.build" />
</target>
</project>

这段脚本没有做什么具体的事,它仅仅是调用了那四个解决方案的构建脚本。每个解决方案都有一个构建文件,其中包含了编译及准备这一部分应用程序的所有代码。

现在让我们来看一看其中一个解决方案的构建脚本。每个解决方案都基于相同的基本步骤:准备、编译及暂存。ServiceLayer.build 就是一个基本的编译脚本,它的语法也非常容易理解:

复制代码
<project name="ServiceLayer">
<property name="msbuildExe" value="c:\windows\
microsoft.net\framework\v4.0.30319\msbuild.exe" />
<target name="build">
<call target="prepare" />
<call target="compile" />
<call target="stage" />
</target>
<target name="prepare">
<!-- Implementation omitted -->
</target>
<target name="compile">
<exec program="${msbuildExe}">
<arg value="ServiceLayer.sln" />
<arg value="/p:Configuration=Release" />
<arg value="/t:rebuild" />
</exec>
</target>
<target name="stage">
<copy todir="../deploy/BankWcf">
<include name="WcfServices/**/*.svc" />
<include name="WcfServices/**/*.asax" />
<include name="WcfServices/**/*.config" />
<include name="WcfServices/**/*.dll" />
</copy>
</target>
</project>

准备步骤可以包括生成 AssemblyInfo 文件、重编译代理或者任意数量的其它任务。这个例子里的编译步骤仅仅是简单调用了 MSBuild,这是 Visual Studio 用来编译解决方案的构建引擎。当构建的每一步骤成功后,最后一步会把生成的文件复制到某个暂存区域,留待之后使用。

对于其它三个解决方案我们也采取同样的做法,只是基于不同的项目类型作些微调。

编写构建脚本其实和写代码一样,可以通过不同的做法达到同样的效果。你可以直接使用命令行编译器替代 MSBuild,也可以分别构建每个项目文件而不是整个解决方案,还可以使用 MSDeploy 来暂存或部署你的应用程序,取代过滤及复制文件的方式。说到底,这取决于你适应于哪种方式。只要你的脚本能生成一致的结果,怎样编写都可以。

持续集成

我们已经有了构建脚本,现在需要某些东西来调用它们了。我们可以在命令行下运行构建脚本,但既然我们打算将一切自动化,我们就需要一台机器在适当时运行脚本,这就是持续集成发挥作用的地方了。

我们使用 JetBrains 的 TeamCity 作为 CI 工具。它的售价模型非常合理,并且为项目创建提供了非常优秀的的用户体验。在我们的新构件服务器上安装它非常简单,一切已准备就绪了。

使用 TeamCity 的第一步是创建一个项目,包含项目名称和一系列的构建配置。让我们创建一个叫做“3rd National Bank”的项目。

我们打算创建一个模板,这样一来,我们打算加入 CI 里的代码分支都可以引用和主分支下的相同配置。我们首先设置版本控制信息,选择 Mercurial 作为源代码资源库,设置默认分支,提供认证凭证,并提供获取文件的地址。接下来是构建步骤,选择 NAnt 选项以及主构建文件。如果我们包含了单元测试项目,我们只需新建一个构建步骤以运行 NUnit、MSTest 或任何一个我们使用的测试框架。最后,我们设置一个基于版本控制的构建触发器(trigger),意味着主资源库的任何一次签入都会触发一次构建。

TeamCity 中还可以做其它很多有意义的事,比如根据构建结果定义失败条件、设置对其它构建的依赖以及定制参数。你可以继续探索各种用法,不过我们现在已经有了一个满足我们所需的基本构建了。

让我们使用这个模板再创建一个新的构建配置“Main Line”,它将处于版本控制树的顶端,包含准备发布到生产环境的稳定代码。特性分支会派生自它,因此我们可以使用这个模板按需创建更多的构建配置,每个配置负责一个特性,所要做的仅仅是为每个配置的源代码设置做一些微调而已。现在,不仅是主线,而且每个特性都可以由代码签入自动触发,并在几分钟内完成构建。

当某个特性完成后,将合并入主线准备发布,对应于这个分支的构建配置就可以移除了。

部署

现在,CI 系统已经为我们构建好代码,运行了测试,并且暂存了待发布的内容,我们可以讨论部署的事了。和其它部分一样,部署你的应用程序也有多种策略供选择。以下是将应用程序部署到 IIS 时可以采用的一些基本策略。

  • 仅备份当前的应用程序,替换为新版本代码,而完全不必考虑修改配置信息。
  • 将你的代码拷贝到 web 服务器上的一个对应新版本的路径下,这可以预先完成。当一切就绪时,再将 IIS 指向新的路径。你也可以选择多做一步,即在替换代码之前,先在 IIS 里创建一个“staging”web 应用程序,指向你的新版本代码并做一些基本的验证测试。
  • 不要隐藏版本号,将它们包含在你的 URL 里:比如 http://example.com/v3.4/ http://example.com/v3.5/ , 并通过简单的改动配置文件或 IIS 设置信息,将根目录应用 http://example.com/ 指向你的新应用程序。3.4 将依然可用,而只有访问根目录应用的用户会应用新版本。这给了你一个不影响现有用户会话的机会。过了大概一小时后,当你发现 3.4 不再有用户会话时,你就可以安全地从 IIS 中移除 3.4 版本了。

你的团队可以决定对你来说最佳的方案,这取决于你的组织对系统正常运行时间及不可用时间所制订的政策,以及数据库的设计方针。拿我们作为例子来说,我们预期每周可以有一小时的不可用时间,因此我们会采取简单的文件拷贝策略,以便能够给我们充足的时间,在系统恢复前备份、部署代码和数据库及运行测试。

你的 CI 系统已经暂存了待发布内容,因此你的工作仅仅是将这些文件放到生产服务器上,并部署你的数据库改动。如果你的本机具有访问构建服务器和 web 服务器文件系统的权限,那么只需运行如下的批处理文件就可以简单地完成文件拷贝工作:

复制代码
rem BACKUP
robocopy /E \\web1\www\BankWeb \\web1\Backups\BankWeb\%DATETIME%
robocopy /E \\web1\www\BankRest \\web1\Backups\BankRest\%DATETIME%
robocopy /E \\svc1\www\BankWcf \\svc1\Backups\BankWcf\%DATETIME%
rem DEPLOY
robocopy /E \\build1\BankWeb\deploy \\web1\www\BankWeb
robocopy /E \\build1\BankRest\deploy \\web1\www\BankRest
robocopy /E \\build1\BankWcf\deploy \\svc1\www\BankWcf
...etc...

如果你不具有完整的文件访问权限,那你需要更有创意的方法来部署文件。当然,你可以通过远程桌面访问服务器,或者跳板服务器以执行批处理文件或手动拷贝文件。但请记住我们的宗旨是将流程自动化,因此步骤越少越好。理想情况是,你有一台受信的中间系统,在它认证你的身份后就能够部署文件到 web 服务器了。DubDubDeploy 就是这样一种选择,它从某个受信的服务器通过 HTTP 方式拷贝代码,使得你可以在不具有 web 服务器的文件系统访问权限时进行部署。

部署数据库也有多种方式,仍然取决于你的组织。在这个示例中,我们使用了一个 database 项目,因此只需运行一句命令就可以将项目内容与生产数据库中的内容自动对比,运行脚本实现内容改变,并运行你所编写的填数据的定制 SQL 语句。如果你乐于让系统自己完成这些任务,那只需执行以下命令就可实现:

复制代码
msbuild.exe /target:Deploy p:TargetDatabase=3rdNational;
TargetConnectionString=”Server=db1;
Integrated Security=True”;
Configuration=release BankDb.sqlproj

当然,你有多种方法来执行它,可以作为一个目标对象加入你的 NAnt 脚本,或者加入 TeamCity,或者手动执行,也可以放入一个部署批处理文件中。

结论

相比之前那种需要手动实现构建、运行测试、暂存、备份及部署代码流程的方式,我们已取得了很大的进步。现在,我们有编译代码的脚本,持续且稳定地运行这些脚本和测试的系统,以及简单且可重复的部署任务。

如果你没有时间,也不必一次到位。你可以一次只做一点点,而仍旧可以从每个步骤中得到好处。比方说,你可以在一小时之内为你的整个应用创建一个基本的构建脚本,然后可以再花一小时进行测试和调试,以确保构建结果和原先的方式一致。那么即使没有使用 CI 或其它自动化工具,你也降低了构建和暂存应用的难度。等到下次手动进行部署时,你甚至不用打开你的 IDE。也许你有时间在下周或下个月搭建一个简单的 CI 项目,然后在下下个月继续改进。没准当你回过神来的时候,你已经有了一套完整的自动化流程了。

在这个示例中我使用了.NET,NAnt 和 TeamCity,不过基本的原理也可以应用到其它平台。无论何种操作系统、编程语言、服务端技术、源代码管理策略和团队结构,自动化都是可以实现,并且值得投入精力的。

关于作者

Joe Enos是一位软件工程师兼企业家,有着 10 年的.NET 平台的工作经验。他主要关注于自动化与过程改进,并且不仅限于软件领域。他在美国国内的各大软件会议中进行过演讲,主要讲述小型开发团队的自动化构建,并介绍了构建脚本和持续集成的相关主题。

他的公司所推出的第一套软件产品 DubDubDeploy 已于近期发布,这是帮助改进开发团队管理构建和部署流程的一系列产品中的第一件。他的团队如今正致力于实现.NET 构建流程的完全自动化。

查看英文原文: Automated Builds: How to Get Started


感谢杨赛对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

2013 年 7 月 26 日 09:003060
用户头像

发布了 428 篇内容, 共 148.4 次阅读, 收获喜欢 20 次。

关注

评论

发布
暂无评论
发现更多内容

作业一:食堂就餐卡系统设计

Geek_36d3e5

架构师思维

林昱榕

极客大学架构师训练营

食堂就餐卡系统设计

李广富

架构师训练营 第一周 作业 食堂就餐卡系统设计

一雄

极客大学架构师训练营 作业 第一周

随遇而安的适配器模式 | Spring 中的适配器

海星

Java spring 面试 设计模式 Java 25 周年

架构设计文档学习总结

jason

食堂就餐卡系统设计

赵龙

Week 01 学习总结

卧石漾溪

极客大学架构师训练营

架构师训练营第一周总结

Cloud.

极客大学架构师训练营

第三季已经起航,送你一份活动手册吧

赵新龙

写作 社群

第01周命题作业-食堂就餐卡系统架构设计

Jaye

极客大学架构师训练营

架构训练营-第一课总结

架构师训练营-第一课学习总结

King

学习 感悟 极客大学架构师训练营

迷宫的生成: DFS与BFS算法的实现

lmymirror

DFS 迷宫 Cocos Creator BFS

ARTS-week3

王钰淇

ARTS 打卡计划

重新定义失败

史方远

个人成长 随笔杂谈

架构训练营-食堂就餐卡管理系统

C02-商业模式与架构设计

吴传卜

机器学习复习-线性回归

JustBuyIt

学习 线性回归

架构师训练营第一周学习总结

不谈

Week 01 命题作业

卧石漾溪

极客大学架构师训练营

01学习总结

赵龙

如何成为一个架构师

_MISSYOURLOVE

极客大学架构师训练营

食堂就餐卡系统设计

stardust20

程序员如何破除「迷茫」

顿晓

学习 程序员 架构 迷茫

架构师训练营 第一周 学习总结

一雄

学习 极客大学架构师训练营 第一周

食堂就餐卡系统设计

Z冰红茶

虽则悲欢不尽相同

zhoo299

随笔

食堂就餐卡系统设计

wyzwlj

极客大学架构师训练营

架构师0期 | 架构师是怎样炼成的?

刁架构

极客大学架构师训练营

考虑用静态工厂方法代替构造器

dreamer

Java 互联网 后端 开发

InfoQ 极客传媒开发者生态共创计划线上发布会

InfoQ 极客传媒开发者生态共创计划线上发布会

自动化构建的那些事儿:金融网站虚拟案例-InfoQ