练习 - 部署 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 中运行的已编译文件。 然后,该作业会将编译后的工件复制到临时暂存文件夹,并将其发布为管道工件。
保存对该文件所做的更改。
重命名第一个管道阶段,并添加构建任务
在部署文件夹中打开azure-pipelines.yml文件。
修改 Lint 阶段。 将其重命名为 “生成”,并添加使用创建的 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 数据库的通信出现问题。 数据库尚未部署或配置,这就是为什么网站无法访问它的原因。 在下一个练习中,你将修复此配置问题。