使用 Azure) 生成Real-World云应用 (自动执行一切

作者 :Rick AndersonTom Dykstra

下载修复项目下载电子书

使用 Azure 构建真实世界云应用 电子书基于 Scott Guthrie 开发的演示文稿。 本文介绍了 13 种模式和做法,可帮助你成功开发适用于云的 Web 应用。 有关电子书的简介,请参阅 第一章

我们将介绍的前三种模式实际上适用于任何软件开发项目,尤其是云项目。 此模式与自动执行开发任务有关。 这是一个重要的主题,因为手动过程缓慢且容易出错;自动执行尽可能多的工作流有助于设置快速、可靠和敏捷的工作流。 对于云开发来说,这一点非常重要,因为可以轻松自动执行许多在本地环境中难以或不可能自动执行的任务。 例如,可以设置整个测试环境,包括新的 Web 服务器和后端 VM、数据库、blob 存储 (文件存储) 、队列等。

DevOps 工作流

你越来越多地听到术语“DevOps”。开发术语是出于对必须集成开发和操作任务才能高效开发软件的认知而开发的。 要启用的工作流类型是可以开发应用、部署应用、从其生产使用情况中学习、根据所学内容进行更改以及快速可靠地重复该周期的工作流。

一些成功的云开发团队每天多次部署到实时环境。 Azure 团队过去每 2-3 个月部署一次主要更新,但现在每 2-3 天发布一次次要更新,而主要版本每 2-3 周发布一次。 进入这种节奏确实可以帮助你响应客户反馈。

为此,必须启用可重复、可靠、可预测且周期时间较短的开发和部署周期。

DevOps 工作流

换句话说,从你对某个功能有想法到客户使用该功能并提供反馈之间的时间段必须尽可能短。 前三种模式(自动执行所有内容、源代码管理以及持续集成和交付)都是我们建议的最佳做法,以便启用此类流程。

Azure 管理脚本

在本电子书简介中,你看到了基于 Web 的控制台 Azure 管理门户。 使用管理门户可以监视和管理已在 Azure 上部署的所有资源。 这是创建和删除 Web 应用和 VM 等服务、配置这些服务、监视服务操作等的简单方法。 这是一个伟大的工具,但使用它是一个手动过程。 如果要开发任何规模的生产应用程序,尤其是在团队环境中,建议通过门户 UI 了解和探索 Azure,然后自动执行将重复执行的过程。

几乎可以在管理门户或 Visual Studio 中手动执行的所有操作也可以通过调用 REST 管理 API 来完成。 可以使用 Windows PowerShell 编写脚本,也可以使用 Chef 或Puppet 等开放源代码框架。 还可以在 Mac 或 Linux 环境中使用 Bash 命令行工具。 Azure 具有适用于所有这些不同环境的脚本 API,并且它有 一个 .NET 管理 API ,以防你想要编写代码而不是脚本。

对于 Fix It 应用,我们创建了一些Windows PowerShell脚本,这些脚本可自动执行创建测试环境并将项目部署到该环境的过程,我们将查看这些脚本的一些内容。

环境创建脚本

我们将查看的第一个脚本名为 New-AzureWebsiteEnv.ps1。 它创建一个 Azure 环境,可将 Fix It 应用部署到其中进行测试。 此脚本执行的main任务如下:

  • 创建 Web 应用。
  • 创建存储帐户。 (Blob 和队列是必需的,如后面的章节所示。)
  • 创建一个SQL 数据库服务器和两个数据库:一个应用程序数据库和一个成员资格数据库。
  • 应用将用于访问存储帐户和数据库的 Azure 中的存储设置。
  • 创建将用于自动执行部署的设置文件。

运行脚本

注意

本章的这一部分显示了脚本的示例以及你为运行脚本而输入的命令。 这是一个演示,不提供运行脚本所需的全部信息。 有关操作说明的分步说明,请参阅 附录:修复它示例应用程序

若要运行管理 Azure 服务的 PowerShell 脚本,必须安装 Azure PowerShell 控制台并将其配置为使用 Azure 订阅。 设置完成后,可以使用如下所示的命令运行 Fix It 环境创建脚本:

.\New-AzureWebsiteEnv.ps1 -Name <websitename> -SqlDatabasePassword <password>

Name参数指定创建数据库和存储帐户时要使用的名称,SqlDatabasePassword参数指定将为 SQL 数据库 创建的管理员帐户的密码。 可以使用其他参数,我们稍后将介绍这些参数。

PowerShell 窗口

脚本完成后,可以在管理门户中查看已创建的内容。 你将找到两个数据库:

数据库

存储帐户:

存储帐户

以及 Web 应用:

网站

在 Web 应用的“ 配置 ”选项卡上,可以看到它已为“修复它”应用设置了存储帐户设置和 SQL 数据库连接字符串。

appSettings 和 connectionStrings

自动化文件夹现在还包含 <websitename.pubxml> 文件。 此文件存储 MSBuild 将用于将应用程序部署到刚刚创建的 Azure 环境的设置。 例如:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <WebPublishMethod>MSDeploy</WebPublishMethod>
    <SiteUrlToLaunchAfterPublish>http://fixitdemo.azurewebsites.net</SiteUrlToLaunchAfterPublish>
    <ExcludeApp_Data>False</ExcludeApp_Data>
    <MSDeployServiceURL>waws-prod-bay-003.publish.azurewebsites.windows.net:443</MSDeployServiceURL>
    <DeployIisAppPath>fixitdemo</DeployIisAppPath>
    <RemoteSitePhysicalPath />
    <SkipExtraFilesOnServer>True</SkipExtraFilesOnServer>
    <MSDeployPublishMethod>WMSVC</MSDeployPublishMethod>
    <EnableMSDeployBackup>True</EnableMSDeployBackup>
    <UserName>$fixitdemo</UserName>
    <PublishDatabaseSettings></PublishDatabaseSettings>
  </PropertyGroup>
</Project>

如你所看到的,该脚本创建了一个完整的测试环境,整个过程大约在 90 秒内完成。

如果团队中的其他人想要创建测试环境,他们只需运行脚本即可。 它不仅速度很快,而且他们可以确信他们使用的环境与你正在使用的环境完全相同。 如果每个人都使用管理门户 UI 手动设置内容,则你无法确信这一点。

查看脚本

实际上有三个脚本可以执行此操作。 从命令行调用一个,它会自动使用其他两个来执行某些任务:

  • New-AzureWebSiteEnv.ps1是main脚本。

    • New-AzureStorage.ps1 创建存储帐户。
    • New-AzureSql.ps1 创建数据库。

main脚本中的参数

main脚本 New-AzureWebSiteEnv.ps1定义多个参数:

[CmdletBinding(PositionalBinding=$True)]
Param(
    [Parameter(Mandatory = $true)]
    [ValidatePattern("^[a-z0-9]*$")]
    [String]$Name,                             
    [String]$Location = "West US",             
    [String]$SqlDatabaseUserName = "dbuser",   
    [String]$SqlDatabasePassword,              
    [String]$StartIPAddress,                   
    [String]$EndIPAddress                      
    )

需要两个参数:

  • 脚本创建的 Web 应用的名称。 (这还用于 URL: <name>.azurewebsites.net.)
  • 脚本创建的数据库服务器的新管理用户的密码。

使用可选参数可以指定数据中心位置 (默认为“美国西部”) ,数据库服务器管理员名称 (默认为“dbuser”) ,以及数据库服务器的防火墙规则。

创建 Web 应用

该脚本做的第一件事是通过调用 New-AzureWebsite cmdlet 创建 Web 应用,将 Web 应用名称和位置参数值传递给它:

# Create a new website
$website = New-AzureWebsite -Name $Name -Location $Location -Verbose

创建存储帐户

然后,main脚本运行New-AzureStorage.ps1脚本,为存储帐户名称指定“*<websitename>*storage”,并指定与 Web 应用相同的数据中心位置。

$storageAccountName = $Name + "storage"
 
$storage = $scriptPath\New-AzureStorage.ps1" -Name $storageAccountName -Location $Location

New-AzureStorage.ps1 调用 New-AzureStorageAccount cmdlet 来创建存储帐户,并返回帐户名称和访问密钥值。 应用程序需要这些值才能访问存储帐户中的 Blob 和队列。

# Create a new storage account
New-AzureStorageAccount -StorageAccountName $Name -Location $Location -Verbose
 
# Get the access key of the storage account
$key = Get-AzureStorageKey -StorageAccountName $Name
 
# Generate the connection string of the storage account
$connectionString = "BlobEndpoint=http://$Name.blob.core.windows.net/;QueueEndpoint=http://$Name.queue.core.windows.net/;TableEndpoint=http://$Name.table.core.windows.net/;AccountName=$Name;AccountKey=$primaryKey"
 
#Return a hashtable of storage account values
Return @{AccountName = $Name; AccessKey = $key.Primary; ConnectionString = $connectionString}

可能并不总是想要创建新的存储帐户;可以通过添加参数来增强脚本,该参数可以选择性地指示该脚本使用现有存储帐户。

创建数据库

然后,main脚本在设置默认数据库和防火墙规则名称后 ,New-AzureSql.ps1运行数据库创建脚本:

$sqlAppDatabaseName = "appdb"
$sqlMemberDatabaseName = "memberdb"
$sqlDatabaseServerFirewallRuleName = $Name + "rule"
# Create a SQL Azure database server, app and member databases
$sql = $scriptPath\New-AzureSql.ps1 `
    -AppDatabaseName $sqlAppDatabaseName `
    -MemberDatabaseName $sqlMemberDatabaseName `
    -UserName $SqlDatabaseUserName `
    -Password $SqlDatabasePassword `
    -FirewallRuleName $sqlDatabaseServerFirewallRuleName `
    -StartIPAddress $StartIPAddress `
    -EndIPAddress $EndIPAddress `
    -Location $Location

数据库创建脚本检索开发计算机的 IP 地址并设置防火墙规则,以便开发计算机可以连接到和管理服务器。 然后,数据库创建脚本会经历几个步骤来设置数据库:

  • 使用 New-AzureSqlDatabaseServer cmdlet 创建服务器。

    $databaseServer = New-AzureSqlDatabaseServer -AdministratorLogin $UserName -AdministratorLoginPassword $Password -Location $Location
    
  • 创建防火墙规则,使开发计算机能够管理服务器,并使 Web 应用能够连接到该服务器。

    # Create a SQL Azure database server firewall rule for the IP address of the machine in which this script will run
    # This will also allowlist all the Azure IP so that the website can access the database server
    New-AzureSqlDatabaseServerFirewallRule -ServerName $databaseServerName -RuleName $FirewallRuleName -StartIpAddress $StartIPAddress 
    -EndIpAddress $EndIPAddress -Verbose
    New-AzureSqlDatabaseServerFirewallRule -ServerName $databaseServer.ServerName -AllowAllAzureServices 
    -RuleName "AllowAllAzureIP" -Verbose
    
  • 使用 New-AzureSqlDatabaseServerContext cmdlet 创建包含服务器名称和凭据的数据库上下文。

    # Create a database context which includes the server name and credential
    # These are all local operations. No API call to Azure
    $credential = New-PSCredentialFromPlainText -UserName $UserName -Password $Password
    $context = New-AzureSqlDatabaseServerContext -ServerName $databaseServer.ServerName -Credential $credential
    

    New-PSCredentialFromPlainText 是脚本中的一个函数,用于调用 ConvertTo-SecureString cmdlet 来加密密码并返回对象 PSCredential ,该对象的类型与 Get-Credential cmdlet 返回的类型相同。

  • 使用 New-AzureSqlDatabase cmdlet 创建应用程序数据库和成员资格数据库。

    # Use the database context to create app database
    New-AzureSqlDatabase -DatabaseName $AppDatabaseName -Context $context -Verbose
     
    # Use the database context to create member database
    New-AzureSqlDatabase -DatabaseName $MemberDatabaseName -Context $context -Verbose
    
  • 调用本地定义的函数,为每个数据库创建连接字符串。 应用程序将使用这些连接字符串来访问数据库。

    $appDatabaseConnectionString = Get-SQLAzureDatabaseConnectionString -DatabaseServerName $databaseServerName -DatabaseName $AppDatabaseName -UserName $UserName -Password $Password
    $memberDatabaseConnectionString = Get-SQLAzureDatabaseConnectionString -DatabaseServerName $databaseServerName -DatabaseName $MemberDatabaseName -UserName $UserName -Password $Password
    

    Get-SQLAzureDatabaseConnectionString是在脚本中定义的函数,该函数根据提供给它的参数值创建连接字符串。

    Function Get-SQLAzureDatabaseConnectionString
    {
        Param(
            [String]$DatabaseServerName,
            [String]$DatabaseName,
            [String]$UserName,
            [String]$Password
        )
    
        Return "Server=tcp:$DatabaseServerName.database.windows.net,1433;Database=$DatabaseName;User ID=$UserName@$DatabaseServerName;Password=$Password;Trusted_Connection=False;Encrypt=True;Connection Timeout=30;"
    }
    
  • 返回包含数据库服务器名称和连接字符串的哈希表。

    Return @{ `
        Server = $databaseServer.ServerName; UserName = $UserName; Password = $Password; `
        AppDatabase = @{Name = $AppDatabaseName; ConnectionString = $appDatabaseConnectionString}; `
        MemberDatabase = @{Name = $MemberDatabaseName; ConnectionString = $memberDatabaseConnectionString} `
    }
    

Fix It 应用使用单独的成员身份和应用程序数据库。 还可以将成员身份和应用程序数据放在单个数据库中。

应用商店应用设置和连接字符串

Azure 具有一项功能,可用于存储设置和连接字符串,当应用程序尝试读取 Web.config 文件中的 appSettingsconnectionStrings 集合时,这些设置和连接字符串会自动替代返回到应用程序的内容。 这是在部署时应用 Web.config转换的 替代方法。 有关详细信息,请参阅本电子书后面的 在 Azure 中存储敏感数据

环境创建脚本将应用程序在 Azure appSettings 中运行时访问存储帐户和数据库所需的所有 和 connectionStrings 值存储在 Azure 中。

# Configure app settings for storage account and New Relic
$appSettings = @{ `
    "StorageAccountName" = $storageAccountName; `
    "StorageAccountAccessKey" = $storage.AccessKey; `
    "COR_ENABLE_PROFILING" = "1"; `
    "COR_PROFILER" = "{71DA0A04-7777-4EC6-9643-7D28B46A8A41}"; `
    "COR_PROFILER_PATH" = "C:\Home\site\wwwroot\newrelic\NewRelic.Profiler.dll"; `
    "NEWRELIC_HOME" = "C:\Home\site\wwwroot\newrelic" `
}
# Configure connection strings for appdb and ASP.NET member db
$connectionStrings = ( `
    @{Name = $sqlAppDatabaseName; Type = "SQLAzure"; ConnectionString = $sql.AppDatabase.ConnectionString}, `
    @{Name = "DefaultConnection"; Type = "SQLAzure"; ConnectionString = $sql.MemberDatabase.ConnectionString}
)
# Add the connection string and storage account name/key to the website
Set-AzureWebsite -Name $Name -AppSettings $appSettings -ConnectionStrings $connectionStrings

New Relic 是一个遥测框架,我们在 监视和遥测 一章中演示。 环境创建脚本还会重启 Web 应用,以确保它选取 New Relic 设置。

# Restart the website to let New Relic hook kick in
Restart-AzureWebsite -Name $websiteName

准备部署

在过程结束时,环境创建脚本调用两个函数来创建部署脚本将使用的文件。

其中一个函数) 创建发布配置文件 (<websitename.pubxml> 文件。 代码调用 Azure REST API 以获取发布设置,并将信息保存在 .publishsettings 文件中。 然后,它使用该文件中的信息以及 (pubxml.template) 模板文件来创建包含发布配置文件的 .pubxml 文件。 此两步过程模拟在 Visual Studio 中执行的操作:下载 .publishsettings 文件并导入该文件以创建发布配置文件。

另一个函数使用另一个模板文件 (website-environment.template) 创建一个 website-environment.xml 文件,该文件包含部署脚本将与 .pubxml 文件一起使用的设置。

故障排除和错误处理

脚本类似于程序:它们可能会失败,你希望尽可能多地了解失败及其原因。 因此,环境创建脚本将变量的值 VerbosePreferenceSilentlyContinue 更改为 Continue ,以便显示所有详细消息。 它还会将变量的值 ErrorActionPreferenceContinue 更改为 Stop,以便脚本即使在遇到非终止错误时也会停止:

# Set the output level to verbose and make the script stop on error
$VerbosePreference = "Continue"
$ErrorActionPreference = "Stop"

在执行任何工作之前,脚本会存储开始时间,以便可以计算完成后的运行时间:

# Mark the start time of the script execution
$startTime = Get-Date

完成工作后,脚本会显示已用时间:

# Mark the finish time of the script execution
$finishTime = Get-Date
# Output the time consumed in seconds
Write-Output ("Total time used (seconds): {0}" -f ($finishTime - $startTime).TotalSeconds)

对于每个键操作,脚本都会写入详细消息,例如:

Write-Verbose "[Start] creating $websiteName website in $Location location"
$website = New-AzureWebsite -Name $websiteName -Location $Location -Verbose
Write-Verbose "[Finish] creating $websiteName website in $Location location"

部署脚本

New-AzureWebsiteEnv.ps1脚本对环境创建执行的操作,Publish-AzureWebsite.ps1脚本用于应用程序部署。

部署脚本从环境创建脚本创建的 website-environment.xml 文件中获取 Web 应用的名称。

[Xml]$envXml = Get-Content "$scriptPath\website-environment.xml"
$websiteName = $envXml.environment.name

它从 .publishsettings 文件获取部署用户密码:

[Xml]$xml = Get-Content $scriptPath\$websiteName.publishsettings 
$password = $xml.publishData.publishProfile.userPWD[0]
$publishXmlFile = Join-Path $scriptPath -ChildPath ($websiteName + ".pubxml")

它执行生成和部署项目的 MSBuild 命令:

& "$env:windir\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" $ProjectFile `
    /p:VisualStudioVersion=12.0 `
    /p:DeployOnBuild=true `
    /p:PublishProfile=$publishXmlFile `
    /p:Password=$password

如果在命令行上指定 Launch 了 参数,它将调用 Show-AzureWebsite cmdlet 以打开网站 URL 的默认浏览器。

If ($Launch)
{
    Show-AzureWebsite -Name $websiteName
}

可以使用如下所示的命令运行部署脚本:

.\Publish-AzureWebsite.ps1 ..\MyFixIt\MyFixIt.csproj -Launch

完成后,浏览器将打开,其中 URL 在云 <websitename>.azurewebsites.net 中运行的站点。

修复部署到 Windows Azure 的 It 应用

总结

使用这些脚本,可以确信始终使用相同的选项以相同的顺序执行相同的步骤。 这有助于确保团队中的每个开发人员不会错过某些内容或弄乱某些内容,也不会在其自己的计算机上部署一些自定义内容,这些自定义内容实际上不会在另一个团队成员的环境中或生产环境中以相同的方式工作。

以类似的方式,可以使用 REST API、Windows PowerShell脚本、.NET 语言 API 或可在 Linux 或 Mac 上运行的 Bash 实用工具,自动执行可在管理门户中完成的大多数 Azure 管理功能。

在下一章中,我们将介绍源代码,并解释为什么在源代码存储库中包含脚本很重要。

资源