Упражнение. Развертывание веб-приложения

Завершено

В вашей компании по производству игрушек команда разработчиков закоммитила последнюю версию веб-сайта в ваш репозиторий Git. Теперь вы готовы обновить конвейер для сборки веб-сайта и развернуть его в Службе приложений Azure.

В этом процессе выполняются следующие задачи:

  • Добавьте новый шаблон конвейера для задания сборки.
  • Обновите конвейер, чтобы включить задание сборки.
  • Добавьте новый тест дыма.
  • Обновите этап развертывания, чтобы развернуть приложение.
  • Запустите конвейер.

Добавление шаблона конвейера для задания сборки

Добавьте новое определение задания, содержащее шаги, необходимые для создания приложения веб-сайта.

  1. Откройте Visual Studio Code.

  2. В папке deploy/pipeline-templates создайте новый файл с именем build.yml.

    снимок экрана обозревателя Visual Studio Code с папкой шаблонов конвейеров и файлом build.yml.

  3. Добавьте следующее содержимое в файл шаблона конвейера 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. Затем задание копирует скомпилированный артефакт во временную промежуточную папку и публикует его в качестве артефакта конвейера.

  4. Сохраните изменения в файле.

Переименуйте первый этап конвейера и добавьте задание сборки

  1. Откройте файл azure-pipelines.yml в папке deploy.

  2. Подкорректируйте этап 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
    
  3. Сохраните изменения в файле.

Обновление файла теста дыма

Разработчики добавили на веб-сайт проверку состояния. Эта конечная точка проверяет, находится ли веб-сайт в сети и что он может получить доступ к базе данных. Здесь вы добавите новый тест дыма для вызова проверки работоспособности из конвейера развертывания.

  1. Откройте файл Website.Tests.ps1 в папке развертывания.

  2. Добавьте новый тестовый случай, вызывающий проверку работоспособности. Тестовый случай завершается ошибкой, если код ответа не равен 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"
        }
    
    }
    
  3. Сохраните изменения в файле.

Добавление выходных данных в файл Bicep

Вы хотите добавить шаг развертывания, который публикует веб-сайт в Службе приложений Azure, но для шага публикации требуется имя приложения службы приложений. Здесь вы предоставляете имя приложения в виде выходных данных из файла Bicep.

  1. Откройте файл main.bicep в папке deploy.

  2. В конце содержимого файла добавьте имя приложения службы приложений в качестве выходных данных.

    output appServiceAppName string = appServiceApp.name
    output appServiceAppHostName string = appServiceApp.properties.defaultHostName
    
  3. Сохраните изменения в файле.

Этап развертывания обновления

  1. Откройте файл deploy.yml в папке deploy/pipeline-templates.

  2. В определении задания развертывания этапа Deploy (около строки 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.

  3. На этапе Deploy в задаче 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.

  4. В конце содержимого задания развертывания добавьте новый шаг для развертывания приложения в Azure App Service:

    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 и зафиксируйте изменения

  1. Убедитесь, что файл 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'
    
  2. Сохраните изменения в файле.

  3. В терминале Visual Studio Code зафиксируйте и отправьте изменения в репозиторий Git, выполнив следующие команды:

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

Запуск конвейера

  1. В браузере перейдите к Конвейеры.

  2. Выберите последний запуск конвейера.

    снимок экрана Azure DevOps с списком выполнения конвейера. Выделен последний запуск конвейера.

    Дождитесь успешного завершения этапа сборки.

    Конвейер приостанавливается перед запуском этапа проверки (тестовая среда), так как конвейеру требуется разрешение на использование группы переменных, на которую ссылается этап. Необходимо одобрить доступ конвейера к группе переменных, так как вы впервые запускаете конвейер в этом проекте. При повторном запуске конвейера не нужно утверждать доступ к той же группе переменных.

  3. Выберите вид.

    снимок экрана Azure DevOps с приостановкой выполнения конвейера на этапе валидации. Для продолжения требуется разрешение. Выделена кнопка

  4. Выберите Разрешение.

    снимок экрана Azure DevOps, показывающий, что конвейеру требуется разрешение на использование группы переменных ToyWebsiteTest. Выделена кнопка

  5. Выберите Разрешение.

    снимок экрана Azure DevOps с интерфейсом подтверждения разрешений. Выделена кнопка

    Этап проверки (тестовая среда) завершается успешно.

    Конвейер продолжается, и этап развертывания (тестовая среда) успешно завершается. Затем конвейер запускает тестовый тест (тестовая среда) этап, но этап тестирования дыма завершается сбоем.

  6. Выберите этап теста дыма (тестовая среда), чтобы открыть журнал конвейера.

    снимок экрана Azure DevOps с этапом тестирования дыма конвейера для тестовой среды. Состояние показывает, что этап завершился сбоем.

  7. Выберите Выполнить тесты дыма, чтобы просмотреть связанный раздел журнала конвейера.

    снимок экрана Azure DevOps с журналом выполнения конвейера, в котором отображается результат дымового теста. Выделен результат проверки работоспособности JSON-теста.

    Обратите внимание, что журнал конвейера включает ответ проверки работоспособности. Ответ указывает, что возникла проблема с взаимодействием приложения с базой данных SQL Azure. База данных еще не развернута или настроена, поэтому веб-сайт не может получить к нему доступ. В следующем упражнении вы исправите эту проблему конфигурации.