Ćwiczenie — wdrażanie aplikacji internetowej

Ukończone

W twojej firmie produkującej zabawki, zespół zajmujący się tworzeniem stron internetowych zaangażował najnowszą wersję witryny do repozytorium Git. Teraz możesz zaktualizować potok, aby utworzyć witrynę internetową i wdrożyć ją w usłudze Azure App Service.

W tym procesie wykonasz następujące zadania:

  • Dodaj nowy szablon potoku dla zadania kompilacji.
  • Zaktualizuj pipeline, aby dodać zadanie kompilacji.
  • Dodaj nowy test podstawowy.
  • Zaktualizuj etap wdrażania, aby wdrożyć aplikację.
  • Uruchom potok.

Dodaj szablon potoku dla zadania kompilacji

Dodaj nową definicję zadania zawierającą kroki wymagane do utworzenia aplikacji witryny internetowej.

  1. Otwórz program Visual Studio Code.

  2. W folderze deploy/pipeline-templates utwórz nowy plik o nazwie build.yml.

    Zrzut ekranu eksploratora programu Visual Studio Code z folderem pipeline-templates i wyświetlonym plikiem

  3. Dodaj następującą zawartość do pliku szablonu przepływu pracy 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'
    

    Zadanie uruchamia krok kompilacji, aby przekształcić kod źródłowy aplikacji witryny internetowej w skompilowany plik gotowy do uruchomienia na platformie Azure. Następnie zadanie kopiuje skompilowany artefakt do tymczasowego folderu przejściowego i publikuje go jako artefakt potoku.

  4. Zapisz zmiany w pliku.

Zmień nazwę pierwszego etapu potoku i dodaj zadanie kompilacji.

  1. Otwórz plik azure-pipelines.yml w folderze deploy.

  2. Zmodyfikuj etap lint. Zmień jego nazwę na Buildi dodaj zadanie kompilacji, które wykorzystuje szablon potoku build.yml, który stworzyłeś.

    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. Zapisz zmiany w pliku.

Zaktualizuj plik testu kontrolnego

Deweloperzy strony internetowej dodali do niej punkt końcowy monitorowania stanu. Ten punkt końcowy sprawdza, czy witryna internetowa jest w trybie online i czy może dotrzeć do bazy danych. W tym miejscu dodasz nowy test powierzchniowy w celu wywołania sprawdzenia kondycji z pipeline'u wdrożeniowego.

  1. Otwórz plik Website.Tests.ps1 w folderze deploy.

  2. Dodaj nowy przypadek testowy, który wywołuje kontrolę kondycji. Przypadek testowy kończy się niepowodzeniem, jeśli kod odpowiedzi nie ma wartości 200, co oznacza powodzenie.

    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. Zapisz zmiany w pliku.

Dodawanie danych wyjściowych do pliku Bicep

Chcesz dodać krok wdrażania, który publikuje witrynę internetową w usłudze Azure App Service, ale krok publikowania wymaga nazwy aplikacji usługi App Service. W tym miejscu uwidaczniasz nazwę aplikacji jako dane wyjściowe z pliku Bicep.

  1. Otwórz plik main.bicep w folderze deploy.

  2. Na końcu zawartości pliku dodaj nazwę aplikacji usługi App Service jako dane wyjściowe.

    output appServiceAppName string = appServiceApp.name
    output appServiceAppHostName string = appServiceApp.properties.defaultHostName
    
  3. Zapisz zmiany w pliku.

