Cvičení – nasazení webové aplikace

Dokončeno

Vaše vývojové oddělení webu ve vaší hračkářské společnosti uložilo nejnovější verzi webu do vašeho úložiště Git. Teď jste připraveni aktualizovat pipeline pro sestavení webu a nasazení do služby Azure App Service.

V tomto procesu provedete následující úlohy:

  • Přidejte novou šablonu pipeline pro úlohu sestavení.
  • Aktualizujte potrubí tak, aby obsahovalo úlohu sestavení.
  • Přidejte nový orientační test.
  • Aktualizujte fázi nasazení a nasaďte aplikaci.
  • Spusťte potrubí.

Přidejte šablonu pipeline pro úlohu sestavení

Přidejte novou definici úlohy, která obsahuje kroky potřebné k sestavení webové aplikace.

  1. Otevřete Visual Studio Code.

  2. Ve složce deploy/pipeline-templates vytvořte nový soubor s názvem build.yml.

    snímek obrazovky Průzkumníka editoru Visual Studio Code se zobrazenou složkou pipeline-templates a souborem build.yml.

  3. Do souboru šablony kanálu build.yml přidejte následující obsah:

    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'
    

    Úloha spustí krok sestavení, který převede zdrojový kód webové aplikace na zkompilovaný soubor, který je připravený ke spuštění v Azure. Úloha pak zkopíruje zkompilovaný artefakt do dočasné inscenační složky a publikuje ho jako artefakt pipeline.

  4. Uložte změny souboru.

Přejmenujte první fázi pipeline a přidejte úlohu sestavení

  1. Otevřete soubor azure-pipelines.yml ve složce deploy.

  2. Upravte fázi Lint. Přejmenujte ho na Builda přidejte úlohu sestavení, která používá šablonu kanálu build.yml, kterou jste vytvořili.

    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. Uložte změny souboru.

Aktualizace souboru orientačního testu

Vývojáři webu přidali na web zdravotní koncový bod. Tento koncový bod zkontroluje, jestli je web online a že se může připojit k databázi. Tady přidáte nový kouřový test, který vyvolá kontrolu stavu z nasazovacího kanálu.

  1. Otevřete soubor Website.Tests.ps1 ve složce deploy.

  2. Přidejte nový testovací případ, který vyvolá kontrolu stavu. Testovací případ selže, pokud kód odpovědi není 200, což značí úspěch.

    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. Uložte změny souboru.

Přidání výstupu do souboru Bicep

Chcete přidat krok nasazení, který publikuje váš web do služby Azure App Service, ale krok publikování vyžaduje název aplikace App Service. V této části zveřejníte název aplikace jako výstup ze souboru Bicep.

  1. Otevřete soubor main.bicep ve složce deploy.

  2. Na konec obsahu souboru přidejte název aplikace App Service jako výstup.

    output appServiceAppName string = appServiceApp.name
    output appServiceAppHostName string = appServiceApp.properties.defaultHostName
    
  3. Uložte změny souboru.

Aktualizace fáze nasazení

  1. Otevřete soubor deploy.yml ve složce deploy/pipeline-templates.

  2. V definici úlohy nasazení Nasadit etapy (okolo řádku 59) nakonfigurujte úlohu tak, aby používala fond agentů hostovaných na platformě 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:
    

    Některé kroky kanálu, které přidáte později, aby fungovaly s databází, vyžadují spuštění operačního systému Windows. Pro různé úlohy ve vašem potrubí můžete použít různé fondy agentů, takže ostatní úlohy budou nadále používat fond agentů pro potrubí Ubuntu Linux.

  3. V kroku Deploy job SaveDeploymentOutputs přidejte novou proměnnou pipeline s hodnotou názvu aplikace z výstupu nasazení 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)
    

    Všimněte si, že proměnná appServiceAppHostName má použitou vlastnost isOutput=true, protože se tato proměnná používá ve fázi kouřového testu. Proměnná appServiceAppName je nastavena a použita ve stejné fázi pipeline a úloze. Proto nepotřebuje nastavení isOutput=true.

  4. Na konci obsahu úlohy Deploy přidejte nový krok pro nasazení aplikace na 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'
    

    Poznámka

    Při práci se souborem YAML buďte opatrní, aby byl nový krok nasazení na stejné úrovni jako krok DeployBicepFile. Pokud si nejste jistí, zkopírujte celý deploy.yml obsah souboru z příkladu v dalším kroku.

    Všimněte si, že jste v definici pipeline explicitně nestahovali artefakt. Protože používáte úlohu nasazení, Azure Pipelines automaticky stáhne artefakt za vás.

Ověřte obsah souboru deploy.yml a potvrďte změny.

  1. Ověřte, že váš soubor deploy.yml vypadá jako v následujícím příkladu:

    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. Uložte změny souboru.

  3. V terminálu editoru Visual Studio Code potvrďte a nasdílejte změny do úložiště Git spuštěním následujících příkazů:

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

Spuštění kanálu

  1. V prohlížeči přejděte na Pipelines.

  2. Vyberte nejnovější spuštění kanálu.

    snímek obrazovky Azure DevOps se seznamem spuštění pipelinu. Nejnovější spuštění pipelinu je zvýrazněno.

    Počkejte, až se build ve fázi úspěšně dokončí.

    Kanál se pozastaví před spuštěním fáze Validate (Testovací prostředí), protože kanál potřebuje oprávnění k použití skupiny proměnných, na kterou fáze odkazuje. Musíte schválit přístup kanálu ke skupině proměnných, protože kanál spouštíte v tomto projektu poprvé. Když kanál spustíte znovu, nemusíte schvalovat přístup ke stejné skupině proměnných.

  3. Vyberte Zobrazit.

    snímek obrazovky Azure DevOps zobrazující běh kanálu pozastavený ve fázi ověření. K pokračování se vyžaduje oprávnění. Tlačítko Zobrazit je zvýrazněné.

  4. Vyberte Povolit.

    snímek obrazovky Azure DevOps ukazující, že pipelina potřebuje oprávnění k používání skupiny proměnných ToyWebsiteTest. Tlačítko Povolit je zvýrazněné.

  5. Vyberte Povolit.

    snímek obrazovky Azure DevOps s rozhraním pro potvrzení oprávnění Tlačítko Povolit je zvýrazněné.

    Fáze Validate (Testovací prostředí) úspěšně skončí.

    Potrubí pokračuje a fáze nasazení (testovací prostředí) úspěšně končí. Potrubí pak spustí etapu kouřového testu (testovací prostředí), ale etapa kouřového testu selže.

  6. Výběrem fáze Smoke Test (testovací prostředí) otevřete protokol kanálu.

    snímek obrazovky Azure DevOps znázorňující fázi kouřového testu kanálu pro testovací prostředí. Stav ukazuje, že fáze selhala.

  7. Vyberte krok Spuštění smoke testů pro zobrazení přidružené části protokolu kanálu.

    snímek obrazovky Azure DevOps s protokolem běhu kanálu, a zobrazeným výstupem orientačního testu. Zvýrazněn je výsledek testu stavu JSON.

    Všimněte si, že protokol kanálu obsahuje odpověď kontroly stavu. Odpověď značí, že došlo k problému s komunikací aplikace se službou Azure SQL Database. Databáze ještě není nasazená ani nakonfigurovaná, proto k ní web nemá přístup. V dalším cvičení tento problém s konfigurací opravíte.