Exercício – implantar um aplicativo Web

Concluído

Na sua empresa de brinquedos, a equipe de desenvolvimento do site confirmou a versão mais recente do site no seu repositório Git. Agora, você está pronto para atualizar seu pipeline para criar o site e para implantá-lo no Serviço de Aplicativo do Azure.

Nesse processo, execute as seguintes tarefas:

  • Adicionar um modelo de pipeline para o trabalho de build.
  • Atualize o pipeline para incluir o trabalho de build.
  • Adicione um smoke test.
  • Atualize a fase de implantação para implantar o aplicativo.
  • Execute o pipeline.

Adicionar um modelo de pipeline para o trabalho de build

Adicione uma nova definição de trabalho que contém as etapas necessárias para criar o aplicativo de site.

  1. Abra o Visual Studio Code.

  2. Na pasta deploy/pipeline-templates, crie um arquivo chamado build.yml.

    Screenshot of Visual Studio Code Explorer, with the pipeline-templates folder and the 'build.yml' file shown.

  3. Adicione o seguinte conteúdo ao arquivo de modelo de pipeline 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'
    

    O trabalho executa uma etapa de build para transformar o código-fonte do aplicativo de site em um arquivo compilado pronto para ser executado no Azure. O trabalho então copia o artefato em uma pasta de preparo temporária e o publica como um artefato de pipeline.

  4. Salve as alterações no arquivo.

Renomear a primeira fase do pipeline e adicionar um trabalho de build

  1. Abra o arquivo azure-pipelines.yml na pasta deploy.

  2. Modifique a fase lint. Renomeie-a para Build e adicione um trabalho de compilação que use o modelo de pipeline build.yml que você criou.

    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
    
  3. Salve as alterações no arquivo.

Atualizar o arquivo de smoke test

Os desenvolvedores do site adicionaram um ponto de extremidade de integridade ao site. Esse ponto de extremidade verifica se o site está online e se ele pode acessar o banco de dados. Aqui, você adiciona um smoke test para invocar a verificação de integridade do seu pipeline de implantação.

  1. Abra o arquivo Website.Tests.ps1 na pasta implantar.

  2. Adicione um caso de teste que invoca a verificação de integridade. O caso de teste falhará se o código de resposta não for 200, o que indica êxito.

    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"
        }
    
    }
    
  3. Salve as alterações no arquivo.

Adicionar saída ao arquivo Bicep

Você deseja adicionar uma etapa de implantação que publique seu site no Serviço de Aplicativo do Azure, mas a etapa de publicação requer o nome do aplicativo do Serviço de Aplicativo. Aqui, você expõe o nome do aplicativo como uma saída do arquivo Bicep.

  1. Abra o arquivo main.bicep na pasta deploy.

  2. No final do conteúdo do arquivo, adicione o nome do aplicativo do Serviço de Aplicativo como uma saída.

    output appServiceAppName string = appServiceApp.name
    output appServiceAppHostName string = appServiceApp.properties.defaultHostName
    
  3. Salve as alterações no arquivo.

Atualizar fase de implantação

  1. Abra o arquivo deploy.yml na pasta deploy/pipeline-templates.

  2. Na definição do trabalho de implantação da fase Implantar (próximo à linha 59), configure o trabalho para usar o pool de agentes hospedados do 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:
    

    Algumas das etapas do pipeline que posteriormente adicionadas para trabalhar com o banco de dados exigem a execução do sistema operacional Windows. Você pode usar pools de agente diferentes para trabalhos diferentes em seu pipeline para que os outros trabalhos continuem a usar o pool de agente de pipeline Ubuntu Linux.

  3. Na etapa SaveDeploymentOutputs do trabalho Implantar, adicione uma variável de pipeline com o valor do nome do aplicativo da saída da implantação do 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)
    

    Observe que a variável appServiceAppHostName tem a propriedade isOutput=true aplicada a ela, pois essa variável é usada na fase de smoke test. A variável appServiceAppName é definida e usada na mesma fase do pipeline e no mesmo trabalho. Portanto, ela não precisa da configuração isOutput=true.

  4. No final do conteúdo do trabalho Implantar, adicione uma etapa para implantar o aplicativo no Serviço de Aplicativo do 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'
    

    Observação

    Tenha cuidado com o recuo do arquivo YAML, garantindo que a nova etapa de implantação seja recuada no mesmo nível que a etapa DeployBicepFile. Se você não tiver certeza, copie todo o conteúdo do arquivo deploy.yml do exemplo na próxima etapa.

    Observe que você não fez o download explicitamente do artefato na definição do pipeline. Como você usa um trabalho de implantação, o Azure Pipelines baixa automaticamente o artefato para você.

Verifique o conteúdo do arquivo deploy.yml e faça o commit de suas alterações

  1. Verifique se o arquivo deploy.yml se parece com o seguinte exemplo:

    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'
    
  2. Salve as alterações no arquivo.

  3. No terminal do Visual Studio Code, faça commit e efetue push das alterações no repositório Git executando os seguintes comandos:

    git add .
    git commit -m "Build and deploy website application"
    git push
    

Executar o pipeline

  1. No navegador, acesse Pipelines.

  2. Selecione a execução mais recente do pipeline.

    Screenshot of Azure DevOps showing the pipeline run list. The latest pipeline run is highlighted.

    Aguarde até que a fase de Build seja concluída com êxito.

    O pipeline pausa antes de executar a fase Validar (Ambiente de Teste) porque o pipeline precisa de permissão para usar o grupo de variáveis ao qual a fase se refere. Você precisa aprovar o acesso do pipeline ao grupo de variáveis, pois está executando o pipeline neste projeto pela primeira vez. Quando você executar o pipeline novamente, não precisará aprovar o acesso ao mesmo grupo de variáveis.

  3. Selecione Modo de Exibição.

    Screenshot of Azure DevOps showing the pipeline run paused at the Validate stage. Permission is required to continue. The View button is highlighted.

  4. Selecione Permitir.

    Screenshot of Azure DevOps showing that the pipeline needs permission to use the ToyWebsiteTest variable group. The Permit button is highlighted.

  5. Selecione Permitir.

    Screenshot of Azure DevOps showing the permission confirmation interface. The Permit button is highlighted.

    A fase Validar (Ambiente de Teste) é concluída com êxito.

    O pipeline continua e a fase Implantar (Ambiente de Teste) é concluída com êxito. O pipeline então executa a fase Smoke Test (Ambiente de Teste), mas a fase smoke test falha.

  6. Selecione a fase Smoke Test (Ambiente de Teste) para abrir o log do pipeline.

    Screenshot of Azure DevOps showing the pipeline run's Smoke Test stage for the test environment. The status shows that the stage failed.

  7. Selecione a etapa Executar smoke tests para exibir a seção associada do log do pipeline.

    Screenshot of Azure DevOps showing the pipeline run log, with the output of the smoke test displayed. The JSON health test result is highlighted.

    Observe que o log de pipeline inclui a resposta da verificação de integridade. A resposta indica que há um problema com a comunicação do aplicativo com Banco de Dados SQL do Azure. O banco de dados ainda não foi implantado ou configurado, e é por isso que o site não consegue acessá-lo. No próximo exercício, você corrigirá esse problema de configuração.