练习 - 部署 Web 应用程序
你任职于一家玩具公司,你们的网站开发团队已将网站的最新版本提交到了 Git 存储库。 现在,你已准备好更新管道以生成网站,并将其部署到 Azure 应用服务。
在此过程中,你将执行以下任务:
- 为生成作业添加新的管道模板。
- 更新管道以包含生成作业。
- 添加新的版本验收测试。
- 更新部署阶段以部署应用程序。
- 运行管道。
为生成作业添加管道模板
你将添加一个新的作业定义,其中包含生成网站应用程序所需的步骤。
打开 Visual Studio Code。
在 deploy/pipeline-templates 文件夹中创建一个名为 build.yml 的新文件。
将以下内容添加到 build.yml 管道模板文件中:
jobs: - job: Build displayName: Build application and database pool: vmImage: windows-latest steps: # Build, copy, and publish the website. - task: DotNetCoreCLI@2 displayName: Build publishable website inputs: command: 'publish' publishWebProjects: true - task: CopyFiles@2 displayName: Copy publishable website inputs: sourceFolder: '$(Build.SourcesDirectory)/src/ToyCompany/ToyCompany.Website/bin' contents: '**/publish.zip' targetFolder: '$(Build.ArtifactStagingDirectory)/website' flattenFolders: true - task: PublishBuildArtifacts@1 displayName: Publish website as pipeline artifact inputs: pathToPublish: '$(Build.ArtifactStagingDirectory)/website' artifactName: 'website'
该作业运行生成步骤,将网站应用程序的源代码转换为准备在 Azure 中运行的编译文件。 然后,该作业将编译后的工件复制到临时暂存文件夹,并将其发布为管道工件。
保存对该文件所做的更改。
重命名第一个管道阶段,并添加生成作业
打开 deploy 文件夹中的 azure-pipelines.yml 文件。
修改 Lint 阶段。 将其重命名为“Build”,并添加使用你创建的 build.yml 管道模板的生成作业。
trigger: batch: true branches: include: - main pool: vmImage: ubuntu-latest stages: - stage: Build jobs: # Build the Visual Studio solution. - template: pipeline-templates/build.yml # Lint the Bicep file. - template: pipeline-templates/lint.yml # Deploy to the test environment. - template: pipeline-templates/deploy.yml parameters: environmentType: Test # Deploy to the production environment. - template: pipeline-templates/deploy.yml parameters: environmentType: Production
保存对该文件所做的更改。
更新版本验收测试文件
网站开发人员向网站添加了运行状况终结点。 此终结点检查网站是否联机,以及其能否访问数据库。 在这里,你将添加新的版本验收测试,以从部署管道调用运行状况检查。
打开 deploy 文件夹中的 Website.Tests.ps1 文件。
添加调用运行状况检查的新测试用例。 如果响应代码不是表示成功的 200,则测试用例失败。
param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $HostName ) Describe 'Toy Website' { It 'Serves pages over HTTPS' { $request = [System.Net.WebRequest]::Create("https://$HostName/") $request.AllowAutoRedirect = $false $request.GetResponse().StatusCode | Should -Be 200 -Because "the website requires HTTPS" } It 'Does not serves pages over HTTP' { $request = [System.Net.WebRequest]::Create("http://$HostName/") $request.AllowAutoRedirect = $false $request.GetResponse().StatusCode | Should -BeGreaterOrEqual 300 -Because "HTTP is not secure" } It 'Returns a success code from the health check endpoint' { $response = Invoke-WebRequest -Uri "https://$HostName/health" -SkipHttpErrorCheck Write-Host $response.Content $response.StatusCode | Should -Be 200 -Because "the website and configuration should be healthy" } }
保存对该文件所做的更改。
将输出添加到 Bicep 文件
你需要添加一个将网站发布到 Azure 应用服务的部署步骤,但该发布步骤需要使用该应用服务应用的名称。 在这里,你将应用名称公开为 Bicep 文件的输出。
打开 deploy 文件夹中的 main.bicep 文件。
在文件内容的末尾,添加该应用服务应用的名称作为输出。
output appServiceAppName string = appServiceApp.name output appServiceAppHostName string = appServiceApp.properties.defaultHostName
保存对该文件所做的更改。
更新部署阶段
打开 deploy/pipeline-templates 文件夹中的 deploy.yml 文件。
在部署阶段的部署作业的定义(第 59 行附近)中,将作业配置为使用 Windows 托管代理池:
- stage: Deploy_${{parameters.environmentType}} displayName: Deploy (${{parameters.environmentType}} Environment) jobs: - deployment: DeployWebsite displayName: Deploy website pool: vmImage: windows-latest variables: - group: ToyWebsite${{parameters.environmentType}} environment: ${{parameters.environmentType}} strategy:
稍后添加的一些用以处理数据库的管道步骤将需要运行 Windows 操作系统。 可以为管道中的不同作业使用不同的代理池,以便其他作业继续使用 Ubuntu Linux 管道代理池。
在部署作业的 SaveDeploymentOutputs 步骤中,添加一个新的管道变量,其值为 Bicep 部署输出的应用名称:
- bash: | echo "##vso[task.setvariable variable=appServiceAppName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppName.value')" echo "##vso[task.setvariable variable=appServiceAppHostName;isOutput=true]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppHostName.value')" name: SaveDeploymentOutputs displayName: Save deployment outputs into variables env: DEPLOYMENT_OUTPUTS: $(deploymentOutputs)
请注意,
appServiceAppHostName
变量应用了isOutput=true
属性,因为该变量用于版本验收测试阶段。appServiceAppName
变量将在同一管道阶段和作业中设置和使用。 因此,它不需要isOutput=true
设置。在部署作业内容的末尾,添加一个新步骤以将应用部署到 Azure 应用服务:
steps: - checkout: self - task: AzureResourceManagerTemplateDeployment@3 name: DeployBicepFile displayName: Deploy Bicep file inputs: connectedServiceName: ToyWebsite${{parameters.environmentType}} deploymentName: $(Build.BuildNumber) location: ${{parameters.deploymentDefaultLocation}} resourceGroupName: $(ResourceGroupName) csmFile: deploy/main.bicep overrideParameters: > -environmentType $(EnvironmentType) -reviewApiUrl $(ReviewApiUrl) -reviewApiKey $(ReviewApiKey) deploymentOutputs: deploymentOutputs - bash: | echo "##vso[task.setvariable variable=appServiceAppName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppName.value')" echo "##vso[task.setvariable variable=appServiceAppHostName;isOutput=true]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppHostName.value')" name: SaveDeploymentOutputs displayName: Save deployment outputs into variables env: DEPLOYMENT_OUTPUTS: $(deploymentOutputs) - task: AzureRmWebAppDeployment@4 name: DeployWebsiteApp displayName: Deploy website inputs: appType: webApp ConnectionType: AzureRM azureSubscription: ToyWebsite${{parameters.environmentType}} ResourceGroupName: $(ResourceGroupName) WebAppName: $(appServiceAppName) Package: '$(Pipeline.Workspace)/website/publish.zip'
注意
请注意 YAML 文件的缩进,确保新部署步骤与
DeployBicepFile
步骤在同一级别缩进。 如果不确定,请从下一步的示例中复制整个 deploy.yml 文件内容。请注意,在管道定义中未显式下载工件。 由于使用了部署作业,Azure Pipelines 会自动下载工件。
验证 deploy.yml 文件内容,并提交更改
验证 deploy.yml 文件是否如以下示例所示:
parameters: - name: environmentType type: string - name: deploymentDefaultLocation type: string default: westus3 stages: - ${{ if ne(parameters.environmentType, 'Production') }}: - stage: Validate_${{parameters.environmentType}} displayName: Validate (${{parameters.environmentType}} Environment) jobs: - job: ValidateBicepCode displayName: Validate Bicep code variables: - group: ToyWebsite${{parameters.environmentType}} steps: - task: AzureResourceManagerTemplateDeployment@3 name: RunPreflightValidation displayName: Run preflight validation inputs: connectedServiceName: ToyWebsite${{parameters.environmentType}} location: ${{parameters.deploymentDefaultLocation}} deploymentMode: Validation resourceGroupName: $(ResourceGroupName) csmFile: deploy/main.bicep overrideParameters: > -environmentType $(EnvironmentType) -reviewApiUrl $(ReviewApiUrl) -reviewApiKey $(ReviewApiKey) - ${{ if eq(parameters.environmentType, 'Production') }}: - stage: Preview_${{parameters.environmentType}} displayName: Preview (${{parameters.environmentType}} Environment) jobs: - job: PreviewAzureChanges displayName: Preview Azure changes variables: - group: ToyWebsite${{parameters.environmentType}} steps: - task: AzureCLI@2 name: RunWhatIf displayName: Run what-if inputs: azureSubscription: ToyWebsite${{parameters.environmentType}} scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | az deployment group what-if \ --resource-group $(ResourceGroupName) \ --template-file deploy/main.bicep \ --parameters environmentType=$(EnvironmentType) \ reviewApiUrl=$(ReviewApiUrl) \ reviewApiKey=$(ReviewApiKey) - stage: Deploy_${{parameters.environmentType}} displayName: Deploy (${{parameters.environmentType}} Environment) jobs: - deployment: DeployWebsite displayName: Deploy website pool: vmImage: windows-latest variables: - group: ToyWebsite${{parameters.environmentType}} environment: ${{parameters.environmentType}} strategy: runOnce: deploy: steps: - checkout: self - task: AzureResourceManagerTemplateDeployment@3 name: DeployBicepFile displayName: Deploy Bicep file inputs: connectedServiceName: ToyWebsite${{parameters.environmentType}} deploymentName: $(Build.BuildNumber) location: ${{parameters.deploymentDefaultLocation}} resourceGroupName: $(ResourceGroupName) csmFile: deploy/main.bicep overrideParameters: > -environmentType $(EnvironmentType) -reviewApiUrl $(ReviewApiUrl) -reviewApiKey $(ReviewApiKey) deploymentOutputs: deploymentOutputs - bash: | echo "##vso[task.setvariable variable=appServiceAppName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppName.value')" echo "##vso[task.setvariable variable=appServiceAppHostName;isOutput=true]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppHostName.value')" name: SaveDeploymentOutputs displayName: Save deployment outputs into variables env: DEPLOYMENT_OUTPUTS: $(deploymentOutputs) - task: AzureRmWebAppDeployment@4 name: DeployWebsiteApp displayName: Deploy website inputs: appType: webApp ConnectionType: AzureRM azureSubscription: ToyWebsite${{parameters.environmentType}} ResourceGroupName: $(ResourceGroupName) WebAppName: $(appServiceAppName) Package: '$(Pipeline.Workspace)/website/publish.zip' - stage: SmokeTest_${{parameters.environmentType}} displayName: Smoke Test (${{parameters.environmentType}} Environment) jobs: - job: SmokeTest displayName: Smoke test variables: appServiceAppHostName: $[ stageDependencies.Deploy_${{parameters.environmentType}}.DeployWebsite.outputs['DeployWebsite.SaveDeploymentOutputs.appServiceAppHostName'] ] steps: - task: PowerShell@2 name: RunSmokeTests displayName: Run smoke tests inputs: targetType: inline script: | $container = New-PesterContainer ` -Path 'deploy/Website.Tests.ps1' ` -Data @{ HostName = '$(appServiceAppHostName)' } Invoke-Pester ` -Container $container ` -CI - task: PublishTestResults@2 name: PublishTestResults displayName: Publish test results condition: always() inputs: testResultsFormat: NUnit testResultsFiles: 'testResults.xml'
保存对该文件所做的更改。
在 Visual Studio Code 终端中,运行以下命令来提交更改并将其推送到 Git 存储库:
git add . git commit -m "Build and deploy website application" git push
运行管道
在浏览器中,转到“管道”。
选择管道的最新运行。
等待生成阶段成功完成。
管道在运行“验证(测试环境)”阶段之前暂停,因为管道需要权限才能使用阶段引用的变量组。 你需要批准管道对变量组的访问权限,因为这是你首次在此项目中运行管道。 再次运行管道时,就无需再批准对同一变量组的访问权限。
选择“视图”。
选择“允许”。
选择“允许”。
“验证(测试环境)”阶段已成功完成。
管道继续运行,“部署(测试环境)”阶段成功完成。 管道然后运行“版本验收测试(测试环境)”阶段,但版本验收测试阶段失败。
选择“版本验收测试(测试环境)”阶段,打开管道日志。
选择“运行版本验收测试”步骤,查看管道日志的关联部分。
请注意,管道日志包含运行状况检查响应。 响应表明应用程序与 Azure SQL 数据库的通信存在问题。 数据库尚未部署或配置,因此网站还无法对其进行访问。 在下一个练习中,你将修复此配置问题。