Etap wdrażania aktualizacji

  1. Otwórz plik deploy.yml w folderze deploy/pipeline-templates.

  2. W definicji zadań wdrożeniowych etapu wdrażania (w wierszu 59) skonfiguruj zadanie do korzystania z puli agentów hostowanych w systemie 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:
    

    Niektóre z kroków potoku, które dodasz później do pracy z bazą danych, wymagają systemu operacyjnego Windows do uruchomienia. Można używać różnych pul agentów dla różnych zadań w swoim potoku, więc inne zadania nadal korzystają z agenta Ubuntu Linux.

  3. W kroku Deploy job's SaveDeploymentOutputs dodaj nową zmienną potoku z wartością nazwy aplikacji z danych wyjściowych wdrożenia 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)
    

    Zwróć uwagę, że zmienna appServiceAppHostName ma zastosowaną właściwość isOutput=true, ponieważ jest używana na etapie testu dymu. Zmienna appServiceAppName jest ustawiana i używana w tej samej fazie i zadaniu potoku. Nie wymaga więc ustawienia isOutput=true.

  4. Na końcu zawartości zadania Deploy dodaj nowy krok w celu wdrożenia aplikacji w usłudze 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'
    

    Notatka

    Zachowaj ostrożność przy wcięciach w pliku YAML, upewniając się, że nowy krok wdrażania jest wcięty na tym samym poziomie co krok DeployBicepFile. Jeśli nie masz pewności, skopiuj całą zawartość pliku deploy.yml z przykładu w następnym kroku.

    Zwróć uwagę, że artefakt nie został jawnie pobrany w definicji potoku. Ponieważ używasz zadania wdrożeniowego, usługa Azure Pipelines automatycznie pobiera artefakt dla Ciebie.

Weryfikowanie zawartości pliku deploy.yml i zatwierdzanie zmian

  1. Sprawdź, czy plik deploy.yml wygląda następująco:

    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. Zapisz zmiany w pliku.

  3. W terminalu programu Visual Studio Code zatwierdź i wypchnij zmiany do repozytorium Git, uruchamiając następujące polecenia:

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

Uruchamianie potoku

  1. W przeglądarce przejdź do Rurociągi.

  2. Wybierz najnowszy przebieg potoku.

    Zrzut ekranu przedstawiający listę przebiegów potoku w usłudze Azure DevOps. Wyróżniono najnowszy przebieg potoku.

    Poczekaj, aż etap kompilacji zakończy się pomyślnie.

    Potok jest wstrzymywany przed uruchomieniem etapu Validate (Test Environment), ponieważ potok musi mieć uprawnienia do używania grupy zmiennych, do której odwołuje się etap. Musisz zatwierdzić dostęp potoku do grupy zmiennych, ponieważ uruchamiasz potok w tym projekcie po raz pierwszy. Gdy ponownie uruchomisz potok, nie musisz zatwierdzać dostępu do tej samej grupy zmiennych.

  3. Wybierz Widok.

    zrzut ekranu usługi Azure DevOps przedstawiający przebieg potoku wstrzymany na etapie weryfikacji. Aby kontynuować, wymagane jest uprawnienie. Przycisk Wyświetl jest wyróżniony.

  4. Wybierz Zezwól.

    Zrzut ekranu usługi Azure DevOps przedstawiający, że pipeline musi mieć uprawnienia do korzystania z grupy zmiennych ToyWebsiteTest. Przycisk Zezwól jest wyróżniony.

  5. Wybierz Zezwól.

    zrzut ekranu usługi Azure DevOps przedstawiający interfejs potwierdzenia uprawnień. Przycisk Zezwól jest wyróżniony.

    Etap weryfikacji (środowisko testowe) zakończy się pomyślnie.

    Potok będzie kontynuowany, a etap wdrażania (środowiska testowego) zakończy się pomyślnie. Następnie pipeline uruchamia test dymny (środowisko testowe) na etapie, ale etap testu dymnego kończy się niepowodzeniem.

  6. Wybierz etap Smoke Test (środowisko testowe), aby otworzyć dziennik dziennika potoku.

    Zrzut ekranu usługi Azure DevOps przedstawiający etap testu weryfikacyjnego (Smoke Test) przebiegu potoku dla środowiska testowego. Stan pokazuje, że etap zakończył się niepowodzeniem.

  7. Wybierz krok Uruchom testy dymne, aby wyświetlić skojarzoną sekcję dziennika potoku.

    Zrzut ekranu usługi Azure DevOps przedstawiający dziennik uruchamiania potoku z wyświetlonym wynikiem testu dymnego. Wyróżniony jest wynik testu kondycji JSON.

    Zwróć uwagę, że dziennik pipeline zawiera odpowiedź z kontroli kondycji. Odpowiedź wskazuje, że występuje problem z komunikacją aplikacji z usługą Azure SQL Database. Baza danych nie jest jeszcze wdrożona ani skonfigurowana, dlatego witryna internetowa nie może uzyskać do niej dostępu. W następnym ćwiczeniu rozwiążesz ten problem z konfiguracją.