Amazon AWS 部署到配置多个网站





5.00/5 (1投票)
本文提供了一种在使用 AWSDeploy 时,将多个应用程序部署到单个 AWS 实例的解决方案。
引言
最近我参与了一个使用 Amazon CloudFormation 实现 BlueGreen 部署的项目,并遇到了一些未解决的问题。
- 如何将多个应用程序部署到单个实例并支持自动伸缩?
- 如何使用 AWSDeploy 部署非网站应用程序?
- 如何在预配期间传入环境配置?
对于前两个问题,官方的答案似乎是,如果没有额外的脚本/工具,则无法实现。第三个问题(有一些例外)是,您需要在部署之前将它们烘焙到包中。
对于这些常见的实际用例,这似乎令人沮丧,但幸运的是,有一个只需很少努力就能解决的方案。
首先,一些背景知识
CloudFormation 是 Amazon 提供的一种服务,用于通过声明式模板在 Amazon 基础设施上预配一组 AWS 资源。这些资源集合称为堆栈,可用于可靠且可重复的基础设施部署。
AWSDeploy 是可与 Microsoft 堆栈一起使用的工具,用于预配 CloudFormation 模板并将 Web 应用程序部署到这些预配的资源上。它底层使用了 Microsoft WebDeploy,因此在使用该工具预配 CloudFormation 堆栈时,必须提供 WebDeploy 包。
用户提供的 WebDeploy 包由该工具上传到 AWS,并存储在 S3 存储桶中。当 EC2 Web 服务器启动时,AWSDeploy 会在初始化堆栈的过程中将该包安装到 Web 服务器上。默认情况下,任何失败都会导致堆栈回滚。
一旦通过 AWSDeploy 预配了 Web 应用程序,堆栈将通过将存储在 S3 上的关联 WebDeploy 包重新部署到任何新实例来自动管理故障转移和自动伸缩场景。通过这样做,它可以近乎免手动地管理资源,并按需复制应用程序的完美副本。
强大的力量伴随着巨大的限制
如上所述,AWSDeploy 在故障转移和伸缩场景中非常有用。您的 Web 应用程序可以自动复制和预配,无需手动干预。
缺点是存在一些限制:
- 每个 Web 服务器实例只能关联一个 WebDeploy 包
- 在 AWSDeploy 安装 WebDeploy 包时,无法传递任何参数
- 因此,WebDeploy 参数化不能用于配置转换。
- 环境配置仅限于一小部分预定义的 appSettings 键,这些键可以在预配期间注入。
其影响是,为了与 AWSDeploy 配合使用,您通常必须为每个部署环境修改您的构建产物,并将配置设置烘焙到其中。这与持续交付原则相悖,并增加了任何部署脚本的复杂性。
一个想法
WebDeploy 本身是一个极其灵活且功能强大的工具。在一个 WebDeploy 包中,可以部署多个网站、部署任意文件目录、执行命令以及更改 ACL 权限。
因此,与其将环境配置烘焙到 WebDeploy 包中以支持 AWSDeploy,不如创建一个包装器 WebDeploy 包,并使用嵌入的 RunCommand
在预配期间将环境变量传递给子包。这样,原始构建产物就不会被修改,并且 WebDeploy 的全部功能可用于在安装期间注入参数。
这种方法还有其他好处,因为它允许通过单个 AWSDeploy 包安装多个 Web 应用程序,以及在自动预配期间部署 Windows 服务和控制台应用程序的能力。此外,然后可以相对容易地为特定实例选择性地部署应用程序,而无需对部署脚本进行大量重构。
问题
在我创建第一个包装器包时,预配时我立即遇到了一个问题。AWSDeploy 日志显示了问题。
ERROR 10 AWSDeploymentHostManager.Tasks.UpdateAppVersionTask - Deploy failed, not iisApp.
似乎 AWSDeploy 希望 WebDeploy 包在其清单中包含一个 iisApp
。所以我包含了我 WebDeploy 包中的一个占位符网站,并再次尝试。令人惊讶的是,我遇到了同样的错误!
经过大量调查,结果发现当 AWSDeploy 执行 WebDeploy 包时,它会将以下参数传递给该包。这是为了在预配期间自定义目标 IIS 网站名称,并且可以通过 AWSDeploy 配置设置进行设置。
-setParam:"IIS Web Application Name"="Default Web Site/"
解决方案
了解参数要求后,解决方案很简单。只要 WebDeploy 包包含一个带有此参数定义的参数文件,AWSDeploy 就可以成功部署该包,而不管其内容如何。这也意味着 WebDeploy 包实际上不需要包含 iisApp
实例,正如错误最初暗示的那样。
整合
首先,将要在服务器实例上安装的任何 WebDeploy 包放在一个名为 C:\Packages
的文件夹中。包含任何环境设置和配置转换的任何 set-parameter 配置文件。
创建一个名为 Install.ps1
的 PowerShell 安装脚本,并将其添加到 C:\Packages
目录中。该脚本将在预配期间在服务器上执行。它将用于部署此文件夹中的 WebDeploy 包,并可用于在安装时调用显式 WebDeploy 命令;例如,利用特定于环境的参数化。
Set-Alias msdeploy "C:\Program Files\IIS\Microsoft Web Deploy V3\msdpeloy.exe"
msdeploy -verb:sync -source:package=WebsiteA.zip -dest:auto -setParamFile:WebSiteA-Params.xml
msdeploy -verb:sync -source:package=WebsiteB.zip -dest:auto -setParamFile:WebSiteB-Params.xml
接下来,创建要包含在 WebDeploy 包装器包中的参数文件,如下所示。将文件命名为 parameters.xml
,并将其放在名为 C:\Build
的文件夹中。
<parameters>
<parameter name="IIS Web Application Name" defaultValue="Default Web Site/" tags="IisApp" />
</parameters>
现在为包装器包创建一个 WebDeploy 清单文件,如下所示。这将把 C:\Packages
的内容预配到服务器实例,并使用 RunCommand
提供程序执行此目录根目录中的 Install.ps1
PowerShell 脚本。将文件命名为 manifest.xml
,并将其放在 C:\Build
文件夹中。
<siteManifest>
<contentPath path="C:\Packages" />
<runCommand path="%SystemRoot%\System32\WindowsPowerShell\v1.0\PowerShell.exe
-inputformat none -command "Set-ExecutionPolicy Unrestricted -force""
waitInterval="10000" />
<runCommand path="%SystemRoot%\System32\WindowsPowerShell\v1.0\PowerShell.exe
-inputformat none -command C:\Packages\Install.ps1"
waitInterval="300000" />
</siteManifest>
最后,使用以下命令行命令打包 WebDeploy 包装器包,该命令应从 C:\Build
文件夹执行。关键在于 declareParamFile
参数和 parameters.xml
文件,它们能让 AWSDeploy 正常工作。
msdeploy -verb:sync -source:manifest=manifest.xml -dest:package=Deploy.zip
-declareParamFile=parameters.xml
生成的 WebDeploy 包现在已准备好与 AWSDeploy 工具一起使用,可以在 EC2 服务器上预配一个或多个应用程序。它还完全兼容自动伸缩和故障转移场景。
您可以通过在 C:\Build
目录中执行以下命令在本地测试安装。
msdeploy -verb:sync -source:package=Deploy.zip -dest:auto
进一步增强
该解决方案最大限度地减少了对不同环境的部署脚本的定制,并且产生的解决方案与 Amazon AWSDeploy 的耦合度大大降低。它还规避了 AWSDeploy 的限制,并在如何配置实例以支持自动预配方面提供了更大的灵活性。
这是一个改进,但我们仍然不得不为每个环境构建不同的产物,这并不理想。进一步的增强是利用 PowerShell 脚本中的 AWS UserData
。UserData
在预配期间进行配置,并通过固定地址的 Web 服务提供给每个服务器实例。
http://169.254.169.254/latest/user-data
UserData
在故障转移和伸缩期间会持久化,并且可以用于动态地向 PowerShell 脚本提供 set-parameter 文件,或者提供标志,指示 PowerShell 脚本在安装包时为每个环境使用哪个预部署的 set-parameter 文件。这样,就可以根据最佳实践,为所有环境使用单个产物,以及单个安装和部署脚本。