Übung: Bereitstellen einer Web-App in Azure

Abgeschlossen

In Ihrem Spielzeugunternehmen hat Ihr Websiteentwicklungsteam die neueste Version der Website in Ihr Git-Repository committet. Nun können Sie Ihren Workflow aktualisieren, um die Website zu erstellen und in Azure App Service bereitzustellen.

Dabei gehen Sie wie folgt vor:

  • Fügen Sie einen neuen Workflow für den Buildauftrag hinzu.
  • Aktualisieren Sie den Workflow, um den Buildauftrag einzuschließen.
  • Fügen Sie eine neue Feuerprobe hinzu.
  • Aktualisieren Sie den Bereitstellungsauftrag, um die Anwendung bereitzustellen.
  • Führen Sie den Workflow aus.

Hinzufügen eines wiederverwendbaren Workflows für den Buildauftrag

Hier fügen Sie eine neue Auftragsdefinition hinzu, die die erforderlichen Schritte zum Erstellen der Websiteanwendung enthält.

  1. Öffnen Sie Visual Studio Code.

  2. Erstellen Sie im Ordner .github/workflows eine neue Datei mit dem Namen build.yml.

    Screenshot: Visual Studio Code-Explorer mit den Ordnern „.github“ und „workflows“ sowie der Datei „build.yml“

  3. Fügen Sie der Workflowdatei build.yml den folgenden Inhalt hinzu:

    name: build-website
    
    on:
      workflow_call:
    
    jobs:
      build-application:
        name: Build application
        runs-on: ubuntu-latest
        steps:
        - uses: actions/checkout@v3
    
        - name: Install .NET Core
          uses: actions/setup-dotnet@v3
          with:
            dotnet-version: 3.1
    
        - name: Build publishable website
          run: |
            dotnet publish --configuration Release
          working-directory: ./src/ToyCompany/ToyCompany.Website
    
        - name: Zip publishable website
          run: |
            zip -r publish.zip .
          working-directory: ./src/ToyCompany/ToyCompany.Website/bin/Release/netcoreapp3.1/publish
    
        - name: Upload website as workflow artifact
          uses: actions/upload-artifact@v3
          with:
            name: website
            path: ./src/ToyCompany/ToyCompany.Website/bin/Release/netcoreapp3.1/publish/publish.zip
    

    Der Auftrag installiert das .NET SDK, um die Projektmappe zu erstellen. Anschließend führt er einen Buildschritt aus, um den Quellcode der Websiteanwendung in eine kompilierte Datei umzuwandeln, die in Azure ausgeführt werden kann. Der Auftrag komprimiert dann das kompilierte Artefakt und lädt es als Workflowartefakt hoch.

  4. Speichern Sie die geänderte Datei.

Hinzufügen des Buildauftrags zum Workflow

  1. Öffnen Sie die Datei workflow.yml.

  2. Fügen Sie unterhalb der Zeile jobs: vor dem Auftrag lint einen neuen Auftrag mit dem Namen build ein, der den soeben definierten wiederverwendbaren Workflow nutzt:

    name: deploy-toy-website-end-to-end
    concurrency: toy-company
    
    on:
      push:
        branches:
          - main
      workflow_dispatch:
    
    permissions:
      id-token: write
      contents: read
    
    jobs:
    
      # Build the application and database.
      build:
        uses: ./.github/workflows/build.yml
    
      # Lint the Bicep file.
      lint:
        uses: ./.github/workflows/lint.yml
    
  3. Aktualisieren Sie den Auftrag deploy-test, damit er vom Auftrag build abhängt:

    # Deploy to the test environment.
    deploy-test:
      uses: ./.github/workflows/deploy.yml
      needs: [build, lint]
      with:
        environmentType: Test
        resourceGroupName: ToyWebsiteTest
        reviewApiUrl: https://sandbox.contoso.com/reviews
      secrets:
        AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID_TEST }}
        AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
        AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
        reviewApiKey: ${{ secrets.REVIEW_API_KEY_TEST }}
    
  4. Aktualisieren Sie den Auftrag deploy-production, damit er auch von den Aufträgen build und lint abhängt.

    # Deploy to the production environment.
    deploy-production:
      uses: ./.github/workflows/deploy.yml
      needs:
      - lint
      - build
      - deploy-test
      with:
        environmentType: Production
        resourceGroupName: ToyWebsiteProduction
        reviewApiUrl: https://api.contoso.com/reviews
      secrets:
        AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID_PRODUCTION }}
        AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
        AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
        reviewApiKey: ${{ secrets.REVIEW_API_KEY_PRODUCTION }}
    

    Da die Produktionsbereitstellung von der Testbereitstellung abhängt, müssen Sie die Abhängigkeiten nicht unbedingt angeben. Es wird aber als Best Practice empfohlen, dies explizit anzugeben. So wird vermieden, dass Ihr Workflow nicht korrekt ausgeführt wird, wenn Sie Ihre Aufträge oder Umgebungen neu anordnen oder entfernen.

    Beachten Sie, dass Sie die needs-Liste auf zwei verschiedene Arten angeben – die Abhängigkeiten Ihrer Testumgebungsbereitstellung werden in einer einzigen Zeile aufgeführt, die Ihrer Produktionsumgebung in einer mehrzeiligen Liste. Die beiden Ansätze sind gleichwertig.

  5. Speichern Sie die geänderte Datei.

