練習 - 部署 Web 應用程式

已完成

在您的玩具公司,您的網站開發小組已向您的 Git 存放庫認可最新版的網站。 現在,您已準備好更新工作流程來建立網站,並部署至 Azure App Service。

在此程序中,您將會:

  • 為建置作業新增新呼叫的工作流程。
  • 更新工作流程以包括建置作業。
  • 新增煙霧測試。
  • 更新部署作業以部署應用程式。
  • 執行工作流程。

為建置作業新增可重複使用的工作流程

在這裡,您會新增包含建置網站應用程式所需步驟的新作業定義。

  1. 打開 Visual Studio Code。

  2. .github/workflows 資料夾中,建立名為 build.yml 的新檔案。

    Visual Studio Code [檔案總管] 的螢幕擷取畫面,其中顯示 [.github] 與 [workflows] 資料夾及 build.yml 檔案。

  3. 將下列內容新增至 build.yml 工作流程檔案:

    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
    

    此作業會安裝 .NET SDK 來建置解決方案。 然後,其會執行建置步驟,以將網站應用程式的原始程式碼轉換成可在 Azure 中執行的已編譯檔案。 接著,此作業會壓縮已編譯的成品,並將其上傳為工作流程成品。

  4. 儲存對檔案所做的變更。

將建置作業新增至工作流程

  1. 開啟 workflow.yml 檔案。

  2. jobs: 行下方,在 lint 作業之前,新增名為 build 的新作業,使其能使用您剛才定義的可重複使用工作流程:

    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. 更新 deploy-test 作業,以相依於新的 build 作業:

    # 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. 更新 deploy-production 作業,以同樣也相依於 builtlint 作業。

    # 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 }}
    

    由於生產部署會相依於測試部署,您並非一定要指定相依性。 但是明確定義是良好的做法,這是為了避免在您重新排序或移除作業或環境的情況下,導致工作流程以錯誤的方式執行。

    請注意,您會以兩種不同的方式來指定 needs 清單:測試環境部署的相依性會列在單一行上,而實際執行環境的相依性則會使用多行清單。 這兩種方法都相等。

  5. 儲存對檔案所做的變更。

更新煙霧測試檔案

網站開發人員已將健康情況端點新增至網站。 此端點會檢查網站是否在線上,以及是否可以連線到資料庫。 在這裡,您會新增煙霧測試,以從部署工作流程叫用健康情況檢查。

  1. 開啟 [deploy] 資料夾中的 Website.Tests.ps1 檔案。

  2. 新增測試案例來叫用健康情況檢查。 如果回應碼不是 200 (表示成功),測試案例便會失敗:

    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. 儲存對檔案所做的變更。

將輸出新增至 Bicep 檔案

您很快就會新增部署步驟,以將網站發佈至 Azure App Service。 發佈步驟需要 App Service 應用程式的名稱。 在這裡,您會公開應用程式名稱作為 Bicep 檔案的輸出。

  1. deploy 資料夾中開啟 main.bicep 檔案。

  2. 在檔案內容的結尾,新增 App Service 應用程式的名稱作為輸出:

    output appServiceAppName string = appServiceApp.name
    output appServiceAppHostName string = appServiceApp.properties.defaultHostName
    
  3. 儲存對檔案所做的變更。

更新部署作業以傳播輸出

現在,您需要更新 deploy 作業,以取得 Bicep 部署的輸出值,並將其提供給工作流程的其餘部分使用。

  1. 開啟 .github/workflows 資料夾中的 deploy.yml 檔案。

  2. deploy 作業的定義中,新增 appServiceAppName 的新輸出:

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

    注意

    當您開始在 Visual Studio Code 中使用 YAML 檔案時,您可能會看到一些紅色波浪線來指出有問題存在。 這是因為 YAML 檔案的 Visual Studio Code 延伸模組有時會猜錯檔案的結構描述。

    您可以忽略延伸模組所報告的問題。 或者,如果您想要的話,也可以在檔案頂端新增下列程式碼,以隱藏擴充功能的猜測:

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

新增作業以部署網站

  1. deploy 作業定義下方和 smoke-test 作業定義上方,定義新的作業以將網站部署至 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
    

    注意

    請小心使用 YAML 檔案的縮排,確保新作業在與 deploy 作業相同的層級縮排。 如果您不確定,請從下一個步驟中的範例複製整個 deploy.yml 檔案內容。

    請注意,作業會使用 needs 關鍵字來相依於 deploy 作業。 相依性可確保在基礎結構就緒之前不會部署網站。 其也可讓作業存取 deploy 作業的 appServiceAppName 輸出。

    此外,請注意,此作業包括下載工作流程成品和登入 Azure 的步驟。 每個作業都會在自己的執行器上執行,因此必須為獨立式。

  2. 儲存對檔案所做的變更。

確認 deploy.yml 檔案內容,並認可您的變更

  1. 確認 deploy.yml 檔案看起來像下列範例:

    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. 儲存對檔案所做的變更。

  3. 在 Visual Studio Code 終端機中執行下列命令,以認可您所做的變更,並將其推送至您的 Git 存放庫:

    git add .
    git commit -m "Build and deploy website application"
    git push
    
  4. 這是您第一次推送至此存放庫,因此系統可能會提示您登入。

    在 Windows 上,輸入 1 以使用網頁瀏覽器進行驗證,然後選取 Enter

    在 macOS 上,選取 [授權]

  5. 瀏覽器視窗隨即出現。 您可能需要重新登入 GitHub。 選取授權

執行工作流程

  1. 在瀏覽器中,前往 [Actions] \(動作\)。

    您工作流程的首次執行 (標示為 [Initial commit] \(初始認可\)) 會顯示為失敗。 在您建立存放庫時,GitHub 會自動執行工作流程。 因為當時沒有準備好祕密,因此其會失敗。 您可以忽略此失敗。

  2. 選取 [deploy-toy-website-end-to-end] 工作流程。

  3. 選取工作流程的最新執行。

  4. 等候 build 作業順利完成。

    GitHub 的螢幕擷取畫面,其中顯示工作流程執行作業。

  5. 等候 deploy-test / deploy 作業順利完成。

    某些警告會列在 [Annotations] \(註釋\) 面板中。 所有這些警告都是因為 Bicep 將資訊訊息寫入工作流程記錄的方式所致。 您可以忽略這些警告。

  6. 然後,工作流程會執行 deploy-test / smoke-test 作業,但煙霧測試會失敗:

    GitHub 的螢幕擷取畫面,其中顯示工作流程執行針對測試環境的煙霧測試工作。狀態顯示作業已失敗。

  7. 選取 deploy-test / smoke-test 作業以開啟工作流程記錄。

  8. 選取 [Run smoke tests] \(執行煙霧測試\) 步驟,以檢視工作流程記錄的相關聯區段:

    GitHub 的螢幕擷取畫面,其中顯示工作流程執行記錄,還有煙霧測試的輸出。已醒目提示 JSON 健康情況測試結果。

    請注意,工作流程記錄指出網站和設定狀況不良。 應用程式與 Azure SQL Database 的通訊發生問題。 您尚未部署或設定資料庫,這就是網站無法加以存取的原因。 你將能很快解決此問題。