Microsoft Build 2022 で Azure Container Apps が GA されました。東日本リージョンでも使えるようになりましたので、Dapr sidecar を使ってバックエンドサービスを呼び出してみました。
Azure Container Apps の Dapr integration
マイクロサービスで構成されるシステムを構築する場合、分散されたサービス群を管理するためのアーキテクチャや運用の複雑さが増す傾向にあります。
分散アーキテクチャのメリットを活かしながら複雑さを最小限に抑える手法として、Dapr を使ったアプリケーション構築が注目されています。
Dapr はマイクロサービスの sidecar パターンを使っているため、分散アーキテクチャの非機能要件をアプリケーションとは疎結合に切り離して実装できます。
- サービス呼び出し
- 状態管理
- 可観測性
- シークレット管理
- Pub / Sub メッセージング など
今回のサービス呼び出しでは、次のような非機能要件への対応が必要となります。
- 他サービスが配置されている場所(URL)の管理
- 一時的な障害が発生した場合のリトライ
- 分散トレーシング
Azure Container Apps では、フルマネージドで管理される Dapr が統合されているため、シンプルに Dapr sidecar をリバース プロキシとして使うことができます。
docs.microsoft.com
Container Apps Environment を作成する
Bicep を使って、App Service Plan にあたる Environment を作成します。
詳細は、こちらの記事を参照してください。
gooner.hateblo.jp
バックエンドサービスを作成する
バックエンドサービスとして、ASP.NET Core Web API を作成します。プロジェクトテンプレートで作成される WeatherForecast API を使います。
Dapr sidecar では HTTPS 通信させることもできますが、今回は [HTTPS 用の構成] チェックボックスは OFF
にしておきます。
Dockerfile を追加し、Ducker Hub に Image をプッシュしておきます。
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /code COPY . . RUN dotnet restore RUN dotnet publish --output /output --configuration Release FROM mcr.microsoft.com/dotnet/aspnet:6.0 COPY --from=build /output /app WORKDIR /app ENTRYPOINT ["dotnet", "WebApp.dll"]
フロントエンドサービスを作成する
フロントエンドサービスとして、ASP.NET Core Web アプリケーションを作成します。
Dapr sidecar でサービスを呼び出すため、Dapr の ASP.NET Core 用ライブラリをインストールします。
www.nuget.org
$ Install-Package Dapr.AspNetCore -Version 1.5.0
Program.cs で、DaprClient
クラスのインスタンスを DI コンテナーに登録します。
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers().AddDapr(); // ←この行を追加 builder.Services.AddRazorPages();
バックエンドサービスの API レスポンスを受け取るモデルクラスを追加します。
public class WeatherForecast { public DateTime Date { get; set; } public int TemperatureC { get; set; } public int TemperatureF { get; set; } public string Summary { get; set; } }
Dapr sidecar でサービス呼び出すページを追加し、DaprClient クラスを使って HttpClient クラスを生成します。Method Invoke や gRPC で呼び出すこともできます。
Web API の呼び出しでは、Dapr に登録する app id の dapr-backend
を使った URL を指定しています。Dapr sidecar をリバース プロキシに使うことで、バックエンドサービスの場所を管理する必要がなくなります。
public class DaprModel : PageModel { private readonly DaprClient _daprClient; public DaprModel(DaprClient daprClient) { _daprClient = daprClient; } public async Task OnGet() { var url = "http://dapr-backend/WeatherForecast"; var httpClient = DaprClient.CreateInvokeHttpClient(); var json = await httpClient.GetStringAsync(url); var options = new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; var forecasts = JsonSerializer.Deserialize<IEnumerable<WeatherForecast>>(json, options); ViewData["WeatherForecastData"] = forecasts; } }
WeatherForecast API のレスポンスを View にバインドします。
@page @using WebApp.Models @model WebApp.Pages.DaprModel @{ ViewData["Title"] = "Dapr page"; } <div class="text-center"> <h1 class="display-4">Service to Service calls</h1> @foreach (var forecast in (IEnumerable<WeatherForecast>)ViewData["WeatherForecastData"]) { <p>The forecast for @forecast.Date is @forecast.Summary!</p> } </div>
ここまでのコードが書けたら、バックエンドサービスと同様に Dockerfile を追加し、Ducker Hub に Image をプッシュしておきます。
Container Apps をデプロイする
Bicep を使って、Container Apps をデプロイします。
まずは、バックエンドサービスです。Environment はデプロイ済みなので、apps.bicep でデプロイします。
param containerAppName string param location string = resourceGroup().location param environmentId string param containerImage string param revisionSuffix string param isExternalIngress bool param isDaprenabled bool param daprAppId string @allowed([ 'multiple' 'single' ]) param revisionMode string = 'single' resource containerApp 'Microsoft.App/containerApps@2022-03-01' = { name: containerAppName location: location properties: { managedEnvironmentId: environmentId configuration: { activeRevisionsMode: revisionMode ingress: { external: isExternalIngress targetPort: 80 transport: 'auto' allowInsecure: false } dapr: { enabled: isDaprenabled appId: daprAppId appPort: 80 appProtocol: 'http' } } template: { revisionSuffix: revisionSuffix containers: [ { image: containerImage name: containerAppName resources: { cpu: '0.25' memory: '0.5Gi' } } ] scale: { minReplicas: 1 maxReplicas: 10 rules: [ { name: 'http-scaling-rule' http: { metadata: { concurrentRequests: '10' } } } ] } } } } output fqdn string = containerApp.properties.configuration.ingress.fqdn
main.bicep では、Dapr を有効化したいので isDaprenabled
を false
にして daprAppId
を指定します。
同一 Environment に配置されたサービスからのみ呼び出されることを想定しているので、isExternalIngress
を false
にしています。
param environmentName string = 'env-${resourceGroup().name}' param containerAppName string = 'dapr-backend' param location string = resourceGroup().location param containerImage string = 'thara0402/dapr-backend:0.1.0' param revisionSuffix string = '' param isExternalIngress bool = false param revisionMode string = 'single' param isDaprenabled bool = true param daprAppId string = 'dapr-backend' resource environment 'Microsoft.App/managedEnvironments@2022-03-01' existing = { name: environmentName } module apps 'apps.bicep' = { name: 'container-apps' params: { containerAppName: containerAppName location: location environmentId: environment.id containerImage: containerImage revisionSuffix: revisionSuffix revisionMode: revisionMode isExternalIngress: isExternalIngress isDaprenabled: isDaprenabled daprAppId: daprAppId } }
Azure CLI を使って、デプロイします。
$ az deployment group create -f ./deploy/app-dapr/main.bicep -g <ResourceGroup Name>
デプロイした結果は、このように Azure ポータルで確認できます。
続いて、フロントエンドサービスをデプロイします。main.bicep で Dapr を構成している部分はバックエンドサービスと同じですが、daprAppId
を変更しています。
ブラウザからアクセスされる Web アプリケーションなので、isExternalIngress
を true
にしています。
バックエンドサービスと同様に、Azure CLI を使ってデプロイします。
param environmentName string = 'env-${resourceGroup().name}' param containerAppName string = 'dapr-frontend' param location string = resourceGroup().location param containerImage string = 'thara0402/dapr-frontend:0.9.0' param revisionSuffix string = '' param isExternalIngress bool = true param revisionMode string = 'single' param isDaprenabled bool = true param daprAppId string = 'dapr-frontend' resource environment 'Microsoft.App/managedEnvironments@2022-03-01' existing = { name: environmentName } module apps 'apps.bicep' = { name: 'container-apps' params: { containerAppName: containerAppName location: location environmentId: environment.id containerImage: containerImage revisionSuffix: revisionSuffix revisionMode: revisionMode isExternalIngress: isExternalIngress isDaprenabled: isDaprenabled daprAppId: daprAppId } }
結果確認
フロントエンドの Web アプリケーションを開いて、バックエンドの WeatherForecast API を呼び出します。
Container Apps Environment を作成する際に、Application Insights を関連付けているので、Application Map でこのような結果が表示されます。
まとめ
Azure Container Apps で Dapr sidecar を使ってバックエンドサービスを呼び出してみました。Kubernetes や Dapr のインフラ周りを気にせず、シンプルに Dapr sidecar をリバース プロキシとして使うことができます。
Azure 環境はシンプルになりましたが、ローカル開発環境では Dapr CLI と Docker Compose を使うなどの煩雑さが残ります。Azure Container Apps 向けの Project Tye とか出来ると良さそうですね。
今回のソースコードは、こちらのリポジトリで公開しています。
github.com
github.com