练习 - 将多个环境添加到管道

已完成

现在可以更新管道,以部署到测试和生产环境。 在本单元中,你将更新管道以使用模板,以便可以跨环境重用阶段。

在此过程中,你将:

  • 为 Lint 分析阶段添加管道模板。
  • 添加一个管道模板,用于定义部署到任何环境所需的阶段。
  • 更新管道以使用模板。
  • 运行管道并查看结果。

为 Lint 分析阶段添加管道模板

无论管道部署到多少个环境,Lint 分析阶段在管道运行期间只发生一次。 因此,实际上不需要为 Lint 分析阶段使用模板。 但是为了保持主管道定义文件简单易读,你决定在模板中定义 Lint 分析阶段。

  1. 在 Visual Studio Code 中,在 deploy 文件夹中创建一个名为 pipeline-templates 的新文件夹。

  2. 在 pipeline-templates 文件夹中创建一个名为 lint.yml 的新文件。

    Visual Studio Code 资源管理器的屏幕截图,其中显示了pipeline-templates 文件夹和 lint.yml 文件。

  3. 将以下管道模板定义粘贴到文件中:

    jobs:
    - job: LintCode
      displayName: Lint code
      steps:
        - script: |
            az bicep build --file deploy/main.bicep
          name: LintBicepCode
          displayName: Run Bicep linter
    

    Lint 分析阶段与管道中已有的 Lint 分析阶段相同,但现在它位于单独的管道模板文件中。

  4. 保存更改并关闭该文件。

添加用于部署的管道模板

