Aspire 是一條用於建置、執行、除錯及部署分散式應用程式的工具鏈。 Aspire Azure Functions 整合讓你能夠開發、除錯並協調 Azure Functions 專案,作為 Aspire AppHost 的一部分。 本文中的 .NET 範例使用孤立工作者模型。
先決條件
設定您的開發環境,以搭配 Aspire 使用 Azure Functions:
安裝 Aspire 的前置條件,包括 AppHost 所需的 .NET SDK。
從 AppHost 目錄安裝 Aspire Azure Functions 主機整合。
aspire add Aspire.Hosting.Azure.Functions
如果你使用 Visual Studio,請安裝最新的 Visual Studio 和 Azure Functions 工具更新:
- 移至 [工具]>[選項]。
- 在 [項目和解決方案] 底下,選取 [Azure Functions]。
- 選取 [檢查更新],並依提示安裝更新。
欲了解更多關於整合套件及支援的 AppHost API 的資訊,請參閱 App Host 中的 Set Up Azure Functions。
解決方案結構
使用 Azure Functions 和 Aspire 的解決方案包含多個專案,包括一個 AppHost 和一個或多個 Functions 專案。
AppHost 是你應用程式的入口。 它會協調應用程式的元件設定,包括 Functions 專案。
解決方案通常也包含 服務預設 專案。 此專案提供一組預設服務和組態,可在應用程式中的項目之間使用。
AppHost 專案
要成功配置整合,請確保 AppHost 專案符合以下要求:
- AppHost 參考了 Aspire.Hosting.Azure.Functions。 此套件定義了整合。
- C# AppHost 會參考 Functions 專案並呼叫
AddAzureFunctionsProject<TProject>(),或AddAzureFunctionsProject(name, projectPath)以專案檔案路徑呼叫。 TypeScript 應用程式主機使用專案路徑形式。addAzureFunctionsProject - 用
AddAzureFunctionsProject代替AddProject。 使用 Functions 新增AddProject的專案無法正常啟動。
以下範例展示了一個 C# AppHost 專案的最小 AppHost.cs 檔案:
var builder = DistributedApplication.CreateBuilder(args);
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject");
builder.Build().Run();
Azure Functions 專案
若要成功設定整合,請確定 Azure Functions 專案符合下列需求:
目標為 .NET 8 或更新版本,使用 .NET 9 SDK 或更新版本,並使用隔離工作者模型。
參考 Microsoft.Azure.Functions.Worker、Microsoft.Azure.Functions.Worker.Sdk,以及針對 HTTP 觸發器的 Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore。
您的
Program.cs檔案必須使用主機執行個體啟動的IHostApplicationBuilder版本。 這項需求表示您必須使用FunctionsApplication.CreateBuilder(args)。如果您的方案包含服務預設專案,請確定您的 Functions 專案已設定為使用它:
- Functions 專案應該包含對服務預設值專案的參考。
- 在
IHostApplicationBuilder中建置Program.cs之前,請先呼叫builder.AddServiceDefaults()。
下列範例顯示 Aspire 中使用的 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 整合。 Aspire 中的監控則會經由其 OpenTelemetry 支援來處理。 您可以將 Aspire 設定為透過服務預設專案將資料匯出至 Azure 監視器。
- 當 Aspire 執行函式專案時,優先選擇由 AppHost 注入的設定。 你可以在
local.settings.json中保留對應的設定,讓專案能夠透過func start獨立執行;Aspire 注入的環境變數會覆寫這些設定。
與 Aspire 的連線配置
AppHost 定義資源,並透過程式碼協助你建立資源間的連結。 本節說明如何設定和自定義 Azure Functions 專案所使用的連線。
Aspire 包含可協助您入門的預設連線權限。 不過,這些許可權可能不適合或足以供您的應用程式使用。
針對使用 Azure 角色型存取控制 (RBAC) 的案例,您可以在專案資源上呼叫 WithRoleAssignments() 方法來自定義許可權。 當您呼叫 WithRoleAssignments()時,會移除所有預設角色指派,而且您必須明確定義您想要的完整設定角色指派。 如果您在 Azure 容器應用程式 上託管應用程式,則使用 WithRoleAssignments() 時,也需要在 AddAzureContainerAppEnvironment() 上呼叫 DistributedApplicationBuilder。
Azure Functions 主機儲存空間
Azure Functions 需要主機儲存體連線 (AzureWebJobsStorage)以執行多項核心功能。 當您在 AppHost 中呼叫 AddAzureFunctionsProject<TProject>() 時,預設會建立 AzureWebJobsStorage 連線,並將其提供給 Functions 專案。 這個預設連線會使用 Azure 儲存體 模擬器進行本地開發,部署時會自動配置儲存帳號。 如需更多控制權,請在 Functions 專案資源上呼叫 .WithHostStorage() 來取代此連線。
Aspire 為主機儲存連線設定的預設權限,取決於你是否呼叫 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 預設值 Storage Blob 資料參與者,您的應用程式可能會遇到問題。
在生產情境中,包含呼叫WithHostStorage()和WithRoleAssignments()。 然後,您可以明確地設定此角色,以及您需要的任何其他角色。
繫結程序與繫結連線
您的觸發程序和繫結會依名稱參考連線。 下列 Aspire 整合會透過專案資源的WithReference()呼叫來提供這些連線:
| Aspire 整合 | 預設角色 |
|---|---|
| Azure Blob 儲存體 |
儲存體 Blob 資料參與者、 儲存體佇列資料參與者、 儲存表資料貢獻者 |
| Azure 佇列儲存體 |
儲存體 Blob 資料參與者、 儲存體佇列資料參與者、 儲存表資料貢獻者 |
| Azure 事件中樞 | Azure 事件中樞資料擁有者 |
| Azure 服務匯流排 | Azure 服務匯流排資料擁有者 |
以下範例展示了一個 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 會以不同的方式設定組態。 它們使設定可供 Aspire 用戶端整合使用,但不適用於觸發程序和繫結。 針對這些整合,呼叫 WithEnvironment() 以傳遞觸發程序或繫結的連線資訊來解析。
下列範例示範如何為公開連接字串表達式的資源設定環境變數 MyBindingConnection :
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithEnvironment("MyBindingConnection", otherIntegration.Resource.ConnectionStringExpression);
如果您想要 Aspire 用戶端整合以及觸發程式和繫結系統都使用連線,您可以同時配置 WithReference() 和 WithEnvironment()。
針對某些資源,當您在本機執行連線和將連線發佈至 Azure 時,連線的結構可能會有所不同。 在上述範例中,otherIntegration可能是以模擬器身分執行的資源,因此ConnectionStringExpression會傳回模擬器 連接字串。 不過,發佈資源時,Aspire 可能會設定身分識別型連線,並 ConnectionStringExpression 傳回服務的 URI。 在此情況下,若要設定 Azure Functions 的身分識別型連線,您可能需要提供不同的環境變數名稱。
下列範例會使用 builder.ExecutionContext.IsPublishMode 來有條件地新增必要的後綴:
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithEnvironment("MyBindingConnection" + (builder.ExecutionContext.IsPublishMode ? "__serviceUri" : ""), otherIntegration.Resource.ConnectionStringExpression);
如需每個系結所支援連接格式的詳細數據,以及這些格式所需的許可權,請參閱系結的 參考頁面。
欲了解更多關於函數程式碼如何讀取注入的WithReference值的資訊,請參閱 Azure Functions 執行時配置。
裝載應用程式
Aspire 支援 Azure 容器應用程式 部署以支援 Functions 專案。 您也可以使用獨立的預覽版 App Service 整合功能來針對支援容器的函數應用程式:
- 部署為容器應用程式
- 使用預覽版 App Service 整合將其部署為函式應用程式
在這兩種情況下,您的專案都會部署為容器。 Aspire 會為您建置容器映像,並將它推送至 Azure 容器登錄。
部署成容器應用程式
當你的 AppHost 針對 Azure 容器應用程式 時,Aspire 會用 KEDA 為你的 Functions 專案設定擴展規則。 使用 Azure 容器應用程式 時,你需要額外設定函式鍵。 欲了解更多資訊,請參閱 Azure 容器應用程式 上的存取金鑰。
透過執行 aspire deploy. 來部署已設定的 AppHost 欲了解更多資訊,請參閱 Deploy to Azure 容器應用程式 及 aspire deploy。
Azure 容器應用程式上的存取金鑰
數個 Azure Functions 案例會使用存取金鑰來針對不想要的存取提供基本風險降低。 例如,預設情況下,HTTP 觸發函數需要呼叫存取金鑰,但可以使用屬性停用AuthLevel此需求。 如需可能需要金鑰的案例,請參閱在 Azure Functions 中使用存取金鑰 。
當你使用 Aspire 部署 Functions 專案到 Azure 容器應用程式 時,系統不會自動建立或管理 Functions 的存取金鑰。 如果你需要使用存取金鑰,可以在 AppHost 設定中管理它們。 本節會展示如何建立一個擴充功能方法,從你的 AppHost AppHost.cs 檔案中呼叫來建立和管理存取金鑰。 此方法會使用 Azure 金鑰保存庫來儲存金鑰,並將其掛接到容器應用程式中做為秘密。
注意
此處的行為依賴於 ContainerApps 祕密提供者,而該提供者需要 Functions 主機版本 4.1044.0 或更高版本。
這些步驟需要 Bicep 版本 0.38.3 或更新版本。 您可以在命令提示字元中執行 bicep --version 來檢查 Bicep 版本。 如果您已安裝 Azure CLI,則可用來 az bicep upgrade 快速將 Bicep 更新至最新版本。
請將以下 NuGet 套件加入您的 AppHost 專案:
在你的 AppHost 專案中建立一個新類別,並包含以下程式碼:
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);
}
}
接著你可以在 AppHost AppHost.cs 的檔案中使用這個方法:
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithHostStorage(storage)
.WithExternalHttpEndpoints()
.PublishWithContainerAppSecrets(systemKeyExtensionNames: ["mcp"]);
此範例會使用延伸模組方法所建立的預設金鑰保存庫。 它會產生預設金鑰和系統金鑰,以與 模型內容通訊協定延伸搭配使用。
若要從用戶端使用這些金鑰,您必須從金鑰保存庫擷取它們。
部署為函式應用程式
注意
部署為函式應用程式需要使用 Aspire Azure App 服務 整合,而此整合目前為預覽版。
您可以透過使用 Aspire Azure App 服務 整合,將 Aspire 設定為部署至函式應用程式。 由於 Aspire 將 Functions 專案部署為容器,功能應用程式的託管計畫必須支援部署容器化應用程式。
要將您的 Aspire Functions 專案部署為函數應用程式,請遵循以下步驟:
- 從 AppHost 目錄中,執行
aspire add Aspire.Hosting.Azure.AppService以新增 Aspire.Hosting.Azure.AppService NuGet 套件。 - 在
AppHost.cs檔案中,呼叫AddAzureAppServiceEnvironment()執行個體上的IDistributedApplicationBuilder以建立 App Service 方案。 請注意,儘管名稱如此,但這不會佈建 App Service 環境資源。 - 在專案功能資源上,呼叫
.WithExternalHttpEndpoints()。 這是使用 Aspire Azure App 服務 整合進行部署的必要條件。 - 在 Functions 專案資源中,呼叫
.PublishAsAzureAppServiceWebsite((infra, app) => app.Kind = "functionapp,linux")將該專案自訂為計畫中的函式應用程式。
重要
請確定您將 app.Kind 屬性設為 "functionapp,linux"。 此設定可確保將資源建立為函式應用程式,這會影響使用應用程式的體驗。
以下範例展示了一個最小 AppHost.cs 檔案,將 Functions 專案部署為函式應用程式:
var builder = DistributedApplication.CreateBuilder(args);
builder.AddAzureAppServiceEnvironment("functions-env");
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithExternalHttpEndpoints()
.PublishAsAzureAppServiceWebsite((infra, app) => app.Kind = "functionapp,linux");
builder.Build().Run();
此配置會產生 Premium V3 方案。 使用專用 App Service 方案 SKU 時,調整不是以事件為基礎。 相反地,縮放是透過 App Service 計劃設定來管理。
考量和最佳做法
當您評估 Azure Functions 與 Aspire 的整合時,請考慮下列幾點:
透過 Aspire 進行的觸發和繫結設定目前僅限於特定整合。 如需詳細資訊,請參閱本文中的 與 Aspire 的連線設定 。
函式專案的
Program.cs檔案應該使用主機執行個體啟動的IHostApplicationBuilder版本。 透過使用IHostApplicationBuilder,你可以呼叫builder.AddServiceDefaults()將 Aspire Service Defaults 加入你的函式專案。Aspire 使用 OpenTelemetry 進行監控。 您可以將 Aspire 設定為透過服務預設專案將資料匯出至 Azure 監視器。
在許多其他 Azure Functions 的情境中,您可能會藉由註冊背景工作角色服務直接整合 Application Insights。 使用 Aspire Service Defaults 時,請勿註冊第二個直接連接到 Application Insights 的管線。
對於納入 Aspire 編排中的 Functions 專案,AppHost 應負責提供大部分的應用程式組態設定。 你可以使用
local.settings.json搭配func start來獨立執行 Functions 專案。 當 Aspire 執行專案時,Aspire 注入的環境變數會覆蓋 中同名local.settings.json的值。避免啟動第二個 Azure 儲存體 模擬器來處理 AppHost 管理的連線。 彼此競用的模擬器執行個體可能會導致埠和儲存空間衝突。
欲了解更多資訊,請參閱 Azure Functions runtime configuration 及 Aspire telemetry。