Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Aspire è una toolchain per costruire, eseguire, fare debug e distribuire applicazioni distribuite. L'integrazione di Aspire Funzioni di Azure ti permette di sviluppare, debug e orchestrare un progetto Funzioni di Azure come parte di un Aspire AppHost. Gli esempi di .NET in questo articolo utilizzano il modello del worker isolato.
Prerequisiti
Configurare l'ambiente di sviluppo per l'uso di Funzioni di Azure con Aspire:
Installa i prerequisiti di Aspire, incluso il .NET SDK richiesto dal tuo AppHost.
Installa l'integrazione di hosting di Aspire Funzioni di Azure dalla directory AppHost.
aspire add Aspire.Hosting.Azure.FunctionsInstallare Funzioni di Azure Core Tools.
Se usi Visual Studio, installa gli ultimi aggiornamenti degli strumenti di Visual Studio e Funzioni di Azure:
- Passare aOpzionistrumenti>.
- In Progetti e soluzioni selezionare Funzioni di Azure.
- Selezionare Controlla aggiornamenti e installare gli aggiornamenti come richiesto.
Per maggiori informazioni sul pacchetto di integrazione e sulle API AppHost supportate, vedi Configura Funzioni di Azure nell'AppHost.
Struttura della soluzione
Una soluzione che utilizza Funzioni di Azure e Aspire ha più progetti, tra cui un AppHost e uno o più progetti Functions.
L'AppHost è il punto di ingresso per la tua candidatura. Orchestra la configurazione dei componenti dell'applicazione, incluso il progetto Funzioni.
La soluzione include in genere anche un progetto predefinito del servizio . Questo progetto fornisce un set di servizi e configurazioni predefiniti da usare tra progetti nell'applicazione.
Progetto AppHost
Per configurare con successo l'integrazione, assicurarsi che il progetto AppHost soddisfi i seguenti requisiti:
- L'AppHost fa riferimento ad Aspire.Hosting.Azure. Functions. Questo pacchetto definisce l'integrazione.
- Un AppHost C# fa riferimento a un progetto Functions e chiama
AddAzureFunctionsProject<TProject>(), ovvero chiamaAddAzureFunctionsProject(name, projectPath)con il percorso verso il file del progetto. Gli AppHost TypeScript utilizzano la forma di percorso di progetto diaddAzureFunctionsProject. - Usare
AddAzureFunctionsProjectanzichéAddProject. Un progetto Functions aggiunto usandoAddProjectnon si avvia correttamente.
Il seguente esempio mostra un file minimale AppHost.cs per un progetto C# AppHost:
var builder = DistributedApplication.CreateBuilder(args);
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject");
builder.Build().Run();
Progetto funzioni di Azure
Per configurare correttamente l'integrazione, assicurarsi che il progetto funzioni di Azure soddisfi i requisiti seguenti:
Imposta come destinazione .NET 8 o versione successiva, usa l'SDK .NET 9 o versione successiva e usa il modello di worker isolato.
Fai riferimento a Microsoft.Azure.Functions.Worker, Microsoft.Azure.Functions.Worker.Sdk e, per i trigger HTTP, Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.
Il file
Program.csdeve usare la versioneIHostApplicationBuilderdell'avvio dell'istanza host. Questo requisito significa che è necessario usareFunctionsApplication.CreateBuilder(args).Se la soluzione include un progetto predefinito del servizio, assicurarsi che il progetto di Funzioni sia configurato per usarlo:
- Il progetto Funzioni deve includere un riferimento al progetto per impostazione predefinita del servizio.
- Prima di creare
IHostApplicationBuilderinProgram.cs, includi una chiamata abuilder.AddServiceDefaults().
L'esempio seguente mostra un file minimo Program.cs per un progetto di Funzioni usato in Aspira:
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Extensions.Hosting;
var builder = FunctionsApplication.CreateBuilder(args);
builder.AddServiceDefaults();
builder.ConfigureFunctionsWebApplication();
builder.Build().Run();
Questo esempio non include la configurazione predefinita di Application Insights visualizzata in molti altri Program.cs esempi e nei modelli di Funzioni di Azure. Invece, l'integrazione di OpenTelemetry in Aspire si configura chiamando il metodo builder.AddServiceDefaults().
Per sfruttare al meglio l'integrazione, prendere in considerazione le linee guida seguenti:
- Non includere integrazioni dirette di Application Insights nel progetto Funzioni. Il monitoraggio in Aspira è invece gestito tramite il supporto OpenTelemetry. È possibile configurare Aspire per esportare i dati in Monitoraggio di Azure tramite le impostazioni predefinite del servizio.
- Quando Aspire esegue il progetto Functions, preferisci le impostazioni iniettate dall'AppHost. Puoi mantenere impostazioni equivalenti in
local.settings.jsonper eseguire il progetto in modo indipendente confunc start; le variabili di ambiente iniettate da Aspire le sovrascrivono.
Configurazione della connessione con Aspira
L'AppHost definisce le risorse e ti aiuta a creare connessioni tra di esse usando il codice. Questa sezione illustra come configurare e personalizzare le connessioni usate dal progetto funzioni di Azure.
Aspire include le autorizzazioni di connessione predefinite che consente di iniziare. Tuttavia, queste autorizzazioni potrebbero non essere appropriate o sufficienti per l'applicazione.
Per gli scenari che usano il controllo degli accessi in base al ruolo di Azure, è possibile personalizzare le autorizzazioni chiamando il WithRoleAssignments() metodo nella risorsa del progetto. Quando si chiama WithRoleAssignments(), tutte le assegnazioni di ruolo predefinite vengono rimosse ed è necessario definire in modo esplicito le assegnazioni di ruolo del set completo desiderate. Se si ospita l'applicazione su App contenitore di Azure, usare WithRoleAssignments() richiede anche di chiamare AddAzureContainerAppEnvironment() su DistributedApplicationBuilder.
Archiviazione delle Funzioni di Azure host
Funzioni di Azure richiede una connessione di archiviazione host (AzureWebJobsStorage) per diversi comportamenti principali. Quando chiami AddAzureFunctionsProject<TProject>() nel tuo AppHost, crei una connessione AzureWebJobsStorage predefinita e la fornisci al progetto Functions. Questa connessione predefinita utilizza l'emulatore Archiviazione di Azure per le run di sviluppo locale e predispone automaticamente un account di archiviazione quando lo distribuisci. Per maggiore controllo, sostituisci questa connessione chiamando .WithHostStorage() la risorsa del progetto Functions.
I permessi predefiniti che Aspire imposta per la connessione di storage host dipendono dal fatto che chiami WithHostStorage() o meno. L'aggiunta di WithHostStorage() comporta la rimozione di un'assegnazione di Collaboratore account di archiviazione. Nella tabella seguente sono elencate le autorizzazioni predefinite impostate da Aspira per la connessione all'archiviazione host:
| Connessione all'archiviazione del server | Ruoli predefiniti |
|---|---|
Nessuna chiamata a WithHostStorage() |
Collaboratore ai dati del BLOB di archiviazione, Collaboratore ai dati della coda di archiviazione, Collaboratore ai dati della tabella di archiviazione. Collaboratore account di archiviazione |
Chiamata WithHostStorage() |
Collaboratore ai dati del BLOB di archiviazione, Collaboratore ai dati della coda di archiviazione, Contributore ai dati delle tabelle di archiviazione |
Il seguente esempio mostra un file minimale AppHost.cs che sostituisce lo storage host e specifica un'assegnazione di ruolo:
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();
Nota
Storage Blob Data Owner è il ruolo consigliato per le esigenze di base della connessione con l'archiviazione host. L'app potrebbe riscontrare problemi se la connessione al servizio BLOB ha solo l'impostazione predefinita di Aspire per collaboratore ai dati dei BLOB di archiviazione.
Per gli scenari di produzione, includere chiamate sia a WithHostStorage() che a WithRoleAssignments(). È quindi possibile impostare questo ruolo in modo esplicito, insieme a qualsiasi altro elemento necessario.
Connessioni di trigger e binding
I trigger e i binding fanno riferimento alle connessioni in base al nome. Le seguenti integrazioni Aspire forniscono queste connessioni tramite una chiamata a WithReference() sulla risorsa del progetto.
L'esempio seguente mostra un file minimale AppHost.cs che configura un trigger di coda. In questo esempio, il trigger della coda corrispondente ha la proprietà Connection impostata su MyQueueTriggerConnection, quindi la chiamata a WithReference() specifica il nome.
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();
Per altre integrazioni, effettua chiamate a WithReference per impostare la configurazione in modo diverso. Rendono disponibile la configurazione per le integrazioni di Aspire client, ma non per i trigger e le associazioni. Per queste integrazioni, chiamare WithEnvironment() per passare le informazioni di connessione per il trigger o il collegamento da risolvere.
Nell'esempio seguente viene illustrato come impostare la variabile MyBindingConnection di ambiente per una risorsa che espone un'espressione di stringa di connessione:
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithEnvironment("MyBindingConnection", otherIntegration.Resource.ConnectionStringExpression);
Se desideri che le integrazioni del client Aspire e il sistema di trigger e associazioni usino una connessione, è possibile configurare sia WithReference() che WithEnvironment().
Per alcune risorse, la struttura di una connessione potrebbe essere diversa tra l'esecuzione locale e la pubblicazione in Azure. Nell'esempio precedente, otherIntegration potrebbe essere una risorsa eseguita come emulatore, quindi ConnectionStringExpression restituirebbe un emulatore stringa di connessione. Tuttavia, quando la risorsa viene pubblicata, Aspire potrebbe configurare una connessione basata sull'identità e ConnectionStringExpression restituirà l'URI del servizio. In questo caso, per configurare le connessioni basate sull'identità per Funzioni di Azure, potrebbe essere necessario specificare un nome di variabile di ambiente diverso.
Nell'esempio seguente viene usato per aggiungere in modo condizionale il suffisso builder.ExecutionContext.IsPublishMode necessario:
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithEnvironment("MyBindingConnection" + (builder.ExecutionContext.IsPublishMode ? "__serviceUri" : ""), otherIntegration.Resource.ConnectionStringExpression);
Per informazioni dettagliate sui formati di connessione supportati da ogni associazione e sulle autorizzazioni necessarie per tali formati, vedere le pagine di riferimento dell'associazione.
Per maggiori informazioni su come il codice Functions legge i valori iniettati da WithReference, vedi Funzioni di Azure runtime configuration.
Hosting dell'applicazione
Aspire supporta il deployment di App contenitore di Azure per progetti Functions. È anche possibile utilizzare l'integrazione separata del servizio app di anteprima per indirizzare un'app per le funzioni compatibile con i contenitori:
- Distribuisci come app contenitore
- Distribuisci come app di funzione utilizzando l'integrazione con l'App Service di anteprima
In entrambi i casi, il progetto viene distribuito come contenitore. Aspire si occupa della creazione dell'immagine del contenitore per l'utente e di inviarla ad Registro Azure Container.
Distribuisci come app contenitore
Quando il tuo AppHost prende di mira App contenitore di Azure, Aspire imposta le regole di scalabilità per il tuo progetto Functions usando KEDA. Quando si usano App contenitore di Azure, è necessario effettuare una configurazione extra per i tasti funzione. Per maggiori informazioni, consulta le Chiavi di accesso su App contenitore di Azure.
Distribuisci l'AppHost configurato eseguendo aspire deploy. Per maggiori informazioni, vedi Deploy to App contenitore di Azure e aspire deploy.
Chiavi di accesso nelle app Azure Container
Diversi scenari di Funzioni di Azure usano le chiavi di accesso per fornire una mitigazione di base contro l'accesso indesiderato. Ad esempio, per impostazione predefinita, le funzioni trigger HTTP richiedono che venga richiamata una chiave di accesso, anche se questo requisito può essere disabilitato tramite la AuthLevel proprietà . Vedere Usare le chiavi di accesso in Funzioni di Azure per scenari che potrebbero richiedere una chiave.
Quando distribuisci un progetto Functions usando Aspire in App contenitore di Azure, il sistema non crea o gestisce automaticamente le chiavi di accesso delle Functions. Se hai bisogno di usare le chiavi di accesso, puoi gestirle come parte della configurazione del tuo AppHost. Questa sezione ti mostra come creare un metodo di estensione che puoi chiamare dal file del AppHost.cs tuo AppHost per creare e gestire le chiavi di accesso. Questo approccio usa Azure Key Vault per archiviare le chiavi e montarle nell'app contenitore come segreti.
Nota
Il comportamento qui si basa sul ContainerApps secret provider, che richiede la versione 4.1044.0 host di Functions o successiva.
Questi passaggi richiedono la versione Bicep 0.38.3 o successiva. È possibile controllare la versione di Bicep eseguendo bicep --version da un prompt dei comandi. Se è installata l'interfaccia della riga di comando di Azure, è possibile usare az bicep upgrade per aggiornare rapidamente Bicep alla versione più recente.
Aggiungi i seguenti pacchetti NuGet al tuo progetto AppHost:
Crea una nuova classe nel tuo progetto AppHost e includi il seguente codice:
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);
}
}
Puoi quindi usare questo metodo nel file del tuo AppHost AppHost.cs :
builder.AddAzureFunctionsProject<Projects.MyFunctionsProject>("MyFunctionsProject")
.WithHostStorage(storage)
.WithExternalHttpEndpoints()
.PublishWithContainerAppSecrets(systemKeyExtensionNames: ["mcp"]);
Questo esempio usa un Key Vault predefinito creato dal metodo di estensione. Viene restituita una chiave predefinita e una chiave di sistema da usare con l'estensione Model Context Protocol.
Per usare queste chiavi dei client, è necessario recuperarle dal vault delle chiavi.
Distribuisci come app per le funzioni
Nota
La distribuzione come app per funzioni richiede l'integrazione di Aspire con Servizio app di Azure, attualmente in anteprima.
Puoi configurare Aspire per distribuire su un'app di funzioni utilizzando l'integrazione con Aspire Servizio app di Azure. Poiché Aspire distribuisce il progetto Functions come container, il piano di hosting della tua app funzione deve supportare il dispiegamento di applicazioni containerizzate.
Per distribuire il tuo progetto Aspire Functions come app di funzione, segui questi passaggi:
- Dalla directory AppHost, esegui
aspire add Aspire.Hosting.Azure.AppServiceper aggiungere il pacchetto Aspire.Hosting.Azure. AppService NuGet. - Nel file
AppHost.cs, chiamareAddAzureAppServiceEnvironment()nell'istanzaIDistributedApplicationBuilderper creare un piano di servizio app. Si noti che, il nome può trarre in inganno, poiché non viene configurata alcuna risorsa di un Ambiente del servizio app. - Nella risorsa del progetto Funzioni, chiamare
.WithExternalHttpEndpoints(). Questa operazione è necessaria per la distribuzione con l'integrazione del servizio app di Azure Aspira. - Nella risorsa del progetto Funzioni, richiamare
.PublishAsAzureAppServiceWebsite((infra, app) => app.Kind = "functionapp,linux")per personalizzare il progetto come app per le funzioni all’interno del piano.
Importante
Assicurarsi di impostare la app.Kind proprietà su "functionapp,linux". Questa impostazione garantisce che la risorsa venga creata come app per le funzioni, che influisce sulle esperienze per l'uso dell'applicazione.
L'esempio seguente mostra un file AppHost.cs minimale che distribuisce un progetto Functions come app per le funzioni:
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();
Questa configurazione crea un piano Premium V3. Quando si usa uno SKU del piano di servizio app dedicato, il ridimensionamento non è basato su eventi. Il ridimensionamento viene invece gestito tramite le impostazioni del piano di servizio app.
Considerazioni e procedure consigliate
Quando si valuta l'integrazione di Funzioni di Azure con Aspira, prendere in considerazione i punti seguenti:
La configurazione di trigger e binding tramite Aspira è attualmente limitata a integrazioni specifiche. Per informazioni dettagliate, vedere Configurazione della connessione con Aspire in questo articolo.
Il file del progetto di funzione
Program.csdeve usare la versioneIHostApplicationBuilderdell'avvio dell'istanza host. UsandoIHostApplicationBuilder, puoi chiamarebuilder.AddServiceDefaults()per aggiungere Aspire Service Defaults al tuo progetto Functions.Aspire utilizza OpenTelemetry per il monitoraggio. È possibile configurare Aspire per esportare i dati in Monitoraggio di Azure tramite le impostazioni predefinite del servizio.
In molti altri contesti di Funzioni di Azure, è possibile includere l'integrazione diretta con Application Insights registrando il servizio di lavoro. Non registrare una seconda pipeline diretta di Application Insights quando usi Aspire Service Defaults.
Per i progetti Functions inseriti in un'orchestrazione Aspire, l'AppHost dovrebbe fornire la maggior parte delle configurazioni applicative. Puoi usare
local.settings.jsonper eseguire il progetto Functions in modo indipendente confunc start. Quando Aspire esegue il progetto, le variabili di ambiente iniettate da Aspire sovrascrivono valori con gli stessi nomi inlocal.settings.json.Evita di avviare un secondo emulatore Archiviazione di Azure per le connessioni gestite dall'AppHost. Le istanze concorrenti degli emulatori possono causare conflitti tra porte e archiviazione.
Per ulteriori informazioni, consulta Funzioni di Azure runtime configuration e Aspire telemetry.