关于如何为单一的ASP.NET web应用程序设置持续集成,你可以找到很多文章。这些文章都写到如何通过Web
Deploy来构建完美的环境来部署简单、只需稍作修改VS.NET模板的web应用程序。任何东西在这一完美环境下都能顺利进行。
但是,真正部署应用程序的话却并非易事。总是有问题不断出现在以下情况中:当需要在注册表(Registry)或自定义文件夹中配置设置,或者你需要部署到Web集群时。
本文中,我们通过使用PowerShell远程处理(PowerShell remoting)和AppVeyor
CI为带有ASP.NET web应用程序和Windows Service的解决方案在其暂存(staging)和产品环境中配置持续集成。
解决方案概述
我们的示例中包含4个项目:
DemoApp.Web -ASP.NET应用程序,前端
DemoApp.Web.Tests -使用VisualStudio测试框架的web应用程序单元测试
DemoApp.Service -承载WCF服务的Windows service,后端
DemoApp.Service.Tests -使用NUnit框架的Windows
service单元测试
我们到底将如何部署?
我们遇到的第一个问题就是如何部署Windows service?我们没有针对Windows service的“发布”菜单,也没有相应的配置转换。根本没法用Web
Deploy。为了自动化项目的部署,我们将使用PowerShell部署框架 - AppRolla。
AppRolla利用PowerShell远程处理在目标机器上执行部署任务。部署任务将下载应用程序包,解包,更新配置设置,然后创建或更新应用程序网站及pool。该应用程序包仅仅是带有应用程序文件夹的压缩包,通过HTTP上传到外部存储。其内部并没有任何特别之处
- 模块写在PowerShell内,很容易查看和修改。
为了让你对PowerShell部署一睹为快,我们现在就建立一个简单web应用程序,并将其部署到服务器上。
整个流程中最具挑战性的部分可能就是设置带有SSL认证的PowerShell远程处理了。我们强烈建议使用HTTPS与远程服务器进行通信,因为所有的数据流量都是加密的。
当你在Windows Azure 上创建新的虚拟机时,PowerShell远程会自动被激活和配置。防火墙将允许PowerShell远程HTTPS端点端口5986,另外我们也将HTTP端点添加到示例服务器上:
如果你需设置其它服务,可以参照该链接里的具体细节:guide on how to configure
PowerShell remoting。
为了在实例机器上快速安装IIS7.5,我们使用以下PowerShell命令:
Add-WindowsFeature -Name Web-Default-Doc,Web-Dir-Browsing,
Web-Http-Errors,Web-Static-Content,Web-Http-Logging,Web-Stat-Compression,
Web-Filtering,Web-Net-Ext,Web-Net-Ext45,Web-Asp-Net,Web-Asp-Net45,Web-ISAPI-Ext,Web-Mgmt-Console |
我们创建带有“Hello world”的简单Web应用程序,然后将其部署到示例服务器。
创建一个新的SimpleWebApp-1.0.zip压缩包,将带有第一版本的default.aspx存档起来。
现在,我们需要将该应用程序压缩包上传到外部存储,这样目标服务器就能通过HTTP来对其访问。该服务器可以是启用了FTP的web服务器,Amazon
S3或Azure blob存储。对于该示例,我们使用DropBox。它为DropBox文件夹下所有项目提供公共的下载链接。
将该SimpleWebApp-1.0.zip拷贝到你的DropBox文件夹中,然后右击文件夹,选择“Share
DropBox link”。在浏览器中打开该链接,然后拷贝“Download”键的URL。
打开PowerShell的控制台“As administrator”,将执行政策(execution
policy)改成为允许远程PowerShell脚本:
Set-ExecutionPolicy RemoteSigned |
安装AppRolla.psl模块(将被安装到用户配置文件中):
(new-object Net.WebClient).DownloadString("https://raw.github.com/AppVeyor/AppRolla/master/install.ps1") | iex |
将AppRolla模块导入到当前会话中:
AppRolla有两组cmdlets:配置和部署。通过配置cmdlets定义应用程序和环境。
紧接着,添加新的“SimpleWebApp”应用程序,该应用程序只带有单一的“website”角色:
New-Application SimpleWebApp |
在服务器上定义“示例”环境(在提示出现后,输入示例服务器的管理员证明 - 后面将有详细讲解):
将“SimpleWebApp”应用程序作为版本1.0部署到“示例”环境上:
New-Deployment SimpleWebApp 1.0 -to demo |
就这样!你已经通过PowerShell将你的Web应用程序发布到示例服务器上了:
现在,我们修改一下页面内容,然后部署一个新的示例应用程序版本。我们将“Hello,World!”改成“Hello,
world2.0!”。随后,创建新的SimpleWebApp-1.1.zip文件将修改好的default.aspx文件存档,再次上传到DropBox。
更新“website”角色,将其压缩包的URL修改为新的值:
Set-WebsiteRole SimpleWebApp Web -PackageUrl <public-URL-of-SimpleWebApp-1.1> |
然后部署新版本1.1:
New-Deployment SimpleWebApp 1.1 -to demo |
从日志可以看出,每次部署都会在本地:\applications\<application-name>\<role-name>\<version>中生成新的文件夹。默认情况下,目标服务器上可以保留5个部署版本,因此应用程序可以很容易地回滚到上一版本:
Restore-Deployment SimpleWebApp -on demo |
从示例环境中删除所有应用程序部署:
Remove-Deployment SimpleWebApp -from demo |
整个过程,我们都使用简单且简洁的命令来完成所有工作!
使用AppVeyor CI持续构建
AppVeyor CI是为Windows开发人员设计的基于云的持续集成和部署平台。它有自己的服务器,因此不需要安装或配置。就算需要,配置起来也非常简单。另外,AppVeyor
CI对开源项目是免费的。
为了在AppVeyor CI中设置工程,其代码源必须托管于在线源代码控制库上,比如:GitHub、BitBucket或Kiln。它支持Git和Mercurial。
在我们示例中,我们使用托管于BitBucket的Mercurial库。对商业项目来说,BitBucket相当实用,它免费地提供无限制的私有库。
启用NuGet存储
如果你的解决方案依赖于NuGet管理包,不要忘了启用NuGet restore来自动下载构建服务器上的各种程序包。可以使用如何启用NuGe包存储这一指导来确保在.Nuget文件夹中的NuGet.exe已经添加到库里了。
添加新项目
我们可以先从创建新项目开始:
一旦新项目建成,新的web hook就会自动添加到项目库中,为新构建拉开序幕。
构建配置和配置交换
当通过Web Deploy部署时,配置交换在应用程序配置过程中是一个关键。对于每个需要部署的环境,都需定义新的VS.NET解决方案配置,然后使用配置转换生成web.config,它带有数据库链接字符及其它应用程序针对每个环境特定的设置。该方法看起来可行,却有一系列问题:
1.类似数据库链接字符这样的敏感数据存储于源代码控制中。
2.配置转换应用于构建过程,每当项目部署到新环境中,都需要重新构建配置转换。
当用AppVeyor部署时,配置转换非常有用,但并非是必需的。默认情况下,VS会产生两种配置:Debug和Release,大多数情况下,这种形式完全OK。Debug配置用于本地开发,而Release配置则用于CI流程生成可以部署到任何环境的包。配置转换应该“真正地”用于转换配置文件结构,比如:禁用“Debug”标识;启用自定义错误;或启用Autofac更换模式等适用于所有环境的常见设置。
我们现在就可以开始改变Release设置页面中的项目构建配置:
AppVeyor提供三种构建方案:
Visual Studio方案 - 运行MSBuild于VS.NET解决方案或工程文件(如果没有指定,首先查找第一个.sln或.*proj文件),还有将所有项目构建结果以工件形式打包。
MSBuild - 根据自己的规定运行MSBuild,允许在“Packaging”页面上定义自定义的构建工件。
Script - 运行特定PowerShell脚本或批文件。为管理构建流程及其结果提供最大化的自由。
组件版本
每个新项目构建都会收到新的版本号,该版本号的格式规定于“General”页面下。
AppVeyor默认提供Windows style versioning(major.minor.{build}.revision),但是你也可以运用其它任意版本风格,比如:SemVer(major.minor.patch.{build})。
当启用“Update assembly version attributes”时,AppVeyor会将解决方案目录下的所有AssemblyInfo.*文件设置成当前版本。
执行测试
AppVeyor能通过以下这些测试框架,在assembly中发现和运行测试:
MSTest
NUnit
xUnit
当“测试”步骤被启动时,AppVeyor会分析“out”文档中所有的assembly,来确认它们对所支持的测试框架是否含有相应的引用。如果有,那么所有assembly内的测试将会在相应的测试执行器中运行一遍。所有assembly的测试结果也会集合显示在UI上。
构建.测试.打包!
我们现在开始创建新构建,可以对项目库进行修改或点击“New Build”。
该模拟项目的构建流程会生产两个构建工件:web application和Windows service。将它们下载下来,你会发现其内容就是带有应用文件的常规压缩文档。
由于Windows service包基本上就是其“Bin”文件夹下用于生成web应用包的内容中还需包含的一些步骤:
Web应用应是解决方案的一部分。
创建应用了“file system”发布方法的新发布配置文件,并发布了使用MSBuild的WAP项目,该配置文件用来确保web.config修改以及其它发布设置的执行。
将发布的web应用打包为一压缩文件。
构建工件将存储于Geo-redundant云存储中,能通过其独有的专属链接下载。如果想有自己专有的构建工件命名结构,以及允许公共访问的话,可以配置专有存储空间。
成功部署到预生产环境
我们现在开始往staging上配置自动化部署,作为该构建流程的一部分。
该部署应脚本的形式来完成,可以是PowerShell或批处理文件。在项目库的根部创建“部署”文件夹以放置部署脚本。
打开PowerShell命令行,跳转到“部署”文件夹,并执行以下命令下载模板脚本到现有目录:
(new-object Net.WebClient).DownloadString("https://raw.github.com/AppVeyor/Deployment/master/install.ps1") | iex |
会有三个脚本被添加:configure.ps1、 project.ps1
和deploy.ps1。
总的说来,我们只需要编辑一个文件就能配置部署:project.psl。该文件定义了我们将要部署的环境。将删除紧接着新staging环境那一行的备注,然后添加模拟服务器:
New-Environment Staging Add-EnvironmentServer Staging “appveyor-demo.cloudapp.net” |
返回到AppVeyor CI,打开项目设置的“Deployment”页面。
选择“Run deployment script”,并指定脚本路径:
设置以下部署变量:
Environment: Staging ServerUsername: ServerPassword: ApiAccessKey: ApiSecretKey: |
ServerUsername和Password用来创建Credential对象以验证PowerShell的远程调用。以下两种情况要求到API关键字:a)阅读项目工件获取项目包的URL;b)验证目标服务器下载工件包。AppVeyor
API关键字可以在用户配置的“API Keys”页面下找到。
就这样。接着只要提交“deployment”文件夹,将其推送到项目库就可以开始该部署的新构建了。
如何更新web.config的链接字符?
部署变量通过$variables参数以哈希表形式传递到脚本中。对于项目或角色,如果想启用附加的配置变量,可以在Set-Application,
Set-WebsiteRole或Set-ServiceRole cmdlets中使用“Configuration”参数。打开project.ps1,并添加以下语句:
Set-WebsiteRole $projectName DemoApp.Web -Configuration @{ “ConnectionStrings.DefaultConnection” = $variables.DefaultConnection } |
然后在部署设置页面上定义“DefaultConnection”变量,用于web应用的链接字符的传递。部署脚本通过web.config为web应用程序应用了角色配置,对Windows应用程序,则通过app.config,运用了以下这些规则:
Setting with name “ConnectionStrings.<name>” updates connection string with <name> name in “connectionStrings”section. Setting with name “AppSettings.<name>” updates “AppSettings” value with <name> name. |
总结
本文中,我并非试图去低估或淡化其它的Web部署工具,它们可能在你的web应用中得到了很好的应用。但是,如果你的项目已经超出模板化的web应用,而必须部署到集群环境中或你需要更复杂,或对部署流程更多控制的话,那么绝对值得考虑像PowerShell
remoting这样替代的解决方案。这对于具有PowerShell技能或使用PSake的开发人员来说,将会特别具有吸引力。 |