アスパイア は、クラウドでの分散アプリケーションの開発を簡素化するオピニオンスタックです。 Azure Functions とアスパイアの統合により、Azure Functions .NET プロジェクトを開発、デバッグ、調整して、アスパイア アプリ ホストの一部として行うことができます。
[前提条件]
Azure Functions とアスパイアを使用するための開発環境を設定します。
-
アスパイアの前提条件をインストールします。
- Azure Functions 統合を完全にサポートするには、Asper 13.1 以降が必要です。 Aspire 13.0 には、
Aspire.Hosting.Azure.Functionsのプレビュー バージョンも含まれており、これは公開サポート付きのリリース候補として機能します。
- Azure Functions 統合を完全にサポートするには、Asper 13.1 以降が必要です。 Aspire 13.0 には、
- Azure Functions Core Tools をインストールします。
Visual Studio を使用する場合は、バージョン 17.12 以降に更新してください。 また、Visual Studio 用 Azure Functions ツールの最新バージョンも必要です。 更新プログラムを確認するには:
- [ツール]>[オプション] に移動します。
- [ プロジェクトとソリューション] で、[ Azure Functions] を選択します。
- [更新プログラムの確認] を選択し、指示に従って更新プログラムをインストールします。
ソリューションの構造
Azure Functions と Aspire を使用するソリューションには、 アプリ ホスト プロジェクト と 1 つ以上の Functions プロジェクトを含む複数のプロジェクトがあります。
アプリ ホスト プロジェクトは、アプリケーションのエントリ ポイントです。 Functions プロジェクトを含め、アプリケーションのコンポーネントのセットアップを調整します。
このソリューションには、通常、 サービスの既定の プロジェクトも含まれます。 このプロジェクトには、アプリケーション内のプロジェクト間で使用される既定のサービスと構成のセットが用意されています。
アプリ ホスト プロジェクト
統合を正常に構成するには、アプリ ホスト プロジェクトが次の要件を満たしていることを確認します。
- アプリ ホスト プロジェクトは 、Aspire.Hosting.Azure.Functions を参照する必要があります。 このパッケージは、統合に必要なロジックを定義します。
- アプリ ホスト プロジェクトには、オーケストレーションに含める Functions プロジェクトごとにプロジェクト参照が必要です。
- アプリ ホストの
AppHost.csファイルには、AddAzureFunctionsProject<TProject>()インスタンスでIDistributedApplicationBuilderを呼び出してプロジェクトを含める必要があります。 このメソッドは、アスパイアの他のプロジェクトタイプに使用するAddProject<TProject>()メソッドを使用する代わりに使用します。AddProject<TProject>()を使用すると、Functions プロジェクトを正しく起動できません。
次の例は、アプリ ホスト プロジェクトの最小 AppHost.cs ファイルを示しています。
var builder = DistributedApplication.CreateBuilder(args);
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject");
builder.Build().Run();
Azure Functions プロジェクト
統合を正常に構成するには、Azure Functions プロジェクトが次の要件を満たしていることを確認します。
Functions プロジェクトは、Microsoft.Azure.Functions.Worker と Microsoft.Azure.Functions.Worker.Sdk の 2.x バージョンを参照する必要があります。 また、 Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore 参照を 2.x バージョンに更新する必要があります。
Program.csファイルでは、IHostApplicationBuilderのバージョンを使用する必要があります。 この要件は、FunctionsApplication.CreateBuilder(args)を使用する必要があることを意味します。ソリューションにサービスの既定のプロジェクトが含まれている場合は、Functions プロジェクトがそのプロジェクトを使用するように構成されていることを確認します。
- Functions プロジェクトには、サービスの既定のプロジェクトへのプロジェクト参照が含まれている必要があります。
-
IHostApplicationBuilderでProgram.csをビルドする前に、builder.AddServiceDefaults()の呼び出しを含めます。
次の例は、アスパイアで使用される Functions プロジェクトの最小 Program.cs ファイルを示しています。
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.Hosting;
var builder = FunctionsApplication.CreateBuilder(args);
builder.AddServiceDefaults();
builder.ConfigureFunctionsWebApplication();
builder.Build().Run();
この例には、他の多くの Program.cs の例や Azure Functions テンプレートに表示される既定の Application Insights 構成は含まれていません。 代わりに、 builder.AddServiceDefaults メソッドを呼び出して、Aspire で OpenTelemetry 統合を構成します。
統合を最大限に活用するには、次のガイドラインを考慮してください。
- Functions プロジェクトには、Application Insights の直接統合を含めないでください。 代わりに、アスパイアでの監視は、OpenTelemetry のサポートを通じて処理されます。 サービスの既定のプロジェクトを使用して Azure Monitor にデータをエクスポートするように、Aspire を構成できます。
- Functions プロジェクトの
local.settings.jsonファイルでカスタム アプリ設定を定義しないでください。local.settings.jsonに含める必要がある唯一の設定は、"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"です。 アプリ ホスト プロジェクトを使用して、他のすべてのアプリ構成を設定します。
アスパイアを使用した接続構成
アプリ ホスト プロジェクトはリソースを定義し、コードを使用してそれらの間の接続を作成するのに役立ちます。 このセクションでは、Azure Functions プロジェクトで使用する接続を構成およびカスタマイズする方法について説明します。
アスパイアには、作業の開始に役立つ既定の接続アクセス許可が含まれています。 ただし、これらのアクセス許可は、アプリケーションに適していないか、十分ではない可能性があります。
Azure ロールベースのアクセス制御 (RBAC) を使用するシナリオでは、プロジェクト リソースで WithRoleAssignments() メソッドを呼び出してアクセス許可をカスタマイズできます。
WithRoleAssignments()を呼び出すと、すべての既定のロールの割り当てが削除され、必要な完全なセット ロールの割り当てを明示的に定義する必要があります。 Azure Container Apps でアプリケーションをホストする場合、WithRoleAssignments()を使用するには、AddAzureContainerAppEnvironment()でDistributedApplicationBuilderを呼び出す必要もあります。
Azure Functions ホスト ストレージ
Azure Functions では、いくつかのコア動作にホスト ストレージ接続 (AzureWebJobsStorage) が必要です。 アプリ ホスト プロジェクトで AddAzureFunctionsProject<TProject>() を呼び出すと、既定で AzureWebJobsStorage 接続が作成され、Functions プロジェクトに提供されます。 この既定の接続では、ローカル開発の実行に Azure Storage エミュレーターが使用され、デプロイ時にストレージ アカウントが自動的にプロビジョニングされます。 より詳細な制御を行うには、Functions プロジェクト リソースで .WithHostStorage() を呼び出すことで、この接続を置き換えることができます。
ホスト ストレージ接続に対して設定される既定のアクセス許可は、 WithHostStorage() 呼び出すかどうかによって異なります。
WithHostStorage()を追加すると、ストレージ アカウント共同作成者の割り当てが削除されます。 Aspire がホストストレージ接続に対して設定する既定のアクセス許可を、次の表に示します。
| ホスト ストレージ接続 | 既定のロール |
|---|---|
WithHostStorage() の呼び出しなし |
ストレージ BLOB データ共同作成者、 ストレージ キュー データ共同作成者、 ストレージ テーブル データ コンストリビューター、 ストレージ アカウント共同作成者 |
WithHostStorage() を呼び出す |
ストレージ BLOB データ共同作成者、 ストレージ キュー データ共同作成者、 ストレージ テーブル データ共同作成者 |
次の例は、ホスト ストレージを置き換え、ロールの割り当てを指定するアプリ ホスト プロジェクトの最小 AppHost.cs ファイルを示しています。
using Azure.Provisioning.Storage;
var builder = DistributedApplication.CreateBuilder(args);
builder.AddAzureContainerAppEnvironment("myEnv");
var myHostStorage = builder.AddAzureStorage("myHostStorage");
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithHostStorage(myHostStorage)
.WithRoleAssignments(myHostStorage, StorageBuiltInRole.StorageBlobDataOwner);
builder.Build().Run();
注
ストレージ BLOB データ所有者 は、 ホスト ストレージ接続の基本的なニーズに推奨されるロールです。 BLOB サービスへの接続に、Aspire の既定値であるストレージ BLOB データ共同作成者のみが設定されている場合、アプリで問題が発生する可能性があります。
運用環境のシナリオでは、 WithHostStorage() と WithRoleAssignments()の両方の呼び出しを含めます。 その後、必要な他のロールと共に、このロールを明示的に設定できます。
トリガーとバインドの接続
トリガーとバインドは、名前で接続を参照します。 次のアスパイア統合は、プロジェクト リソースの WithReference() 呼び出しを通じてこれらの接続を提供します。
次の例は、キュー トリガーを構成するアプリ ホスト プロジェクトの最小 AppHost.cs ファイルを示しています。 この例では、対応するキュー トリガーの Connection プロパティが MyQueueTriggerConnection に設定されているため、 WithReference() の呼び出しで名前が指定されます。
var builder = DistributedApplication.CreateBuilder(args);
var myAppStorage = builder.AddAzureStorage("myAppStorage").RunAsEmulator();
var queues = myAppStorage.AddQueues("queues");
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithReference(queues, "MyQueueTriggerConnection");
builder.Build().Run();
他の統合の場合は、 WithReference 呼び出しによって構成が別の方法で設定されます。 この構成は 、アスパイア クライアント統合では使用できますが、トリガーとバインドには使用できません。 これらの統合の場合は、 WithEnvironment() を呼び出して、解決するトリガーまたはバインドの接続情報を渡します。
次の例は、接続文字列式を公開するリソースの環境変数 MyBindingConnection を設定する方法を示しています。
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithEnvironment("MyBindingConnection", otherIntegration.Resource.ConnectionStringExpression);
アスパイア クライアント統合とトリガーとバインドのシステムの両方で接続を使用する場合は、 WithReference() と WithEnvironment()の両方を構成できます。
一部のリソースでは、接続の構造が、ローカルで実行する場合と Azure に発行する場合とで異なる可能性があります。 前の例では、otherIntegration はエミュレーターとして実行されるリソースである可能性があるため、ConnectionStringExpression からエミュレーター接続文字列が返されます。 ただし、リソースが発行されると、Aspire によって ID ベースの接続が設定され、 ConnectionStringExpression はサービスの URI を返す可能性があります。 この場合、 Azure Functions の ID ベースの接続を設定するには、別の環境変数名を指定する必要がある場合があります。
次の例では、必要なサフィックスを条件付きで追加するため builder.ExecutionContext.IsPublishMode が使用されています。
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithEnvironment("MyBindingConnection" + (builder.ExecutionContext.IsPublishMode ? "__serviceUri" : ""), otherIntegration.Resource.ConnectionStringExpression);
各バインドでサポートされる接続形式と、それらの形式に必要なアクセス許可の詳細については、バインディングの 参照ページを参照してください。
アプリケーションのホスト
アスパイアは、Azure で Functions プロジェクトをホストする 2 つの異なる方法をサポートしています。
- コンテナー アプリとして発行する (既定)
- プレビュー版のApp Service 統合を使用して関数アプリとして発行する
どちらの場合も、プロジェクトはコンテナーとしてデプロイされます。 アスパイアは、コンテナーイメージを構築し、それをAzure Container Registryにプッシュします。
コンテナー アプリとして発行する
既定では、アスパイア プロジェクトを Azure に発行すると、Azure Container Apps にデプロイされます。 システムは 、KEDA を使用して Functions プロジェクトのスケーリングルールを設定します。 Azure Container Apps を使用する場合は、ファンクション キーに追加のセットアップが必要です。 詳細については、 Azure Container Apps のアクセス キーに関 するページを参照してください。
Azure Container Apps のアクセス キー
いくつかの Azure Functions シナリオでは、アクセス キーを使用して、不要なアクセスに対する基本的な軽減策を提供します。 たとえば、HTTP トリガー関数は既定でアクセス キーを呼び出す必要がありますが、この要件は AuthLevel プロパティを使用して無効にすることができます。 キーが必要になる可能性があるシナリオについては、「 Azure Functions でのアクセス キーの操作 」を参照してください。
Aspire を使用して Functions プロジェクトを Azure Container Apps にデプロイする場合、システムは Functions アクセス キーを自動的に作成または管理しません。 アクセス キーを使用する必要がある場合は、アプリ ホストのセットアップの一部として管理できます。 このセクションでは、アプリ ホストの Program.cs ファイルから呼び出してアクセス キーを作成および管理できる拡張メソッドを作成する方法について説明します。 この方法では、Azure Key Vault を使用してキーを格納し、シークレットとしてコンテナー アプリにマウントします。
注
ここでの動作は、 ContainerApps シークレット プロバイダーに依存します。これは、Functions ホスト バージョン 4.1044.0以降でのみ使用できます。 このバージョンは、すべてのリージョンでまだ使用できるわけではありません。また、アスパイア プロジェクトを発行するまで、Functions プロジェクトに使用される基本イメージに必要な変更が含まれていない場合があります。
これらの手順には、Bicep バージョン 0.38.3 以降が必要です。 コマンド プロンプトから bicep --version を実行して、Bicep のバージョンを確認できます。 Azure CLI がインストールされている場合は、 az bicep upgrade を使用して、Bicep を最新バージョンにすばやく更新できます。
アプリ ホスト プロジェクトに次の NuGet パッケージを追加します。
アプリ ホスト プロジェクトに新しいクラスを作成し、次のコードを含めます。
using Aspire.Hosting.Azure;
using Azure.Provisioning.AppContainers;
namespace Aspire.Hosting;
internal static class Extensions
{
private record SecretMapping(string OriginalName, IAzureKeyVaultSecretReference Reference);
public static IResourceBuilder<T> PublishWithContainerAppSecrets<T>(
this IResourceBuilder<T> builder,
IResourceBuilder<AzureKeyVaultResource>? keyVault = null,
string[]? hostKeyNames = null,
string[]? systemKeyExtensionNames = null)
where T : AzureFunctionsProjectResource
{
if (!builder.ApplicationBuilder.ExecutionContext.IsPublishMode)
{
return builder;
}
keyVault ??= builder.ApplicationBuilder.AddAzureKeyVault("functions-keys");
var hostKeysToAdd = (hostKeyNames ?? []).Append("default").Select(k => $"host-function-{k}");
var systemKeysToAdd = systemKeyExtensionNames?.Select(k => $"host-systemKey-{k}_extension") ?? [];
var secrets = hostKeysToAdd.Union(systemKeysToAdd)
.Select(secretName => new SecretMapping(
secretName,
CreateSecretIfNotExists(builder.ApplicationBuilder, keyVault, secretName.Replace("_", "-"))
)).ToList();
return builder
.WithReference(keyVault)
.WithEnvironment("AzureWebJobsSecretStorageType", "ContainerApps")
.PublishAsAzureContainerApp((infra, app) => ConfigureFunctionsContainerApp(infra, app, builder.Resource, secrets));
}
private static void ConfigureFunctionsContainerApp(
AzureResourceInfrastructure infrastructure,
ContainerApp containerApp,
IResource resource,
List<SecretMapping> secrets)
{
const string volumeName = "functions-keys";
const string mountPath = "/run/secrets/functions-keys";
var appIdentityAnnotation = resource.Annotations.OfType<AppIdentityAnnotation>().Last();
var containerAppIdentityId = appIdentityAnnotation.IdentityResource.Id.AsProvisioningParameter(infrastructure);
var containerAppSecretsVolume = new ContainerAppVolume
{
Name = volumeName,
StorageType = ContainerAppStorageType.Secret
};
foreach (var mapping in secrets)
{
var secret = mapping.Reference.AsKeyVaultSecret(infrastructure);
containerApp.Configuration.Secrets.Add(new ContainerAppWritableSecret()
{
Name = mapping.Reference.SecretName.ToLowerInvariant(),
KeyVaultUri = secret.Properties.SecretUri,
Identity = containerAppIdentityId
});
containerAppSecretsVolume.Secrets.Add(new SecretVolumeItem
{
Path = mapping.OriginalName.Replace("-", "."),
SecretRef = mapping.Reference.SecretName.ToLowerInvariant()
});
}
containerApp.Template.Containers[0].Value!.VolumeMounts.Add(new ContainerAppVolumeMount
{
VolumeName = volumeName,
MountPath = mountPath
});
containerApp.Template.Volumes.Add(containerAppSecretsVolume);
}
public static IAzureKeyVaultSecretReference CreateSecretIfNotExists(
IDistributedApplicationBuilder builder,
IResourceBuilder<AzureKeyVaultResource> keyVault,
string secretName)
{
var secretParameter = ParameterResourceBuilderExtensions.CreateDefaultPasswordParameter(builder, $"param-{secretName}", special: false);
builder.AddBicepTemplateString($"key-vault-key-{secretName}", """
param ___location string = resourceGroup().___location
param keyVaultName string
param secretName string
@secure()
param secretValue string
// Reference the existing Key Vault
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = {
name: keyVaultName
}
// Deploy the secret only if it does not already exist
@onlyIfNotExists()
resource newSecret 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = {
parent: keyVault
name: secretName
properties: {
value: secretValue
}
}
""")
.WithParameter("keyVaultName", keyVault.GetOutput("name"))
.WithParameter("secretName", secretName)
.WithParameter("secretValue", secretParameter);
return keyVault.GetSecret(secretName);
}
}
その後、アプリ ホストの Program.cs ファイルで次のメソッドを使用できます。
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithHostStorage(storage)
.WithExternalHttpEndpoints()
.PublishWithContainerAppSecrets(systemKeyExtensionNames: ["mcp"]);
この例では、拡張メソッドによって作成された既定のキー コンテナーを使用します。 その結果、 モデル コンテキスト プロトコル拡張機能で使用する既定のキーとシステム キーが作成されます。
クライアントからこれらのキーを使用するには、キー コンテナーからキーを取得する必要があります。
関数アプリとして発行する
注
関数アプリとして発行するには、現在プレビュー段階の Azure App Service 統合が必要です。
アスパイア Azure App Service 統合を使用して、関数アプリにデプロイするように アスパイアを構成できます。 アスパイアは Functions プロジェクトをコンテナーとして発行するため、関数アプリのホスティング プランでは、コンテナー化されたアプリケーションのデプロイをサポートする必要があります。
関数アプリとしてアスパイア関数プロジェクトを発行するには、次の手順に従います。
- アプリ ホスト プロジェクトで 、Aspire.Hosting.Azure.AppService NuGet パッケージへの参照を追加します。
-
AppHost.csファイルで、AddAzureAppServiceEnvironment()インスタンスのIDistributedApplicationBuilderを呼び出して App Service プランを作成します。 名前に関わらず、App Service Environment リソースはプロビジョニングされないことに注意してください。 - Functions プロジェクト リソースで、
.WithExternalHttpEndpoints()を呼び出します。 これは、Azure App Service 統合を利用してデプロイする場合に必要です。 - Functions プロジェクト リソースで、
.PublishAsAzureAppServiceWebsite((infra, app) => app.Kind = "functionapp,linux")を呼び出して、そのプロジェクトをプランに発行します。
Von Bedeutung
必ず、 app.Kind プロパティを "functionapp,linux" に設定してください。 この設定により、リソースが関数アプリとして作成されます。これは、アプリケーションを操作するためのエクスペリエンスに影響します。
次の例は、Functions プロジェクトを関数アプリとして発行するアプリ ホスト プロジェクトの最小 AppHost.cs ファイルを示しています。
var builder = DistributedApplication.CreateBuilder(args);
builder.AddAzureAppServiceEnvironment("functions-env");
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithExternalHttpEndpoints()
.PublishAsAzureAppServiceWebsite((infra, app) => app.Kind = "functionapp,linux");
この構成では、Premium V3 プランが作成されます。 専用の App Service プラン SKU を使用する場合、スケーリングはイベントベースではありません。 代わりに、スケーリングは App Service プランの設定を通じて管理されます。
考慮事項とベスト プラクティス
Azure Functions と Aspire の統合を評価するときは、次の点を考慮してください。
現在、Aspire を使用したトリガーとバインドの構成は、特定の統合に制限されています。 詳細については、この記事の 「アスパイアとの接続の構成 」を参照してください。
関数プロジェクトの
Program.csファイルでは、IHostApplicationBuilderのバージョンを使用する必要があります。IHostApplicationBuilderを使用すると、builder.AddServiceDefaults()を呼び出して、Aspire サービスの既定値 を Functions プロジェクトに追加できます。アスパイアは、監視に OpenTelemetry を使用します。 サービスの既定のプロジェクトを使用して Azure Monitor にデータをエクスポートするように、Aspire を構成できます。
他の多くの Azure Functions コンテキストでは、ワーカー サービスを登録することで Application Insights との直接統合を含めることができます。 この種の統合は、アスパイアではお勧めしません。 バージョン 2.22.0 の
Microsoft.ApplicationInsights.WorkerServiceでランタイム エラーが発生する可能性がありますが、バージョン 2.23.0 はこの問題に対処します。 アスパイアを使用している場合は、Functions プロジェクトから直接 Application Insights 統合を削除します。アスパイア オーケストレーションに参加している Functions プロジェクトの場合、ほとんどのアプリケーション構成は、アスパイア アプリ ホスト プロジェクトから取得する必要があります。
local.settings.json設定以外は、FUNCTIONS_WORKER_RUNTIMEで設定しないでください。local.settings.jsonとアスパイアで同じ環境変数を設定すると、システムはアスパイアバージョンを使用します。local.settings.json内の接続に対して Azure Storage エミュレーターを構成しないでください。 多くの Functions スターター テンプレートには、AzureWebJobsStorageの既定値としてエミュレーターが含まれています。 ただし、エミュレーターの構成によっては、一部の開発者ツールがエミュレーターの別バージョンを開始することがあります。このバージョンは、Aspireが使用するバージョンと競合する可能性があります。