开头语
\关于如何为单一的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单元测试\
该示例应用程序的代码库托管于BitBucket:
\我们到底将如何部署?
\我们遇到的第一个问题就是如何部署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模块导入到当前会话中:
\\Import-Module AppRolla\
AppRolla有两组cmdlets:配置和部署。通过配置cmdlets定义应用程序和环境。
\紧接着,添加新的“SimpleWebApp”应用程序,该应用程序只带有单一的“website”角色:
\\New-Application SimpleWebApp\
Add-WebsiteRole SimpleWebApp Web -PackageURL \"\u0026lt;your-dropbox-download-link\u0026gt;\"
\在服务器上定义“示例”环境(在提示出现后,输入示例服务器的管理员证明 - 后面将有详细讲解):
\\New-Environment demo\
Add-EnvironmentServer demo \"appveyor-demo.cloudapp.net\" -Credential (Get-Credential)
\将“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 \u0026lt;public-URL-of-SimpleWebApp-1.1\u0026gt;\
然后部署新版本1.1:
\\New-Deployment SimpleWebApp 1.1 -to demo\
从日志可以看出,每次部署都会在本地:\\applications\\\u0026lt;application-name\u0026gt;\\\u0026lt;role-name\u0026gt;\\\u0026lt;version\u0026gt;中生成新的文件夹。默认情况下,目标服务器上可以保留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,它带有数据库链接字符及其它应用程序针对每个环境特定的设置。该方法看起来可行,却有一系列问题:
\• 类似数据库链接字符这样的敏感数据存储于源代码控制中。
\• 配置转换应用于构建过程,每当项目部署到新环境中,都需要重新构建配置转换。
\当用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”,并指定脚本路径:
\\deployment\\deploy.ps1\
设置以下部署变量:
\\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.\u0026lt;name\u0026gt;” updates connection string with \u0026lt;name\u0026gt; name in “connectionStrings”section.\
- Setting with name “AppSettings.\u0026lt;name\u0026gt;” updates “AppSettings” value with \u0026lt;name\u0026gt; name.\
总结
\本文中,我并非试图去低估或淡化其它的Web部署工具,它们可能在你的web应用中得到了很好的应用。但是,如果你的项目已经超出模板化的web应用,而必须部署到集群环境中或你需要更复杂,或对部署流程更多控制的话,那么绝对值得考虑像PowerShell remoting这样替代的解决方案。这对于具有PowerShell技能或使用PSake的开发人员来说,将会特别具有吸引力。
\关于作者
\Feodor Fitsner是个具有创业精神的.NET开发人员,他接触Windows Web平台已超过10年。Feodor最新项目就是Appveyor CI:专为.NET开发人员设计的主机型持续集成解决方案。在AppVeyor之前,Feodor针对Windows主机开发了DotNetPanel控制面板,之后就职于Microsoft的Azure 部门。
\参考英文原文:Deploying it right with AppVeyor CI and PowerShell
\感谢陈菲对本文的审校。
\给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ)或者腾讯微博(@InfoQ)关注我们,并与我们的编辑和其他读者朋友交流。