Aspire 는 분산 애플리케이션을 구축, 실행, 디버깅, 배포하기 위한 툴체인입니다. Aspire Azure Functions 통합은 Aspire AppHost의 일부로서 Azure Functions 프로젝트를 개발, 디버깅, 조정할 수 있게 해줍니다. 이 글의 .NET 예시들은 고립된 워커 모델을 사용합니다.
필수 조건
Azure Functions를 Aspire와 함께 사용하기 위한 개발 환경을 설정합니다.
AppHost에서 요구하는 .NET SDK를 포함한 Aspire 사전 조건을 설치하세요.
AppHost 디렉터리에서 Aspire Azure Functions 호스팅 통합을 설치하세요.
aspire add Aspire.Hosting.Azure.Functions
Visual Studio를 사용한다면, 최신 Visual Studio와 Azure Functions 툴링 업데이트를 설치하세요:
- 도구>옵션으로 이동합니다.
- 프로젝트 및 솔루션에서 Azure Functions를 선택합니다.
- 업데이트 확인을 선택하고 메시지가 표시되면 업데이트를 설치합니다.
통합 패키지와 지원되는 AppHost API에 대한 자세한 내용은 AppHost의 '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 AppHost는 의addAzureFunctionsProject프로젝트 경로 형식을 사용합니다. -
AddAzureFunctionsProject대신AddProject를 사용합니다. FunctionsAddProject로 추가된 프로젝트가 제대로 시작되지 않습니다.
다음 예시는 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 구성이 포함되지 않습니다. 대신 메서드를 호출하여 Aspire에서 OpenTelemetry 통합을 구성합니다 builder.AddServiceDefaults() .
통합을 최대한 활용하려면 다음 지침을 고려하세요.
- Functions 프로젝트에 직접 Application Insights 통합을 포함하지 마세요. Aspire의 모니터링은 대신 OpenTelemetry 지원을 통해 처리됩니다. 서비스 기본 프로젝트를 통해 Azure Monitor로 데이터를 내보내도록 Aspire를 구성할 수 있습니다.
- Aspire가 Functions 프로젝트를 실행할 때, AppHost가 주입한 설정을 선호하세요.
func start에 동일한 설정을 유지하면local.settings.json로 프로젝트를 독립적으로 실행할 수 있습니다. Aspire에서 주입한 환경 변수는 이러한 설정보다 우선 적용됩니다.
Aspire를 사용하여 연결 구성
AppHost는 자원을 정의하고 코드를 사용해 리소스 간 연결을 돕습니다. 이 섹션에서는 Azure Functions 프로젝트에서 사용하는 연결을 구성하고 사용자 지정하는 방법을 보여 줍니다.
Aspire에는 시작하는 데 도움이 되는 기본 연결 권한이 포함됩니다. 그러나 이러한 권한은 애플리케이션에 적절하지 않거나 충분하지 않을 수 있습니다.
Azure RBAC(역할 기반 액세스 제어)를 사용하는 시나리오의 경우 프로젝트 리소스에 대한 메서드를 호출 WithRoleAssignments() 하여 권한을 사용자 지정할 수 있습니다. 호출 WithRoleAssignments()할 때 모든 기본 역할 할당이 제거되고 원하는 전체 집합 역할 할당을 명시적으로 정의해야 합니다. Azure Container Apps에서 애플리케이션을 호스트하는 경우 WithRoleAssignments()을 사용하는 것은 AddAzureContainerAppEnvironment()에서 DistributedApplicationBuilder을 호출해야 합니다.
Azure Functions 호스트 스토리지
Azure Functions에는 핵심 동작 중 일부를 위해 호스트 스토리지 연결(AzureWebJobsStorage)이 필요합니다. AppHost에서 AddAzureFunctionsProject<TProject>()를 호출하면 기본적으로 AzureWebJobsStorage 연결을 생성하고 이를 Functions 프로젝트에 제공합니다. 이 기본 연결은 로컬 개발 실행에 Azure Storage 에뮬레이터를 사용하며, 배포 시 자동으로 스토리지 계정을 프로비저닝합니다. 더 많은 제어를 원한다면, Functions 프로젝트 리소스를 호출 .WithHostStorage() 하여 이 연결을 교체하세요.
Aspire가 호스트 스토리지 연결에 설정하는 기본 권한은 호출 WithHostStorage() 여부에 따라 달라집니다. 추가 WithHostStorage() 하면 스토리지 계정 기여자 할당이 제거됩니다. 다음 표에서는 호스트 스토리지 연결에 대해 Aspire가 설정하는 기본 사용 권한을 나열합니다.
| 호스트 스토리지 연결 | 기본 역할 |
|---|---|
호출 없음 WithHostStorage() |
Storage Blob 데이터 기여자, Storage 큐 데이터 기여자, 스토리지 테이블 데이터 기여자 스토리지 계정 기여자 |
호출 WithHostStorage() |
Storage Blob 데이터 기여자, Storage 큐 데이터 기여자, 스토리지 테이블 데이터 기여자 |
다음 예시는 호스트 저장소를 대체하고 역할 할당을 지정하는 최소 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 서비스에 대한 연결에 Storage Blob 데이터 기여자의 Aspire 기본값만 있는 경우 앱에 문제가 발생할 수 있습니다.
프로덕션 시나리오의 경우 둘 다 WithHostStorage() 및 WithRoleAssignments().에 대한 호출을 포함합니다. 그런 다음 필요한 다른 역할과 함께 이 역할을 명시적으로 설정할 수 있습니다.
트리거 및 바인딩 연결
트리거와 바인딩은 이름으로 연결을 참조하세요. 다음 Aspire 통합은 프로젝트 리소스에 대한 호출을 WithReference() 통해 이러한 연결을 제공합니다.
다음 예시는 큐 트리거를 구성하는 최소 AppHost.cs 파일을 보여줍니다. 이 예제에서는 해당 큐 트리거의 속성이 Connection으로 설정되므로, MyQueueTriggerConnection 호출에서 이름을 지정합니다.
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는 ID 기반 연결을 ConnectionStringExpression 설정하고 서비스의 URI를 반환할 수 있습니다. 이 경우 Azure Functions에 대한 ID 기반 연결을 설정하려면 다른 환경 변수 이름을 제공해야 할 수 있습니다.
다음 예에서는 builder.ExecutionContext.IsPublishMode를 사용하여 필요한 접미사를 조건부로 추가합니다.
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithEnvironment("MyBindingConnection" + (builder.ExecutionContext.IsPublishMode ? "__serviceUri" : ""), otherIntegration.Resource.ConnectionStringExpression);
각 바인딩에서 지원하는 연결 형식 및 해당 형식에 필요한 사용 권한에 대한 자세한 내용은 바인딩의 참조 페이지를 참조하세요.
함수 코드가 주입된 WithReference값을 어떻게 읽는지에 대한 자세한 내용은 Azure Functions 런타임 구성을 참조하세요.
애플리케이션 호스팅
Aspire는 Functions 프로젝트에 대한 Azure Container Apps 배포를 지원합니다. 또한 별도의 프리뷰 App Service 통합을 사용하여 컨테이너 지원 기능 앱을 타겟팅할 수도 있습니다:
- 컨테이너 앱으로 배포하기
- Preview App Service 통합을 이용한 함수 앱으로 배포하기
두 경우 모두 프로젝트가 컨테이너로 배포됩니다. Aspire는 컨테이너 이미지를 빌드하고 Azure Container Registry에 푸시하는 작업을 처리합니다.
컨테이너 앱으로 배포하기
AppHost가 Azure Container Apps를 대상으로 할 때, Aspire는 KEDA를 사용해 Functions 프로젝트에 대한 확장 규칙을 설정합니다. Azure Container Apps를 사용할 때는 함수 키를 위해 추가 설정을 해야 합니다. 자세한 내용은 Azure Container Apps의 Access Keys를 참조하세요.
aspire deploy를 실행하여 구성된 AppHost를 배포합니다. 자세한 내용은 Deploy to Azure Container Apps 및 aspire deploy.
Azure Container Apps의 액세스 키
여러 Azure Functions 시나리오는 액세스 키를 사용하여 원치 않는 액세스에 대한 기본 완화를 제공합니다. 예를 들어 HTTP 트리거 함수는 기본적으로 액세스 키를 호출해야 하지만 이 요구 사항은 속성을 사용하여 AuthLevel사용하지 않도록 설정할 수 있습니다.
Azure Functions의 액세스 키 작업에서 키가 필요할 수 있는 시나리오를 참조하세요.
Aspire를 사용해 Azure Container Apps에 Functions 프로젝트를 배포할 때, 시스템이 Functions 접근 키를 자동으로 생성하거나 관리하지 않습니다. 접근 키를 사용해야 한다면, AppHost 설정의 일부로 관리할 수 있습니다. 이 섹션에서는 AppHost AppHost.cs 파일에서 호출할 수 있는 확장 메서드를 생성하여 접근 키를 생성하고 관리하는 방법을 보여줍니다. 이 방법은 Azure Key Vault를 사용하여 키를 저장하고 컨테이너 앱에 비밀로 탑재합니다.
참고
여기서 동작은 비밀 제공자 ContainerApps 에 의존하며, 이는 Functions 호스트 버전 4.1044.0 이상이어야 합니다.
이러한 단계에는 Bicep 버전 0.38.3 이상이 필요합니다. 명령 프롬프트에서 실행 bicep --version 하여 Bicep 버전을 확인할 수 있습니다. Azure CLI가 설치된 경우 Bicep을 최신 버전으로 빠르게 업데이트하는 데 사용할 az bicep upgrade 수 있습니다.
다음 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"]);
이 예제에서는 확장 메서드로 생성된 기본 키 볼트를 사용합니다. 그러면 모델 컨텍스트 프로토콜 확장에 사용할 기본 키와 시스템 키가 생성됩니다.
클라이언트에서 이러한 키를 사용하려면 키 보관소에서 해당 키를 검색해야 합니다.
함수 앱으로 배포하기
Note
함수 앱으로 배포하려면 현재 프리뷰 중인 Aspire Azure App Service 통합이 필요합니다.
Aspire Azure App Service 통합을 사용해 함수형 앱에 배포하도록 설정할 수 있습니다. Aspire는 Functions 프로젝트를 컨테이너로 배포하기 때문에, 함수 앱의 호스팅 계획은 컨테이너화된 애플리케이션 배포를 지원해야 합니다.
Aspire Functions 프로젝트를 함수 앱으로 배포하려면 다음 단계를 따라 하세요:
- AppHost 디렉터리에서
aspire add Aspire.Hosting.Azure.AppService를 실행하여 Aspire.Hosting.Azure.AppService NuGet 패키지를 추가합니다. - 파일의
AppHost.cs에서AddAzureAppServiceEnvironment()인스턴스에 대해IDistributedApplicationBuilder을 호출하여 App Service 계획을 만듭니다. 이름에서 느껴지는 것과는 달리, App Service Environment라는 리소스를 실제로 제공하지 않습니다. - Functions 프로젝트 리소스에서
.WithExternalHttpEndpoints()을 호출합니다. 이는 Aspire Azure App Service 통합을 사용하여 배포하는 데 필요합니다. - Functions 프로젝트 리소스에서 해당 프로젝트를 계획 내 함수 앱으로 커스터마이즈하도록 호출
.PublishAsAzureAppServiceWebsite((infra, app) => app.Kind = "functionapp,linux")하세요.
Important
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");
builder.Build().Run();
이 구성은 프리미엄 V3 계획을 만듭니다. 전용 App Service 계획 SKU를 사용하는 경우 크기 조정은 이벤트 기반이 아닙니다. 대신, 크기 조정은 App Service 계획 설정을 통해 관리됩니다.
고려 사항 및 모범 사례
Azure Functions와 Aspire의 통합을 평가할 때 다음 사항을 고려합니다.
Aspire를 통한 트리거 및 바인딩 구성은 현재 특정 통합으로 제한됩니다. 자세한 내용은 이 문서에서 Aspire를 사용한 연결 구성 을 참조하세요.
함수 프로젝트의 파일은
Program.csIHostApplicationBuilder버전을 사용해야 합니다.IHostApplicationBuilder를 사용하면 를 호출하여builder.AddServiceDefaults()를 Functions 프로젝트에 추가할 수 있습니다.Aspire는 모니터링에 OpenTelemetry를 사용합니다. 서비스 기본 프로젝트를 통해 Azure Monitor로 데이터를 내보내도록 Aspire를 구성할 수 있습니다.
다른 많은 Azure Functions 컨텍스트에서는 작업자 서비스를 등록하여 Application Insights와의 직접 통합을 포함할 수 있습니다. Aspire Service Defaults를 사용할 때는 두 번째 직접 Application Insights 파이프라인을 등록하지 마세요.
Aspire 오케스트레이션에 등록된 Functions 프로젝트의 경우, AppHost가 대부분의 애플리케이션 구성을 제공해야 합니다.
local.settings.json를 사용하여 Functions 프로젝트를func start와 함께 독립적으로 실행할 수 있습니다. Aspire가 프로젝트를 실행할 때 Aspire가 주입한 환경 변수는local.settings.json에 있는 동일한 이름의 값을 덮어씁니다.AppHost가 관리하는 연결용으로 두 번째 Azure Storage 에뮬레이터를 시작하는 것은 피하세요. 경쟁하는 에뮬레이터 인스턴스가 포트 및 저장 공간 충돌을 일으킬 수 있습니다.
자세한 내용은 Azure Functions runtime configuration and Aspire telemetry를 참조하세요.