Aktualisieren der Datei für die Feuerprobe

Die Websiteentwickler haben der Website einen Integritätsendpunkt hinzugefügt. Dieser Endpunkt überprüft, ob die Website online ist und die Datenbank erreichen kann. Hier fügen Sie eine neue Feuerprobe hinzu, um die Integritätsprüfung aus Ihrem Bereitstellungsworkflow aufzurufen.

  1. Öffnen Sie die Datei Website.Tests.ps1 im Ordner deploy.

  2. Fügen Sie einen neuen Testfall hinzu, der die Integritätsprüfung aufruft. Der Testfall schlägt fehl, wenn nicht der Antwortcode 200 für einen erfolgreichen Test angegeben wird:

    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. Speichern Sie die geänderte Datei.

Hinzufügen der Ausgabe zur Bicep-Datei

In Kürze fügen Sie einen Bereitstellungsschritt hinzu, der Ihre Website in Azure App Service veröffentlicht. Für den Veröffentlichungsschritt ist der Name der App Service-App erforderlich. Hier machen Sie den App-Namen als Ausgabe aus Ihrer Bicep-Datei verfügbar.

  1. Öffnen Sie die Datei main.bicep im Ordner deploy.

  2. Fügen Sie am Ende des Dateiinhalts den Namen der App Service-App als Ausgabe hinzu:

    output appServiceAppName string = appServiceApp.name
    output appServiceAppHostName string = appServiceApp.properties.defaultHostName
    
  3. Speichern Sie die geänderte Datei.

Aktualisieren des Bereitstellungsauftrags zur Verteilung der Ausgabe

Nun müssen Sie Ihren Auftrag deploy aktualisieren, um den Wert der Ausgabe aus der Bicep-Bereitstellung zu übernehmen und für den Rest des Workflows verfügbar zu machen.

  1. Öffnen Sie die Datei deploy.yml im Ordner .github/workflows.

  2. Fügen Sie der deploy-Auftragsdefinition eine neue Ausgabe für appServiceAppName hinzu:

    deploy:
      needs: validate
      environment: ${{ inputs.environmentType }}
      runs-on: ubuntu-latest
      outputs:
        appServiceAppName: ${{ steps.deploy.outputs.appServiceAppName }}
        appServiceAppHostName: ${{ steps.deploy.outputs.appServiceAppHostName }}
      steps:
    

    Hinweis

    Wenn Sie beginnen, mit Ihrer YAML-Datei in Visual Studio Code zu arbeiten, werden möglicherweise einige rote Wellenlinien angezeigt, die darauf hindeuten, dass ein Problem vorliegt. Dies liegt daran, dass die Visual Studio Code-Erweiterung für YAML-Dateien manchmal fälschlicherweise das Schema der Datei errät.

    Sie können die Probleme ignorieren, die die Erweiterung meldet. Wenn Sie möchten, können Sie den folgenden Code am Anfang der Datei hinzufügen, um das Raten durch die Erweiterung zu unterdrücken:

    # yaml-language-server: $schema=./deploy.yml
    

