Azure Container Apps に Orleans をデプロイする

このチュートリアルでは、Azure Container Apps にサンプルの Orleans ショッピング カート アプリケーションをデプロイする方法について説明します。 このチュートリアルでは、「Azure App Service に Orleans をデプロイする」で紹介している、サンプルの Orleans ショッピング カート アプリの機能を拡張します。 サンプル アプリでは、Azure Active Directory (AAD) の企業-消費者間 (B2C) 認証を追加し、Azure Container Apps にデプロイします。

GitHub Actions、.NET と Azure CLI、Azure Bicep を使用してデプロイする方法について学習します。 さらに、Container Apps の HTTP イングレスを構成する方法について学習します。

このチュートリアルでは、以下の内容を学習します。

  • Azure Container Apps に Orleans アプリケーションをデプロイする
  • GitHub Actions と Azure Bicep を使ってデプロイを自動化する
  • HTTP イングレスを構成する

前提条件

アプリをローカルで実行する

アプリをローカルで実行するには、Azure サンプル: Azure Container Apps の Orleans ショッピング カートのリポジトリをフォークし、ローカル コンピューターにクローンします。 クローンしたら、お好みの IDE でソリューションを開きます。 Visual Studio を使っている場合は、Orleans.ShoppingCart.Silo プロジェクトを右クリックして [スタートアップ プロジェクトに設定] を選択してから、アプリを実行します。 それ以外の場合は、次の .NET CLI コマンドを使ってアプリを実行できます。

dotnet run --project Silo\Orleans.ShoppingCart.Silo.csproj

詳細については、「dotnet run」を参照してください。 アプリを実行すると、アプリの機能について説明するランディング ページが表示されます。 右上隅にサインイン ボタンが表示されます。 アカウントにサインアップすることも、既にアカウントをお持ちの場合はサインインすることもできます。 サインインすると、中を移動して、その機能をテストすることができます。 ローカルで実行する場合、アプリのすべての機能はインメモリ永続化とローカル クラスタリングに依存します。また、Bogus NuGet パッケージを使用して架空の商品が生成されます。 アプリを停止するには、Visual Studio の [デバッグの停止] オプションを選択するか、.NET CLI で Ctrl+C キーを押してください。

AAD B2C

認証の概念については、このチュートリアルでは説明していませんが、Azure Active Directory B2C テナントを作成する方法を学習してから、Web アプリを登録してそれを使用することができます。 このショッピング カートのサンプル アプリの場合、結果としてデプロイされた Container Apps の URL を B2C テナントに登録する必要があります。 詳細については、「ASP.NET Core Blazor 認証と承認」を参照してください。

重要

Container Apps がデプロイされた後、B2C テナントにアプリの URL を登録する必要があります。 ほとんどの運用環境のシナリオでは、アプリの URL を 1 回登録するだけで済みます。変更することはできません。

Azure Container Apps 環境内でアプリがどのように分離されているかを視覚化するために、次の図を参照してください。

Azure Container Apps HTTP イングレス。

前の図では、アプリへのすべての受信トラフィックは、セキュリティで保護された HTTP イングレスを介してファネルされます。 Azure Container Apps 環境にはアプリ インスタンスが含まれており、そのアプリ インスタンスには、Blazor Server と Orleans アプリ機能を公開する ASP.NET Core ホストが含まれています。

Azure Container Apps をデプロイする

Azure Container Apps にアプリをデプロイするために、リポジトリでは GitHub Actions が利用されます。 このデプロイを行う前に、いくつかの Azure リソースが必要になり、GitHub リポジトリを正しく構成する必要があります。

アプリをデプロイする前に、Azure リソース グループを作成する必要があります (既存のものを使用することもできます)。 新しい Azure リソース グループを作成するには、次のいずれかの記事を使用します。

選択したリソース グループ名をメモしておいてください。アプリをデプロイするために後で必要になります。

サービス プリンシパルの作成