创建一个管道模板,用于定义部署每个环境所需的所有阶段。 你需要使用模板参数来指定环境之间可能不同的设置。

  1. 在 pipeline-templates 文件夹中创建一个名为 deploy.yml 的新文件。

    Visual Studio Code 资源管理器的屏幕截图,其中显示了 pipeline-templates 文件夹和 deploy.yml 文件。

    此文件代表针对每个环境运行的所有部署活动。

  2. 将以下管道模板参数粘贴到文件中:

    parameters:
    - name: environmentType
      type: string
    - name: resourceGroupName
      type: string
    - name: serviceConnectionName
      type: string
    - name: deploymentDefaultLocation
      type: string
      default: westus3
    

    注意

    当你开始在 Visual Studio Code 中使用 YAML 文件时,可能会看到一些红色波浪线,指示存在问题。 这是因为 YAML 文件的 Visual Studio Code 扩展有时会错误地猜测文件的架构。

    可以忽略扩展报告的问题。 或者,如果你愿意,可以将以下代码添加到文件顶部以禁用扩展的猜测:

    # yaml-language-server: $schema=./deploy.yml
    
  3. 在参数下方,粘贴验证阶段的定义:

    stages:
    
    - ${{ if ne(parameters.environmentType, 'Production') }}:
      - stage: Validate_${{parameters.environmentType}}
        displayName: Validate (${{parameters.environmentType}} Environment)
        jobs:
        - job: ValidateBicepCode
          displayName: Validate Bicep code
          steps:
            - task: AzureResourceManagerTemplateDeployment@3
              name: RunPreflightValidation
              displayName: Run preflight validation
              inputs:
                connectedServiceName: ${{parameters.serviceConnectionName}}
                location: ${{parameters.deploymentDefaultLocation}}
                deploymentMode: Validation
                resourceGroupName: ${{parameters.resourceGroupName}}
                csmFile: deploy/main.bicep
                overrideParameters: >
                  -environmentType ${{parameters.environmentType}}
    

    请注意,这个阶段应用了一个条件。 它仅针对非生产环境运行。

    另请注意,阶段标识符包括 environmentType 参数的值。 此参数可确保管道中的每个阶段都具有唯一标识符。 该阶段还有一个 displayName 属性,用于创建格式正确的名称供你阅读。

  4. 在验证阶段下方,粘贴预览阶段的定义:

    - ${{ if eq(parameters.environmentType, 'Production') }}:
      - stage: Preview_${{parameters.environmentType}}
        displayName: Preview (${{parameters.environmentType}} Environment)
        jobs:
        - job: PreviewAzureChanges
          displayName: Preview Azure changes
          steps:
            - task: AzureCLI@2
              name: RunWhatIf
              displayName: Run what-if
              inputs:
                azureSubscription: ${{parameters.serviceConnectionName}}
                scriptType: 'bash'
                scriptLocation: 'inlineScript'
                inlineScript: |
                  az deployment group what-if \
                    --resource-group ${{parameters.resourceGroupName}} \
                    --template-file deploy/main.bicep \
                    --parameters environmentType=${{parameters.environmentType}}
    

    请注意,这个阶段也应用了一个条件,但它与验证阶段的条件相反。 预览阶段仅针对生产环境运行。

  5. 在预览阶段下方,粘贴部署阶段的定义:

    - stage: Deploy_${{parameters.environmentType}}
      displayName: Deploy (${{parameters.environmentType}} Environment)
      jobs:
      - deployment: DeployWebsite
        displayName: Deploy website
        environment: ${{parameters.environmentType}}
        strategy:
          runOnce:
            deploy:
              steps:
                - checkout: self
    
                - task: AzureResourceManagerTemplateDeployment@3
                  name: DeployBicepFile
                  displayName: Deploy Bicep file
                  inputs:
                    connectedServiceName: ${{parameters.serviceConnectionName}}
                    deploymentName: $(Build.BuildNumber)
                    location: ${{parameters.deploymentDefaultLocation}}
                    resourceGroupName: ${{parameters.resourceGroupName}}
                    csmFile: deploy/main.bicep
                    overrideParameters: >
                      -environmentType ${{parameters.environmentType}}
                    deploymentOutputs: deploymentOutputs
    
                - bash: |
                    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)
    
  6. 在部署阶段下方,粘贴版本验收测试阶段的定义:

    - 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'
    

    请注意,appServiceAppHostName 变量定义在引用发布主机名的阶段时合并了 environmentType 参数。 此参数可确保每个版本验收测试阶段都针对正确的环境运行。

  7. 验证 deploy.yml 文件是否如以下示例所示:

    parameters:
    - name: environmentType
      type: string
    - name: resourceGroupName
      type: string
    - name: serviceConnectionName
      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
          steps:
            - task: AzureResourceManagerTemplateDeployment@3
              name: RunPreflightValidation
              displayName: Run preflight validation
              inputs:
                connectedServiceName: ${{parameters.serviceConnectionName}}
                location: ${{parameters.deploymentDefaultLocation}}
                deploymentMode: Validation
                resourceGroupName: ${{parameters.resourceGroupName}}
                csmFile: deploy/main.bicep
                overrideParameters: >
                  -environmentType ${{parameters.environmentType}}
    
    - ${{ if eq(parameters.environmentType, 'Production') }}:
      - stage: Preview_${{parameters.environmentType}}
        displayName: Preview (${{parameters.environmentType}} Environment)
        jobs:
        - job: PreviewAzureChanges
          displayName: Preview Azure changes
          steps:
            - task: AzureCLI@2
              name: RunWhatIf
              displayName: Run what-if
              inputs:
                azureSubscription: ${{parameters.serviceConnectionName}}
                scriptType: 'bash'
                scriptLocation: 'inlineScript'
                inlineScript: |
                  az deployment group what-if \
                    --resource-group ${{parameters.resourceGroupName}} \
                    --template-file deploy/main.bicep \
                    --parameters environmentType=${{parameters.environmentType}}
    
    - stage: Deploy_${{parameters.environmentType}}
      displayName: Deploy (${{parameters.environmentType}} Environment)
      jobs:
      - deployment: DeployWebsite
        displayName: Deploy website
        environment: ${{parameters.environmentType}}
        strategy:
          runOnce:
            deploy:
              steps:
                - checkout: self
    
                - task: AzureResourceManagerTemplateDeployment@3
                  name: DeployBicepFile
                  displayName: Deploy Bicep file
                  inputs:
                    connectedServiceName: ${{parameters.serviceConnectionName}}
                    deploymentName: $(Build.BuildNumber)
                    location: ${{parameters.deploymentDefaultLocation}}
                    resourceGroupName: ${{parameters.resourceGroupName}}
                    csmFile: deploy/main.bicep
                    overrideParameters: >
                      -environmentType ${{parameters.environmentType}}
                    deploymentOutputs: deploymentOutputs
    
                - bash: |
                    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)
    
    - 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'
    
  8. 保存对该文件所做的更改。

