开头语
关于如何为单一的 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 “
在服务器上定义“示例”环境(在提示出现后,输入示例服务器的管理员证明 - 后面将有详细讲解):
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 <public-URL-of-SimpleWebApp-1.1>
然后部署新版本 1.1:
New-Deployment SimpleWebApp 1.1 -to demo
从日志可以看出,每次部署都会在本地:\applications\
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.
” updates connection string with name in “connectionStrings”section. - Setting with name “AppSettings.
” updates “AppSettings” value with 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 )关注我们,并与我们的编辑和其他读者朋友交流。
评论