アプリのデプロイを自動化するには、サービス プリンシパルを作成する必要があります。 これは、ユーザーに代わって Azure リソースを管理するためのアクセス許可を持つ Microsoft アカウントです。

az ad sp create-for-rbac --sdk-auth --role Contributor \
  --name "<display-name>"  --scopes /subscriptions/<your-subscription-id>

作成される JSON 資格情報は次のようになりますが、クライアント、サブスクリプション、テナントには実際の値が使用されます。

{
  "clientId": "<your client id>",
  "clientSecret": "<your client secret>",
  "subscriptionId": "<your subscription id>",
  "tenantId": "<your tenant id>",
  "activeDirectoryEndpointUrl": "https://login.microsoftonline.com/",
  "resourceManagerEndpointUrl": "https://brazilus.management.azure.com",
  "activeDirectoryGraphResourceId": "https://graph.windows.net/",
  "sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
  "galleryEndpointUrl": "https://gallery.azure.com",
  "managementEndpointUrl": "https://management.core.windows.net"
}

コマンドの出力をクリップボードにコピーし、次の手順に進みます。

GitHub シークレットを作成します

GitHub では、暗号化されたシークレットを作成するためのメカニズムが提供されています。 作成したシークレットは、GitHub Actions のワークフローで使用できます。 アプリのデプロイを自動化するために、Azure Bicep と組み合わせて GitHub Actions を使う方法を見ていきます。 Bicep は、宣言型の構文を使用して Azure リソースをデプロイするドメイン固有言語 (DSL) です。 詳細については、「Bicep とは」を参照してください。 「サービス プリンシパルの作成」の手順で得た出力を使用し、JSON 形式の資格情報を使って AZURE_CREDENTIALS という名前の GitHub シークレットを作成する必要があります。

GitHub リポジトリ内で、[Settings](設定)>[Secrets](シークレット)>[Create a new secret](新しいシークレットの作成) の順に選択します。 AZURE_CREDENTIALS という名前を入力し、前の手順の JSON 資格情報を [Value](値) フィールドに貼り付けます。

GitHub リポジトリ: [設定] > [シークレット]

詳細については、GitHub: 「Encrypted Secrets (暗号化されたシークレット)」を参照してください。

Azure デプロイの準備をする

アプリは、デプロイ用にパッケージ化する必要があります。 Orleans.ShoppingCart.Silos プロジェクトで、Publish ステップの後に実行される Target 要素を定義します。 これにより、発行ディレクトリが silo.zip ファイルに圧縮されます。

<Target Name="ZipPublishOutput" AfterTargets="Publish">
    <Delete Files="$(ProjectDir)\..\silo.zip" />
    <ZipDirectory SourceDirectory="$(PublishDir)" DestinationFile="$(ProjectDir)\..\silo.zip" />
</Target>

Azure Container Apps に .NET アプリをデプロイする方法は多数あります。 このチュートリアルでは、GitHub Actions、Azure Bicep、.NET と Azure CLI を使用します。 GitHub リポジトリのルートにある ./github/workflows/deploy.yml ファイルについて考えてみましょう。

name: Deploy to Azure Container Apps

on:
  push:
    branches:
    - main