Hinzufügen eines Auftrags zum Bereitstellen der Website

  1. Definieren Sie unterhalb der deploy-Auftragsdefinition und oberhalb der smoke-test-Auftragsdefinition einen neuen Auftrag für die Bereitstellung der Website in App Service:

    deploy-website:
      needs: deploy
      environment: ${{ inputs.environmentType }}
      runs-on: ubuntu-latest
      steps:
      - uses: actions/download-artifact@v3
      - uses: azure/login@v1
        name: Sign in to Azure
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - uses: azure/webapps-deploy@v2
        name: Deploy website
        with:
          app-name: ${{ needs.deploy.outputs.appServiceAppName }}
          package: website/publish.zip
    

    Hinweis

    Achten Sie auf die Einzüge der YAML-Datei und stellen Sie sicher, dass der neue Bereitstellungsschritt auf der gleichen Ebene wie der Auftrag deploy eingerückt ist. Wenn Sie nicht sicher sind, kopieren Sie den gesamten Inhalt der Datei deploy.yml aus dem Beispiel im nächsten Schritt.

    Beachten Sie, dass der Auftrag durch die Verwendung des Schlüsselworts needs vom Auftrag deploy abhängt. Mit dieser Abhängigkeit wird sichergestellt, dass die Website erst bereitgestellt wird, wenn die Infrastruktur bereit ist. Darüber hinaus kann der Auftrag auf die Ausgabe appServiceAppName des Auftrags deploy zugreifen.

    Beachten Sie außerdem, dass dieser Auftrag Schritte zum Herunterladen der Workflowartefakte und zur Anmeldung bei Azure enthält. Jeder Auftrag wird in einem eigenen Runner ausgeführt und muss deshalb eigenständig sein.

  2. Speichern Sie die geänderte Datei.

Überprüfen des Inhalts der Datei deploy.yml und Committen Ihrer Änderungen

  1. Vergewissern Sie sich, dass die Datei deploy.yml wie im folgenden Beispiel aussieht:

    name: deploy
    
    on:
      workflow_call:
        inputs:
          environmentType:
            required: true
            type: string
          resourceGroupName:
            required: true
            type: string
          reviewApiUrl:
            required: true
            type: string
        secrets:
          AZURE_CLIENT_ID:
            required: true
          AZURE_TENANT_ID:
            required: true
          AZURE_SUBSCRIPTION_ID:
            required: true
          reviewApiKey:
            required: true
    
    jobs:
      validate:
         runs-on: ubuntu-latest
         steps:
         - uses: actions/checkout@v3
         - uses: azure/login@v1
           name: Sign in to Azure
           with:
             client-id: ${{ secrets.AZURE_CLIENT_ID }}
             tenant-id: ${{ secrets.AZURE_TENANT_ID }}
             subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
         - if: inputs.environmentType != 'Production'
           uses: azure/arm-deploy@v1
           name: Run preflight validation
           with:
             deploymentName: ${{ github.run_number }}
             resourceGroupName: ${{ inputs.resourceGroupName }}
             template: ./deploy/main.bicep
             parameters: >
               environmentType=${{ inputs.environmentType }}
               reviewApiUrl=${{ inputs.reviewApiUrl }}
               reviewApiKey=${{ secrets.reviewApiKey }}
             deploymentMode: Validate
         - if: inputs.environmentType == 'Production'
           uses: azure/arm-deploy@v1
           name: Run what-if
           with:
             failOnStdErr: false
             resourceGroupName: ${{ inputs.resourceGroupName }}
             template: ./deploy/main.bicep
             parameters: >
               environmentType=${{ inputs.environmentType }}
               reviewApiUrl=${{ inputs.reviewApiUrl }}
               reviewApiKey=${{ secrets.reviewApiKey }}
             additionalArguments: --what-if
    
      deploy:
        needs: validate
        environment: ${{ inputs.environmentType }}
        runs-on: ubuntu-latest
        outputs:
          appServiceAppName: ${{ steps.deploy.outputs.appServiceAppName }}
          appServiceAppHostName: ${{ steps.deploy.outputs.appServiceAppHostName }}
        steps:
        - uses: actions/checkout@v3
        - uses: azure/login@v1
          name: Sign in to Azure
          with:
            client-id: ${{ secrets.AZURE_CLIENT_ID }}
            tenant-id: ${{ secrets.AZURE_TENANT_ID }}
            subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
        - uses: azure/arm-deploy@v1
          id: deploy
          name: Deploy Bicep file
          with:
            failOnStdErr: false
            deploymentName: ${{ github.run_number }}
            resourceGroupName: ${{ inputs.resourceGroupName }}
            template: ./deploy/main.bicep
            parameters: >
               environmentType=${{ inputs.environmentType }}
               reviewApiUrl=${{ inputs.reviewApiUrl }}
               reviewApiKey=${{ secrets.reviewApiKey }}
    
      deploy-website:
        needs: deploy
        environment: ${{ inputs.environmentType }}
        runs-on: ubuntu-latest
        steps:
        - uses: actions/download-artifact@v3
        - uses: azure/login@v1
          name: Sign in to Azure
          with:
            client-id: ${{ secrets.AZURE_CLIENT_ID }}
            tenant-id: ${{ secrets.AZURE_TENANT_ID }}
            subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
        - uses: azure/webapps-deploy@v2
          name: Deploy website
          with:
            app-name: ${{ needs.deploy.outputs.appServiceAppName }}
            package: website/publish.zip
    
      smoke-test:
        runs-on: ubuntu-latest
        needs: deploy
        steps:
        - uses: actions/checkout@v3
        - run: |
            $container = New-PesterContainer `
              -Path 'deploy/Website.Tests.ps1' `
              -Data @{ HostName = '${{needs.deploy.outputs.appServiceAppHostName}}' }
            Invoke-Pester `
              -Container $container `
              -CI
          name: Run smoke tests
          shell: pwsh
    
  2. Speichern Sie die geänderte Datei.

  3. Committen und pushen Sie Ihre Änderungen im Visual Studio Code-Terminal in Ihr Git-Repository, indem Sie die folgenden Befehle ausführen:

    git add .
    git commit -m "Build and deploy website application"
    git push
    
  4. Dies ist das erste Mal, dass Sie dieses Repository verwendet haben, sodass Sie u. U. aufgefordert werden, sich anzumelden.

    Geben Sie unter Windows 1 ein, um sich über einen Webbrowser zu authentifizieren, und drücken Sie die EINGABETASTE.

    Wählen Sie unter macOS Authorize (Autorisieren) aus.

  5. Ein Browserfenster wird geöffnet. Sie müssen sich möglicherweise erneut bei GitHub anmelden. Wählen Sie Autorisieren.

