Ćwiczenie — wdrażanie aplikacji internetowej

Ukończone

W firmie zajmującej się projektowaniem witryn internetowych zaangażuj najnowszą wersję witryny internetowej do repozytorium Git. Teraz możesz zaktualizować potok, aby skompilować witrynę internetową i wdrożyć go w usłudze aplikacja systemu Azure Service.

W tym procesie wykonasz następujące zadania:

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

Dodawanie szablonu potoku dla zadania kompilacji

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

  1. Otwórz Visual Studio Code.

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

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

  3. Dodaj następującą zawartość do pliku szablonu potoku 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.

Zmienianie nazwy pierwszego etapu potoku i dodawanie zadania kompilacji

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

  2. Zmodyfikuj etap Lint . Zmień jego nazwę na Build (Kompilacja) i dodaj zadanie kompilacji, które używa utworzonego szablonu potoku 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. Zapisz zmiany w pliku.

Aktualizowanie pliku testu weryfikacyjnego kompilacji

Deweloperzy witryny internetowej dodali do witryny internetowej punkt końcowy kondycji. 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 weryfikacyjny kompilacji w celu wywołania kontroli kondycji z potoku wdrażania.

  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 aplikacja systemu Azure 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 zadania wdrażania etapu wdrażania (w pobliżu wiersza 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 kroki potoku dodane później do pracy z bazą danych wymagają uruchomienia systemu operacyjnego Windows. W potoku można używać różnych pul agentów dla różnych zadań, więc inne zadania nadal korzystają z puli agentów potoku systemu Ubuntu Linux.

  3. W kroku SaveDeploymentOutputs zadania wdrażania 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ą isOutput=true właściwość, ponieważ ta zmienna jest używana na etapie testu weryfikacyjnego kompilacji. Zmienna appServiceAppName jest ustawiana i używana w tym samym etapie i zadaniu potoku. W związku z tym ustawienie nie jest potrzebne isOutput=true .

  4. Na końcu zawartości zadania Deploy (Wdrażanie zadania) dodaj nowy krok, aby wdrożyć aplikację w usłudze aplikacja systemu Azure 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'
    

    Uwaga

    Zachowaj ostrożność przy użyciu wcięcia pliku YAML, upewniając się, że nowy krok wdrażania jest wcięcie na tym samym poziomie co DeployBicepFile krok. 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 wdrażania, usługa Azure Pipelines automatycznie pobiera artefakt.

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 pozycji Potoki.

  2. Wybierz najnowszy przebieg potoku.

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

    Poczekaj na pomyślne zakończenie etapu kompilacji .

    Potok jest wstrzymywane przed uruchomieniem etapu Validate (Test Environment), ponieważ potok musi mieć uprawnienia do używania grupy zmiennych, do których odwołuje się etap. Należy zatwierdzić dostęp potoku do grupy zmiennych, ponieważ uruchamiasz potok w tym projekcie po raz pierwszy. Po ponownym uruchomieniu potoku nie musisz zatwierdzać dostępu do tej samej grupy zmiennych.

  3. Wybierz pozycję Widok.

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

  4. Wybierz pozycję Zezwól.

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

  5. Wybierz pozycję Zezwól.

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

    Etap Weryfikacji (środowisko testowe) kończy się pomyślnie.

    Potok będzie kontynuowany, a etap Wdrażanie (środowisko testowe) zakończy się pomyślnie. Następnie potok uruchamia etap Testu weryfikacyjnego kompilacji (środowisko testowe), ale etap testu weryfikacyjnego kompilacji kończy się niepowodzeniem.

  6. Wybierz etap Testu weryfikacyjnego kompilacji (środowisko testowe), aby otworzyć dziennik potoku.

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

  7. Wybierz krok Uruchom testy weryfikacyjne kompilacji, aby wyświetlić skojarzona sekcja dziennika potoku.

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

    Zwróć uwagę, że dziennik potoku zawiera odpowiedź na kontrolę 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ą.