env:
  UNIQUE_APP_NAME: orleanscart
  SILO_IMAGE_NAME: orleanscart-silo
  AZURE_RESOURCE_GROUP_NAME: orleans-resourcegroup
  AZURE_RESOURCE_GROUP_LOCATION: eastus

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3

    - name: Setup .NET 6.0
      uses: actions/setup-dotnet@v3
      with:
        dotnet-version: 6.0.x

    - name: .NET publish shopping cart app
      run: dotnet publish ./Silo/Orleans.ShoppingCart.Silo.csproj --configuration Release

    - name: Login to Azure
      uses: azure/login@v1
      with:
        creds: ${{ secrets.AZURE_CREDENTIALS }}

    - name: Flex ACR Bicep
      run: |
        az deployment group create \
          --resource-group ${{ env.AZURE_RESOURCE_GROUP_NAME }} \
          --template-file '.github/workflows/flex/acr.bicep' \
          --parameters location=${{ env.AZURE_RESOURCE_GROUP_LOCATION }}

    - name: Get ACR Login Server
      run: |
        ACR_NAME=$(az deployment group show -g ${{ env.AZURE_RESOURCE_GROUP_NAME }} -n acr \
        --query properties.outputs.acrName.value | tr -d '"')
        echo "ACR_NAME=$ACR_NAME" >> $GITHUB_ENV
        ACR_LOGIN_SERVER=$(az deployment group show -g ${{ env.AZURE_RESOURCE_GROUP_NAME }} -n acr \
        --query properties.outputs.acrLoginServer.value | tr -d '"')
        echo "ACR_LOGIN_SERVER=$ACR_LOGIN_SERVER" >> $GITHUB_ENV

    - name: Prepare Docker buildx
      uses: docker/setup-buildx-action@v1

    - name: Login to ACR
      run: |
        access_token=$(az account get-access-token --query accessToken -o tsv)
        refresh_token=$(curl https://${{ env.ACR_LOGIN_SERVER }}/oauth2/exchange -v \
        -d "grant_type=access_token&service=${{ env.ACR_LOGIN_SERVER }}&access_token=$access_token" | jq -r .refresh_token)
        # The null GUID 0000... tells the container registry that this is an ACR refresh token during the login flow
        docker login -u 00000000-0000-0000-0000-000000000000 \
        --password-stdin ${{ env.ACR_LOGIN_SERVER }} <<< "$refresh_token"

    - name: Build and push Silo image to registry
      uses: docker/build-push-action@v2
      with:
        push: true
        tags: ${{ env.ACR_LOGIN_SERVER }}/${{ env.SILO_IMAGE_NAME }}:${{ github.sha }}
        file: Silo/Dockerfile

    - name: Flex ACA Bicep
      run: |
        az deployment group create \
          --resource-group ${{ env.AZURE_RESOURCE_GROUP_NAME }} \
          --template-file '.github/workflows/flex/main.bicep' \
          --parameters location=${{ env.AZURE_RESOURCE_GROUP_LOCATION }} \
            appName=${{ env.UNIQUE_APP_NAME }} \
            acrName=${{ env.ACR_NAME }} \
            repositoryImage=${{ env.ACR_LOGIN_SERVER }}/${{ env.SILO_IMAGE_NAME }}:${{ github.sha }} \
          --debug

    - name: Get Container App URL
      run: |
        ACA_URL=$(az deployment group show -g ${{ env.AZURE_RESOURCE_GROUP_NAME }} \
        -n main --query properties.outputs.acaUrl.value | tr -d '"')
        echo $ACA_URL

    - name: Logout of Azure
      run: az logout

上記の GitHub ワークフローでは、次の処理が行われます。

  • dotnet publish コマンドを使って、ショッピング カート アプリを zip ファイルとして発行します。
  • サービス プリンシパルの作成」の手順で作成した資格情報を使って Azure にログインします。
  • acr.bicep ファイルを評価し、az deployment group create を使ってデプロイ グループを開始します。
  • デプロイ グループから Azure Container Registry (ACR) ログイン サーバーを取得します。
  • リポジトリ AZURE_CREDENTIALS シークレットを使用して ACR にログインします。
  • サイロ イメージをビルドして ACR に発行します。
  • main.bicep ファイルを評価し、az deployment group create を使って配置グループを開始します。
  • サイロをデプロイする
  • Azure からログアウトします。

ワークフローは、"メイン" ブランチへのプッシュによってトリガーされます。 詳細については、「GitHub Actions と .NET」を参照してください。

ヒント

