Azure App Service に Orleans をデプロイする
このチュートリアルでは、Azure App Service に Orleans ショッピング カート アプリをデプロイする方法を学習します。 チュートリアルでは、次の機能をサポートするサンプル アプリケーションについて説明します。
ショッピング カート: クロスプラットフォーム フレームワークのサポートとスケーラブルな分散アプリケーション機能のために Orleans を使用する、シンプルなショッピング カート アプリケーション。
- 在庫管理: 商品在庫の編集や作成を行います。
- 在庫の購入: 購入可能な商品を調べてカートに追加します。
- カート: カート内の全項目の概要を表示し、それらの項目を管理します。各項目を削除したり、数量を変更したりします。
アプリとその機能について理解したら、GitHub Actions、.NET と Azure CLI、Azure Bicep を使ってアプリを Azure App Service にデプロイする方法について説明します。 さらに、Azure 内でアプリ用の仮想ネットワークを構成する方法について説明します。
このチュートリアルでは、次の作業を行う方法について説明します。
- Azure App Service に Orleans アプリケーションをデプロイする
- GitHub Actions と Azure Bicep を使ってデプロイを自動化する
- Azure 内でアプリ用の仮想ネットワークを構成する
前提条件
- GitHub アカウント
- Orleans の概要を確認する
- .NET 6 SDK
- Azure CLI
- .NET 統合開発環境 (IDE)
- Visual Studio または Visual Studio Code を自由にお使いください
アプリをローカルで実行する
アプリをローカルで実行するには、Azure Samples: Orleans Cluster on Azure App Service リポジトリをフォークし、ローカル コンピューターにクローンします。 クローンしたら、お好みの 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 キーを押してください。
ショッピング カート アプリの内部
Orleans は、分散アプリケーションを構築するための信頼性が高くスケーラブルなフレームワークです。 このチュートリアルでは、Orleans を使用して構築されたシンプルなショッピング カート アプリを Azure App Service にデプロイします。 このアプリでは、在庫の管理、カート内の項目の追加と削除、利用可能な商品の購入を行う機能が公開されます。 クライアントは、Blazor とサーバー ホスティング モデルを使用して構築されます。 アプリのアーキテクチャは次のようになります。
上の図は、クライアントがサーバー側の Blazor アプリであることを示しています。 これは、対応する Orleans グレインを使用したいくつかのサービスで構成されています。 各サービスは、次のように Orleans グレインとペアになっています。
InventoryService
:IInventoryGrain
を使用し、在庫を商品カテゴリ別に分割します。ProductService
:IProductGrain
を使用し、1 つの商品を 1 つのグレイン インスタンスにId
で結び付けます。ShoppingCartService
:IShoppingCartGrain
を使用し、使用するクライアントに関係なく、1 人のユーザーが 1 つのショッピング カート インスタンスのみを持つようにします。
ソリューションには 3 つのプロジェクトが含まれています。
Orleans.ShoppingCart.Abstractions
: アプリのモデルとインターフェイスを定義するクラス ライブラリ。Orleans.ShoppingCart.Grains
: アプリのビジネス ロジックを実装するグレインを定義するクラス ライブラリ。Orleans.ShoppingCart.Silos
: Orleans サイロをホストするサーバー側 Blazor アプリ。
クライアントのユーザー エクスペリエンス
ショッピング カートのクライアント アプリには複数のページがあり、それぞれが異なるユーザー エクスペリエンスを提供します。 アプリの UI は、MudBlazor NuGet パッケージを使用して構築されています。
ホーム ページ
ユーザーがアプリの目的を理解し、ナビゲーション メニューの各項目の意味を説明するための、いくつかの簡単なフレーズ。
在庫の購入ページ
購入可能なすべての商品を表示するページ。 このページからカートに項目を追加できます。
空のカート ページ
カートに何も追加していない場合、カートに項目がないことを示すメッセージがページに表示されます。
在庫の購入ページでカートに追加された項目
在庫の購入ページでカートに項目が追加されると、その項目がカートに追加されたことを示すメッセージがアプリに表示されます。
商品管理ページ
ユーザーはこのページから在庫を管理できます。 商品を追加したり、編集したり、在庫から削除したりできます。
商品管理ページの新規作成ダイアログ
ユーザーが [Create new product](新しい商品の作成) ボタンをクリックすると、新しい商品を作成できるダイアログがアプリによって表示されます。
カート ページの項目
カートに項目が入っている場合は、それらを表示して数量を変更したり、カートから削除したりすることもできます。 ユーザーには、カート内の項目の概要と税込み合計価格が表示されます。
重要
このアプリをローカルの開発環境内で実行する場合、アプリでは localhost クラスタリング、インメモリ ストレージ、ローカル サイロが使用されます。 また、在庫は、Bogus NuGet パッケージを使用して自動的に生成される架空のデータによって設定されます。 これはすべて意図的であり、機能のデモンストレーションを行うことが目的です。
Azure App Service に配置する
一般的な Orleans アプリケーションは、グレインが存在するサーバー プロセス (サイロ) のクラスターと、外部要求を受信し、グレイン メソッドの呼び出しに変換して結果を返す、一連のクライアント プロセス (通常は Web サーバー) で構成されます。 そのため、Orleans アプリケーションを実行するために最初に行う必要があるのは、サイロのクラスターを起動することです。 テスト目的のために、クラスターを 1 つのサイロで構成することができます。
注意
信頼性の高い運用環境のデプロイでは、フォールト トレランスとスケーリングのために、クラスター内に複数のサイロが必要です。
アプリをデプロイする前に、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: 「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 App Service に .NET アプリをデプロイする方法は多数あります。 このチュートリアルでは、GitHub Actions、Azure Bicep、.NET と Azure CLI を使用します。 GitHub リポジトリのルートにある ./github/workflows/deploy.yml ファイルについて考えてみましょう。
name: Deploy to Azure App Service
on:
push:
branches:
- main
env:
UNIQUE_APP_NAME: cartify
AZURE_RESOURCE_GROUP_NAME: orleans-resourcegroup
AZURE_RESOURCE_GROUP_LOCATION: centralus
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 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 }} \
--debug
- name: Webapp deploy
run: |
az webapp deploy --name ${{ env.UNIQUE_APP_NAME }} \
--resource-group ${{ env.AZURE_RESOURCE_GROUP_NAME }} \
--clean true --restart true \
--type zip --src-path silo.zip --debug
上記の GitHub ワークフローでは、次の処理が行われます。
- dotnet publish コマンドを使って、ショッピング カート アプリを zip ファイルとして発行します。
- 「サービス プリンシパルの作成」の手順で作成した資格情報を使って Azure にログインします。
- main.bicep ファイルを評価し、az deployment group create を使って配置グループを開始します。
- az webapp deploy を使って、Azure App Service に silo.zip ファイルをデプロイします。
ワークフローは、"メイン" ブランチへのプッシュによってトリガーされます。 詳細については、「GitHub Actions と .NET」を参照してください。
ヒント
ワークフローの実行時に問題が発生した場合は、サービス プリンシパルに必要なすべてのプロバイダーの名前空間が登録されていることを確認する必要があります。 次のプロバイダーの名前空間が必要です。
Microsoft.Web
Microsoft.Network
Microsoft.OperationalInsights
Microsoft.Insights
Microsoft.Storage
詳細については、「リソース プロバイダー登録エラーの解決」を参照してください。
Azure では、リソースに対する名前付けの制限事項と規則が適用されます。 次に対して deploy.yml ファイルの値を更新する必要があります。
UNIQUE_APP_NAME
AZURE_RESOURCE_GROUP_NAME
AZURE_RESOURCE_GROUP_LOCATION
これらの値を、使用する一意のアプリ名、Azure リソース グループ名、場所に設定します。
詳細については、「Azure リソースの名前付け規則と制限事項」を参照してください。
Bicep テンプレートを調べる
az deployment group create
コマンドを実行すると、main.bicep ファイルが評価されます。 このファイルには、デプロイする Azure リソースが含まれています。 この手順は、デプロイのためにすべてのリソースを "プロビジョニング" すると考えることもできます。
重要
Visual Studio Code を使用している場合は、Bicep 拡張機能を使うと bicep の作成エクスペリエンスが向上します。
多くの bicep ファイルがあり、それぞれにリソースまたはモジュール (リソースのコレクション) が含まれています。 main.bicep ファイルはエントリ ポイントであり、主に module
の定義で構成されます。
param appName string
param location string = resourceGroup().location
module storageModule 'storage.bicep' = {
name: 'orleansStorageModule'
params: {
name: '${appName}storage'
location: location
}
}
module logsModule 'logs-and-insights.bicep' = {
name: 'orleansLogModule'
params: {
operationalInsightsName: '${appName}-logs'
appInsightsName: '${appName}-insights'
location: location
}
}
resource vnet 'Microsoft.Network/virtualNetworks@2021-05-01' = {
name: '${appName}-vnet'
location: location
properties: {
addressSpace: {
addressPrefixes: [
'172.17.0.0/16'
]
}
subnets: [
{
name: 'default'
properties: {
addressPrefix: '172.17.0.0/24'
delegations: [
{
name: 'delegation'
properties: {
serviceName: 'Microsoft.Web/serverFarms'
}
}
]
}
}
]
}
}
module siloModule 'app-service.bicep' = {
name: 'orleansSiloModule'
params: {
appName: appName
location: location
vnetSubnetId: vnet.properties.subnets[0].id
appInsightsConnectionString: logsModule.outputs.appInsightsConnectionString
appInsightsInstrumentationKey: logsModule.outputs.appInsightsInstrumentationKey
storageConnectionString: storageModule.outputs.connectionString
}
}
上記の bicep ファイルでは、次のものが定義されています。
- リソース グループ名とアプリ名のための 2 つのパラメーター。
storageModule
の定義。ストレージ アカウントを定義します。logsModule
の定義。Azure Log Analytics と Application Insights のリソースを定義します。vnet
リソース。仮想ネットワークを定義します。siloModule
の定義。Azure App Service を定義します。
1 つの非常に重要な resource
は、Virtual Network のものです。 vnet
リソースによって、Azure App Service が Orleans クラスターと通信できるようになります。
bicep ファイル内で module
が検出されるたびに、それはそのリソース定義を含む別の bicep ファイルを介して評価されます。 最初に検出されたモジュールは storageModule
でした。これは 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 ファイルではパラメーターを指定できます。それは param
キーワードを使って宣言します。 同様に、output
キーワードを使って出力を宣言することもできます。 ストレージの resource
は、Microsoft.Storage/storageAccounts@2021-08-01
の種類とバージョンに依存します。 これはリソース グループの場所に、StorageV2
および Standard_LRS
SKU としてプロビジョニングされます。 ストレージの bicep では、その接続文字列が output
として定義されます。 この connectionString
は、ストレージ アカウントに接続するために、後でサイロ bicep によって使用されます。
次に、logs-and-insights.bicep ファイルでは、Azure Log Analytics と Application Insights のリソースが定義されます。
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'
}
}
}
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 では、後ほど App Service の module
で使用する 2 つの出力が定義されています。
最後に、app-service.bicep ファイルでは、Azure App Service のリソースが定義されます。
param appName string
param location string
param vnetSubnetId string
param appInsightsInstrumentationKey string
param appInsightsConnectionString string
param storageConnectionString string
resource appServicePlan 'Microsoft.Web/serverfarms@2021-03-01' = {
name: '${appName}-plan'
location: location
kind: 'app'
sku: {
name: 'S1'
capacity: 1
}
}
resource appService 'Microsoft.Web/sites@2021-03-01' = {
name: appName
location: location
kind: 'app'
properties: {
serverFarmId: appServicePlan.id
virtualNetworkSubnetId: vnetSubnetId
httpsOnly: true
siteConfig: {
vnetPrivatePortsCount: 2
webSocketsEnabled: true
netFrameworkVersion: 'v6.0'
appSettings: [
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: appInsightsInstrumentationKey
}
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: appInsightsConnectionString
}
{
name: 'ORLEANS_AZURE_STORAGE_CONNECTION_STRING'
value: storageConnectionString
}
]
alwaysOn: true
}
}
}
resource appServiceConfig 'Microsoft.Web/sites/config@2021-03-01' = {
name: '${appService.name}/metadata'
properties: {
CURRENT_STACK: 'dotnet'
}
}
この bicep ファイルでは、Azure App Service を .NET 6 アプリケーションとして構成しています。 appServicePlan
リソースと appService
リソースは、両方ともリソース グループの場所にプロビジョニングされます。 appService
リソースは S1
SKU を使用するように構成されており、容量は 1
です。 さらに、リソースは vnetSubnetId
サブネットを使用し、HTTPS を使用するように構成されています。 また、appInsightsInstrumentationKey
インストルメンテーション キー、appInsightsConnectionString
接続文字列、storageConnectionString
接続文字列も構成されています。 これらはショッピング カート アプリで使用されます。
前述の Bicep 用 Visual Studio Code 拡張機能には、ビジュアライザーが含まれています。 これらすべての bicep ファイルは、次のように視覚化されます。
まとめ
ソース コードを更新し、変更をリポジトリの main
ブランチに push
すると、deploy.yml ワークフローが実行されます。 各 bicep ファイルで定義されているリソースがプロビジョニングされ、アプリケーションがデプロイされます。 このアプリケーションを拡張して、認証などの新機能を追加したり、アプリケーションの複数のインスタンスをサポートしたりできます。 このワークフローの主な目的は、リソースのプロビジョニングとデプロイを 1 つのステップで行う機能を示すことです。
bicep 拡張機能のビジュアライザーに加えて、アプリケーションをプロビジョニングしてデプロイした後の Azure portal のリソース グループ ページは次の例のようになります。