更新管道定义以使用模板

  1. 打开“azure pipelines.yml”文件

  2. 通过使用以下代码替换内容来更新文件以使用新模板:

    trigger:
      batch: true
      branches:
        include:
        - main
    
    pool:
      vmImage: ubuntu-latest
    
    stages:
    
    # Lint the Bicep file.
    - stage: Lint
      jobs: 
      - template: pipeline-templates/lint.yml
    
    # Deploy to the test environment.
    - template: pipeline-templates/deploy.yml
      parameters:
        environmentType: Test
        resourceGroupName: ToyWebsiteTest
        serviceConnectionName: ToyWebsiteTest
    
    # Deploy to the production environment.
    - template: pipeline-templates/deploy.yml
      parameters:
        environmentType: Production
        resourceGroupName: ToyWebsiteProduction
        serviceConnectionName: ToyWebsiteProduction
    

    该管道运行一次 Lint 分析阶段。 然后它使用 deploy.yml 模板文件两次:每个环境一次。 这使得管道定义清晰易懂。 此外,注释还有助于解释发生的情况。

  3. 保存所做更改。

  4. 通过在 Visual Studio Code 终端中运行以下命令来提交更改并将其推送到 Git 存储库:

    git add .
    git commit -m "Add pipeline templates"
    git push
    

查看管道运行

  1. 在浏览器中,转到“管道”。

  2. 选择管道的最新运行。

    请注意,管道运行现在显示了你在 YAML 文件中定义的所有阶段。 可能需要水平滚动才能看到所有这些内容。

    显示管道运行阶段的 Azure Pipelines 屏幕截图。

  3. 在部署(生产环境)阶段之前等待管道暂停。 管道可能需要几分钟时间才能达到这一点。

    显示管道运行暂停以供审批的 Azure Pipelines 屏幕截图。

  4. 通过选择“审核”按钮批准部署到生产环境。

  5. 选择“批准”按钮。

    Azure DevOps 界面的屏幕截图,其中显示了管道审批页面和“批准”按钮。

    等待管道完成运行。

  6. 选择“测试”选项卡以显示此管道运行的测试结果。

    请注意,现在有四个测试结果。 版本验收测试会在测试和生产这两个环境中运行,因此你可以看到两组测试的结果。

    Azure Pipelines 的屏幕截图,其中显示了用于管道运行测试的页面,包含四个测试结果。

  7. 选择“管道”>“环境”。

  8. 选择生产环境。

  9. 请注意,在环境详细信息屏幕上,你会看到生产环境部署历史记录的概述。

    Azure Pipelines 的屏幕截图,其中显示了生产环境,包含显示单个部署的部署历史记录。

  10. 选择该部署,然后选择“更改”选项卡。

    请注意,“更改”选项卡显示了部署中包含的提交列表。 此信息可帮助你准确了解环境中一段时间的变化。

    Azure Pipelines 的屏幕截图,其中显示了生产环境的部署详细信息,包含提交列表。

  11. 在浏览器中转到 Azure 门户

  12. 请转到 ToyWebsiteProduction 资源组。

  13. 在资源列表中,打开 Azure 应用服务应用。

    Azure 门户的屏幕截图,其中显示了生产应用服务应用和应用服务计划 SKU 详细信息。

    请注意,应用服务计划的类型为 S1。

  14. 转到 ToyWebsiteTest 资源组中的应用服务应用。

    请注意,应用服务计划的类型为 F1。 正如你在 Bicep 文件中定义的那样,这两种环境使用不同的设置。