ワークフローの実行時に問題が発生した場合は、サービス プリンシパルに必要なすべてのプロバイダーの名前空間が登録されていることを確認する必要があります。 次のプロバイダーの名前空間が必要です。

  • Microsoft.App
  • Microsoft.ContainerRegistry
  • Microsoft.Insights
  • Microsoft.OperationalInsights
  • Microsoft.Storage

詳細については、「リソース プロバイダー登録エラーの解決」を参照してください。

Azure では、リソースに対する名前付けの制限事項と規則が適用されます。 次に対して deploy.yml ファイルの値を更新する必要があります。

  • UNIQUE_APP_NAME
  • SILO_IMAGE_NAME
  • AZURE_RESOURCE_GROUP_NAME
  • AZURE_RESOURCE_GROUP_LOCATION

これらの値を、使用する一意のアプリ名、Azure リソース グループ名、場所に設定します。

詳細については、「Azure リソースの名前付け規則と制限事項」を参照してください。

Bicep テンプレートを調べる

az deployment group create コマンドを実行すると、指定された .bicep ファイル参照が評価されます。 このファイルには、デプロイする Azure リソースの詳細な宣言情報が含まれています。 この手順は、デプロイのためにすべてのリソースを "プロビジョニング" すると考えることもできます。

重要

Visual Studio Code を使っている場合、Bicep 機能拡張を使用すると Bicep 作成エクスペリエンスが向上します。

評価される最初の Bicep ファイルは acr.bicep ファイルです。 このファイルには、Azure Container Registry (ACR) ログイン サーバー リソースの詳細が含まれています。

param location string = resourceGroup().location

resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
  name: toLower('${uniqueString(resourceGroup().id)}acr')
  location: location
  sku: {
    name: 'Basic'
  }
  properties: {
    adminUserEnabled: true
  }
}

output acrLoginServer string = acr.properties.loginServer
output acrName string = acr.name

この bicep ファイルでは、ACR ログイン サーバーと対応する名前が出力されます。 検出された次の Bicep ファイルに含まれるのは、単なる 1 つの resource ではありません。 主に module 定義の委任で構成される main.bicep ファイルを考えてみましょう。

param appName string
param acrName string
param repositoryImage string
param location string = resourceGroup().location

resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' existing = {
  name: acrName
}

module env 'environment.bicep' = {
  name: 'containerAppEnvironment'
  params: {
    location: location
    operationalInsightsName: '${appName}-logs'
    appInsightsName: '${appName}-insights'
  }
}

var envVars = [
  {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
  }
  {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
  }
  {
    name: 'ORLEANS_AZURE_STORAGE_CONNECTION_STRING'
    value: storageModule.outputs.connectionString
  }
  {
    name: 'ASPNETCORE_FORWARDEDHEADERS_ENABLED'
    value: 'true'
  }
]

module storageModule 'storage.bicep' = {
  name: 'orleansStorageModule'
  params: {
    name: '${appName}storage'
    location: location
  }
}

module siloModule 'container-app.bicep' = {
  name: 'orleansSiloModule'
  params: {
    appName: appName
    location: location
    containerAppEnvironmentId: env.outputs.id
    repositoryImage: repositoryImage
    registry: acr.properties.loginServer
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    envVars: envVars
  }
}

output acaUrl string = siloModule.outputs.acaUrl

上記の Bicep ファイル:

  • existing ACR リソースを参照します。詳細については、「Azure Bicep: 既存のリソース」を参照してください。
  • environment.bicep 定義ファイルに委任する module env を定義します。
  • storage.bicep 定義ファイルに委任する module storageModule を定義します。
  • サイロ モジュールによって使用される複数の共有 envVars を宣言します。
  • container-app.bicep 定義ファイルに委任する module siloModule を定義します。
  • ACA URL を出力します (これは、既存の AAD B2C アプリ登録のリダイレクト URI を更新するために使用される可能性があります)。

main.bicep は他のいくつかの Bicep ファイルに委任されます。 最初は environment.bicep ファイルです。

param operationalInsightsName string
param appInsightsName string
param location string

resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
  name: appInsightsName
  location: location
  kind: 'web'
  properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
  }
}

resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
  name: operationalInsightsName
  location: location
  properties: {
    retentionInDays: 30
    features: {
      searchVersion: 1
    }
    sku: {
      name: 'PerGB2018'
    }
  }
}

resource env 'Microsoft.App/managedEnvironments@2022-03-01' = {
  name: '${resourceGroup().name}env'
  location: location
  properties: {
    appLogsConfiguration: {
      destination: 'log-analytics'
      logAnalyticsConfiguration: {
        customerId: logs.properties.customerId
        sharedKey: logs.listKeys().primarySharedKey
      }
    }
  }
}

output id string = env.id
output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
output appInsightsConnectionString string = appInsights.properties.ConnectionString

この bicep ファイルでは、Azure Log Analytics と Application Insights のリソースが定義されています。 appInsights のリソースは web の種類であり、logs のリソースは PerGB2018 の種類です。 appInsights リソースと logs リソースは、両方ともリソース グループの場所にプロビジョニングされます。 appInsights リソースは、WorkspaceResourceId プロパティを使用して logs リソースにリンクされます。 この bicep で定義されている出力は 3 つあり、後で Container Apps module によって使用されます。 次に、storage.bicep ファイルを見てみましょう。

param name string
param location string

resource storage 'Microsoft.Storage/storageAccounts@2021-08-01' = {
  name: name
  location: location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
}

var key = listKeys(storage.name, storage.apiVersion).keys[0].value
var protocol = 'DefaultEndpointsProtocol=https'
var accountBits = 'AccountName=${storage.name};AccountKey=${key}'
var endpointSuffix = 'EndpointSuffix=${environment().suffixes.storage}'

output connectionString string = '${protocol};${accountBits};${endpointSuffix}'

上の Bicep ファイルでは次のものが定義されています。

  • リソース グループ名とアプリ名のための 2 つのパラメーター。
  • ストレージ アカウントの resource storage 定義。
  • ストレージ アカウントの接続文字列を構築する 1 つの output

最後の Bicep ファイルは container-app.bicep ファイルです。

param appName string
param location string
param containerAppEnvironmentId string
param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
param envVars array = []
param registry string
param registryUsername string
@secure()
param registryPassword string

resource containerApp 'Microsoft.App/containerApps@2022-03-01' = {
  name: appName
  location: location
  properties: {
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
      activeRevisionsMode: 'multiple'
      secrets: [
        {
          name: 'container-registry-password'
          value: registryPassword
        }
      ]
      registries: [
        {
          server: registry
          username: registryUsername
          passwordSecretRef: 'container-registry-password'
        }
      ]
      ingress: {
        external: true
        targetPort: 80
      }
    }
    template: {
      revisionSuffix: uniqueString(repositoryImage, appName)
      containers: [
        {
          image: repositoryImage
          name: appName
          env: envVars
        }
      ]
      scale: {
        minReplicas: 1
        maxReplicas: 1
      }
    }
  }
}

output acaUrl string = containerApp.properties.configuration.ingress.fqdn

前述の Bicep 用 Visual Studio Code 拡張機能には、ビジュアライザーが含まれています。 これらすべての Bicep ファイルは、次のように視覚化されます。

Orleans: ショッピング カート サンプル アプリの Bicep プロビジョニング ビジュアライザー レンダリング。

まとめ

ソース コードを更新し、変更をリポジトリの main ブランチに push すると、deploy.yml ワークフローが実行されます。 Bicep ファイルで定義されている Azure リソースをプロビジョニングし、アプリケーションをデプロイします。 リビジョンは、Azure Container Registry に自動的に登録されます。

Bicep 拡張機能のビジュアライザーに加えて、アプリケーションをプロビジョニングしてデプロイした後の Azure portal のリソース グループ ページは次の例のようになります。

Azure Portal: Azure Container Apps 用 Orleans ショッピング カート サンプル アプリのリソース。

関連項目