Ausführen des Workflows

  1. Navigieren Sie in Ihrem Browser zu Actions.

    Für die erste Ausführung Ihres Workflows mit der Bezeichnung Initial commit (Erster Commit) wird ein Fehler angezeigt. GitHub hat den Workflow automatisch ausgeführt, als Sie das Repository erstellt haben. Der Fehler ist aufgetreten, da die Geheimnisse zu diesem Zeitpunkt nicht verfügbar waren. Diesen Fehler können Sie ignorieren.

  2. Wählen Sie den Workflow deploy-toy-website-end-to-end aus.

  3. Wählen Sie die letzte Ausführung Ihres Workflows aus.

  4. Warten Sie, bis der Auftrag build erfolgreich abgeschlossen wurde.

    Screenshot: GitHub mit Anzeige der Aufträge zur Workflowausführung

  5. Warten Sie, bis der Auftrag deploy-test / deploy erfolgreich abgeschlossen wurde.

    Im Bereich Annotations (Anmerkungen) werden einige Warnungen aufgeführt. All diese Warnungen sind darauf zurückzuführen, wie Bicep Informationsmeldungen in das Workflowprotokoll schreibt. Sie können diese Warnungen ignorieren.

  6. Der Workflow führt dann den Auftrag deploy-test / smoke-test, aber die Feuerprobe ist nicht erfolgreich:

    Screenshot: GitHub mit Anzeige des Auftrags „smoke-test“ der Workflowausführung für die Testumgebung. Der Status zeigt, dass der Auftrag nicht erfolgreich ausgeführt wurde.

  7. Wählen Sie den Auftrag deploy-test / smoke-test aus, um das Workflowprotokoll zu öffnen.

  8. Wählen Sie den Schritt deploy-test / smoke-test aus, um den zugehörigen Abschnitt im Workflowprotokoll anzuzeigen:

    Screenshot: Protokoll der Workflowausführung in GitHub mit Anzeige der Ausgabe der Feuerprobe. Das Ergebnis des JSON-Integritätstests ist hervorgehoben.

    Beachten Sie, dass das Workflowprotokoll anzeigt, dass die Website und die Konfiguration nicht fehlerfrei sind. Es liegt ein Problem bei der Kommunikation der Anwendung mit Azure SQL-Datenbank vor. Sie haben noch keine Datenbank bereitgestellt oder konfiguriert, weshalb die Website nicht darauf zugreifen kann. Sie beheben dieses Problem zeitnah.