연습 - 파이프라인에 여러 환경 추가

완료됨

이제 테스트 및 프로덕션 환경 모두에 배포하도록 파이프라인을 업데이트할 준비가 되었습니다. 본 단원에서는 여러 환경에 걸쳐 스테이지를 재사용하기 위해 템플릿을 사용하도록 파이프라인을 업데이트합니다.

프로세스 중에 다음을 수행합니다.

  • 린트 스테이지에 대한 파이프라인 템플릿을 추가합니다.
  • 환경에 배포하는 데 필요한 스테이지를 정의하는 파이프라인 템플릿을 추가합니다.
  • 템플릿을 사용하도록 파이프라인을 업데이트합니다.
  • 파이프라인을 실행하고 결과를 확인합니다.

린트 스테이지에 대한 파이프라인 템플릿 추가

린트 스테이지는 파이프라인이 배포하는 환경의 수에 관계없이 파이프라인 실행 중에 한 번만 발생합니다. 따라서 린트 스테이지에 템플릿을 사용할 필요가 없습니다. 그러나 기본 파이프라인 정의 파일을 간단하고 읽기 쉽게 유지하기 위해 템플릿에서 린트 스테이지를 정의하려고 합니다.

  1. Visual Studio Code에서 ‘deploy’ 폴더 안에 ‘pipeline-templates’라는 새 폴더를 만듭니다.

  2. ‘pipeline-templates’ 폴더에 ‘lint.yml’이라는 새 파일을 만듭니다.

    Screenshot of Visual Studio Code Explorer, with the pipeline-templates folder and the lint dot Y M L file.

  3. 다음 파이프라인 템플릿 정의를 파일에 붙여넣습니다.

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

    이 린트 스테이지는 파이프라인에 이미 있는 린트 스테이지와 동일하지만 이제는 별도의 파이프라인 템플릿 파일에 있습니다.

  4. 변경 내용을 저장하고 파일을 닫습니다.

배포용 파이프라인 템플릿 추가

각 환경을 배포하는 데 필요한 모든 스테이지를 정의하는 파이프라인 템플릿을 만듭니다. 템플릿 매개 변수를 사용하여 환경 간에 다를 수 있는 설정을 지정합니다.

  1. ‘pipeline-templates’ 폴더에 ‘deploy.yml’이라는 새 파일을 만듭니다.

    Screenshot of Visual Studio Code Explorer, with the pipeline-templates folder and the deploy dot YML file.

    이 파일은 각 환경에 대해 실행되는 모든 배포 작업을 나타냅니다.

  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
    

    이 파이프라인은 린트 스테이지를 한 번 실행합니다. 그런 다음, ‘deploy.yml’ 템플릿 파일을 환경당 한 번씩, 총 두 번 사용합니다. 이렇게 하면 파이프라인 정의를 명확하고 쉽게 이해할 수 있습니다. 또한 주석은 무슨 일이 발생하는지 설명하는 데 도움이 될 수 있습니다.

  3. 변경 내용을 저장합니다.

  4. Visual Studio Code 터미널에서 다음 명령을 실행하여 변경 내용을 커밋하고 Git 리포지토리에 푸시합니다.

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

파이프라인 실행 보기

  1. 브라우저에서 Pipelines로 이동합니다.

  2. 가장 최근에 실행한 파이프라인을 선택합니다.

    이제 YAML 파일에서 정의한 모든 스테이지가 파이프라인 실행에 표시됩니다. 모두 보려면 가로로 스크롤해야 할 수 있습니다.

    Screenshot of Azure Pipelines that shows the pipeline run stages.

  3. 배포(프로덕션 환경) 스테이지 전에 파이프라인이 일시 중지될 때까지 기다립니다. 파이프라인이 이 지점에 도달하는 데는 몇 분 정도 걸릴 수 있습니다.

    Screenshot of Azure Pipelines that shows the pipeline run paused for approval.

  4. 검토 단추를 선택하여 프로덕션 환경에 대한 배포를 승인합니다.

  5. 승인 단추를 선택합니다.

    Screenshot of the Azure DevOps interface that shows the pipeline approval page and the Approve button.

    파이프라인이 실행을 마칠 때까지 기다립니다.

  6. 테스트 탭을 선택하여 이 파이프라인 실행의 테스트 결과를 표시합니다.

    이제 4개의 테스트 결과가 있습니다. 스모크 테스트는 테스트 환경과 프로덕션 환경 모두에서 실행되므로 두 테스트 집합에 대한 결과를 확인할 수 있습니다.

    Screenshot of Azure Pipelines that shows the page for pipeline run tests, with four test results.

  7. Pipelines>환경을 선택합니다.

  8. 프로덕션 환경을 선택합니다.

  9. 환경 세부 정보 화면에 프로덕션 환경의 배포 기록에 대한 개요가 표시됩니다.

    Screenshot of Azure Pipelines that shows the production environment, with the deployment history showing a single deployment.

  10. 배포를 선택하고 변경 내용 탭을 선택합니다.

    변경 내용 탭에는 배포에 포함된 커밋 목록이 표시됩니다. 이 정보를 통해 시간이 지남에 따라 환경에서 변경된 내용을 정확히 확인할 수 있습니다.

    Screenshot of Azure Pipelines that shows the production environment's deployment details, with a list of commits.

  11. 브라우저에서 Azure Portal로 이동합니다.

  12. ToyWebsiteProduction 리소스 그룹으로 이동합니다.

  13. 리소스 목록에서 Azure App Service 앱을 엽니다.

    Screenshot of the Azure portal that shows the production App Service app and the App Service plan SKU details.

    App Service 요금제 유형은 S1입니다.

  14. ToyWebsiteTest 리소스 그룹의 App Service 앱으로 이동합니다.

    App Service 요금제 유형은 F1입니다. Bicep 파일에서 정의한 대로 두 환경에서 서로 다른 설정을 사용합니다.