演習 - Web アプリケーションをデプロイする

完了

あなたの玩具会社では、Web サイト開発チームが最新バージョンの Web サイトを Git リポジトリにコミット済みです。 これで、Web サイトをビルドして Azure App Service にデプロイするようにワークフローを更新する準備が整いました。

このプロセスでは、次のことを行います。

  • ビルド ジョブ用に新しい呼び出し先ワークフローを追加する。
  • ビルド ジョブを含めるようにワークフローを更新する。
  • 新しいスモーク テストを追加します。
  • アプリケーションをデプロイするようにデプロイ ジョブを更新する。
  • ワークフローを実行します。

ビルド ジョブに再利用可能なワークフローを追加する

ここで、Web サイト アプリケーションをビルドするために必要なステップを含む新しいジョブ定義を追加します。

  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 がインストールされます。 その後、ビルド ステップが実行されて、Web サイト アプリケーションのソース コードが、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. 新しい build ジョブに依存するように deploy-test ジョブを更新します。

    # 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. 同様に、built ジョブと lint ジョブにも依存するように deploy-production ジョブを更新します。

    # 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 の内容が 2 とおりの方法で指定されていることに注目してください。デプロイの依存関係が、テスト環境では 1 行で列挙されているのに対し、運用環境では複数行で入力されています。 2 つの方法は等価です。

  5. ファイルに加えた変更を保存します。

スモーク テスト ファイルを更新する

Web サイト開発者は Web サイトに正常性エンドポイントを追加済みです。 このエンドポイントは、Web サイトがオンラインであり、データベースにアクセスできることを確認します。 ここで、デプロイ ワークフローから正常性チェックを呼び出す新しいスモーク テストを追加します。

  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 ファイルに追加する

Web サイトを 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. ファイルに加えた変更を保存します。

出力を伝達するようにデプロイ ジョブを更新する

今度は、Bicep デプロイから得られた出力の値を受け取って残りのワークフローから利用できるよう、deploy ジョブを更新する必要があります。

  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:
    

    Note

    Visual Studio Code で YAML ファイルの編集を始めると、問題があることを示す赤い波線が表示されることがあります。 これは、YAML ファイル用の Visual Studio Code 拡張機能がファイルのスキーマを誤って判断することがあるからです。

    拡張機能が指摘する問題は無視してかまいません。 または、次のコードをファイルの上部に追加して拡張機能が推測しないようにすることもできます。

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

Web サイトをデプロイするためのジョブを追加する

  1. deploy ジョブ定義と smoke-test ジョブ定義の間に、Web サイトを 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
    

    Note

    新しいジョブが deploy ジョブと同じレベルでインデントされるように、YAML ファイルのインデントに注意してください。 不明な場合は、次の手順の例から deploy.yml ファイルの内容全体をコピーしてください。

    このジョブは、needs キーワードを使用して deploy ジョブに依存していることに注目してください。 インフラストラクチャの準備が整うまで Web サイトがデプロイされないことが、この依存関係によって保証されます。 また、そうすることで、appServiceAppName の出力に deploy ジョブからアクセスできるようになります。

    さらに、このジョブには、ワークフロー成果物をダウンロードするステップと 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」と入力して Web ブラウザーの使用を認証し、Enter キーを押します。

    macOS の場合は、[承認] を選択します。

  5. ブラウザー ウィンドウが開きます。 場合によっては、再度 GitHub にサインインする必要があります。 [承認] を選択します。

ワークフローを実行する

  1. ブラウザーで、Actions に移動します。

    Initial commit というラベルが付いたワークフローの初めての実行が失敗として表示されます。 GitHub が、リポジトリの作成時にワークフローを自動的に実行しました。 その時点でシークレットの準備が整っていなかったため、失敗しました。 この失敗は無視してかまいません。

  2. deploy-toy-website-end-to-end ワークフローを選択します。

  3. ワークフローの最新の実行を選択します。

  4. build ジョブが正常に完了するまで待ちます。

    ワークフロー実行ジョブを示す GitHub のスクリーンショット。

  5. deploy-test / deploy ジョブが正常に完了するまで待ちます。

    [注釈] パネルにいくつかの警告がリストされています。 これらの警告すべては、Bicep が情報メッセージをワークフロー ログに書き込む方法が原因です。 これらの警告は無視してかまいません。

  6. その後、ワークフローによって deploy-test / smoke-test ジョブが実行されますが、スモーク テストは失敗します。

    テスト環境におけるワークフロー実行のスモーク テスト ジョブを示す GitHub のスクリーンショット。ステータスは、ジョブが失敗したことを示しています。

  7. [deploy-test / smoke-test] ジョブを選択してワークフロー ログを開きます。

  8. [Run smoke tests](スモーク テストの実行) ステップを選択して、ワークフロー ログの関連セクションを表示します。

    ワークフロー実行ログが表示されている GitHub のスクリーンショット。スモーク テストの出力が表示されています。JSON 正常性テストの結果が強調表示されています。

    Web サイトと構成が正常ではないというワークフロー ログが表示されていることに注目してください。 Azure SQL Database とのアプリケーションの通信に問題があります。 データベースのデプロイも構成もまだ完了していません。このため、Web サイトからアクセスできません。 この問題はすぐに解決します。