Övning – Distribuera ett webbprogram

Slutförd

På ditt leksaksföretag har ditt webbplatsutvecklingsteam checkat in den senaste versionen av webbplatsen till din Git-lagringsplats. Nu är du redo att uppdatera din pipeline för att skapa webbplatsen och distribuera den till Azure App Service.

I den här processen utför du följande uppgifter:

  • Lägg till en ny pipelinemall för byggjobbet.
  • Uppdatera pipelinen så att den inkluderar byggjobbet.
  • Lägg till ett nytt röktest.
  • Uppdatera distributionssteget för att distribuera programmet.
  • Kör pipelinen.

Lägga till en pipelinemall för byggjobbet

Lägg till en ny jobbdefinition som innehåller de steg som krävs för att skapa webbplatsprogrammet.

  1. Öppna Visual Studio Code.

  2. I mappen deploy/pipeline-templates skapar du en ny fil med namnet build.yml.

    Skärmbild av Visual Studio Code Explorer med mappen pipeline-templates och filen

  3. Lägg till följande innehåll i mallfilen build.yml pipeline:

    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'
    

    Jobbet kör ett byggsteg för att omvandla webbplatsprogrammets källkod till en kompilerad fil som är redo att köras i Azure. Jobbet kopierar sedan den kompilerade artefakten till en tillfällig mellanlagringsmapp och publicerar den som en pipelineartefakt.

  4. Spara ändringarna i filen.

Byt namn på den första pipelinefasen och lägg till ett byggjobb

  1. Öppna filen azure-pipelines.yml i mappen distribution.

  2. Ändra Lint--fasen. Byt namn på den till Skapaoch lägg till ett byggjobb som använder den build.yml pipelinemall som du skapade.

    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. Spara ändringarna i filen.

Uppdatera röktestfilen

Webbplatsutvecklarna lade till en hälsoslutpunkt på webbplatsen. Den här slutpunkten kontrollerar att webbplatsen är online och att den kan nå databasen. Här lägger du till ett nytt smoketest för att anropa hälsokontrollen från din distributionspipeline.

  1. Öppna filen Website.Tests.ps1 i mappen distribuera.

  2. Lägg till ett nytt testfall som anropar hälsokontrollen. Testfallet misslyckas om svarskoden inte är 200, vilket indikerar att det lyckades.

    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. Spara ändringarna i filen.

Lägga till utdata i Bicep-filen

Du vill lägga till ett distributionssteg som publicerar din webbplats i Azure App Service, men publiceringssteget kräver namnet på App Service-appen. Här exponerar du appnamnet som utdata från din Bicep-fil.

  1. Öppna filen main.bicep i deploy-mappen.

  2. I slutet av filinnehållet lägger du till App Service-appens namn som utdata.

    output appServiceAppName string = appServiceApp.name
    output appServiceAppHostName string = appServiceApp.properties.defaultHostName
    
  3. Spara ändringarna i filen.

Uppdatera distributionssteg

  1. Öppna deploy.yml-filen i mappen deploy/pipeline-templates.

  2. I definitionen av distributionsjobbet i Deploy-steget (nära rad 59), konfigurerar du jobbet för att använda Windows-värdbaserade agentpoolen.

    - 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:
    

    Vissa av de pipelinesteg som du lägger till senare för att arbeta med databasen kräver att Windows-operativsystemet körs. Du kan använda olika agentpooler för olika jobb i pipelinen, så att de andra jobben fortsätter att använda Ubuntu Linux-pipelineagentpoolen.

  3. I Deploy job's SaveDeploymentOutputs step lägger du till en ny pipelinevariabel med värdet för appnamnet från Bicep-distributionens utdata:

    - 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)
    

    Observera att variabeln appServiceAppHostName har egenskapen isOutput=true tillämpad på den, eftersom variabeln används i rökteststeget. Variabeln appServiceAppName anges och används i samma pipelinesteg och jobb. Därför behöver den inte inställningen isOutput=true.

  4. I slutet av Distribuera jobbinnehåll lägger du till ett nytt steg för att distribuera appen till 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'
    

    Notgång

    Var försiktig med indragningen för YAML-filen och se till att det nya distributionssteget är indraget på samma nivå som det DeployBicepFile-steget. Om du inte är säker kopierar du hela deploy.yml filinnehållet från exemplet i nästa steg.

    Observera att du inte uttryckligen laddade ned artefakten i pipelinedefinitionen. Eftersom du använder ett distributionsjobb laddar Azure Pipelines automatiskt ned artefakten åt dig.

Verifiera innehållet i deploy.yml-filen och checka in ändringarna

  1. Kontrollera att filen deploy.yml ser ut som i följande exempel:

    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. Spara ändringarna i filen.

  3. I Visual Studio Code-terminalen checkar du in och push-överför dina ändringar till Git-lagringsplatsen genom att köra följande kommandon:

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

Kör pipelinen

  1. I webbläsaren går du till Pipelines.

  2. Välj den senaste körningen av din pipeline.

    Skärmbild av Azure DevOps som visar listan över pipelinekörningar. Den senaste pipelinekörningen är markerad.

    Vänta tills Build-fasen har slutförts framgångsrikt.

    Pipelinen pausas innan den kör Validate (Test Environment)-fasen eftersom pipelinen behöver behörighet att använda variabelgruppen som fasen refererar till. Du måste godkänna pipelinens åtkomst till variabelgruppen eftersom du kör pipelinen i det här projektet för första gången. När du kör pipelinen igen behöver du inte godkänna åtkomst till samma variabelgrupp.

  3. Välj Visa.

    Skärmbild av Azure DevOps som visar pipelinekörningen pausad i valideringsfasen. Behörighet krävs för att fortsätta. Knappen Visa är markerad.

  4. Välj Tillåt.

    Skärmbild av Azure DevOps som visar att pipelinen behöver behörighet att använda variabelgruppen ToyWebsiteTest. Knappen Tillåt är markerad.

  5. Välj Tillåt.

    Skärmbild av Azure DevOps som visar gränssnittet för behörighetsbekräftelse. Knappen Tillåt är markerad.

    Verifiera (Testmiljö)-fasen har slutförts framgångsrikt.

    Pipelinen fortsätter och fasen Deploy (Test Environment) avslutas framgångsrikt. Pipelinen kör sedan röktestet (testmiljö) fasen, men röktestfasen misslyckas.

  6. Välj röktest (testmiljö) fas för att öppna pipelineloggen.

    Skärmbild av Azure DevOps som visar pipelinekörningens rökteststeg för testmiljön. Statusen visar att fasen misslyckades.

  7. Välj steget Kör röktester för att visa det relaterade avsnittet i pipelinens logg.

    Skärmbild av Azure DevOps som visar pipelinekörningsloggen med utdata från röktestet. JSON-hälsotestresultatet är markerat.

    Observera att pipelineloggen innehåller hälsokontrollsvaret. Svaret anger att det finns ett problem med programmets kommunikation med Azure SQL Database. Databasen har inte distribuerats eller konfigurerats än, vilket är anledningen till att webbplatsen inte kan komma åt den. I nästa övning åtgärdar du det här konfigurationsproblemet.