Condividi tramite


Distribuire un progetto di .NET Aspire in Azure Container Apps usando l'Azure Developer CLI (guida dettagliata)

Il Azure Developer CLI (azd) è stato esteso per supportare la distribuzione di progetti .NET.NET Aspire. Usa questa guida per seguire il processo di creazione e distribuzione di un progetto .NET Aspire in Azure Container Apps usando il Azure Developer CLI. In questa esercitazione verranno illustrati i concetti seguenti:

  • Esplora come funziona l'integrazione azd con i progetti .NET.NET Aspire
  • Fornire e distribuire le risorse su Azure per un progetto .NET Aspire usando azd
  • Generare l'infrastruttura Bicep e altri modelli di file usando azd

Prerequisiti

Per usare .NET.NET Aspire, è necessario che il codice seguente sia installato in locale:

Per altre informazioni, vedere .NET.NET Aspire configurazione e strumentie .NET.NET Aspire SDK.

Sarà anche necessario che il Azure Developer CLIsia installato localmente. Le opzioni di installazione comuni includono quanto segue:

winget install microsoft.azd

In che modo funziona l'integrazione di Azure Developer CLI

Il flusso di lavoro azd init fornisce supporto personalizzato per i progetti .NET.NET Aspire. Il diagramma seguente illustra il funzionamento di questo flusso concettualmente e la modalità di integrazione di azd e .NET.NET Aspire:

Illustrazione dell'elaborazione interna di 'azd' durante la distribuzione del progetto .NET.NET Aspire.

  1. Quando azd è destinato a un progetto .NET.NET Aspire avvia AppHost con un comando speciale (dotnet run --project AppHost.csproj --output-path manifest.json --publisher manifest), che produce il file manifesto Aspire.
  2. Il file manifesto viene interrogato dalla logica del sottocomando azd provision per, per impostazione predefinita, generare file Bicep solo nella memoria.
  3. Dopo aver generato i file Bicep, viene attivata una distribuzione usando le API ARM di Azuredestinate alla sottoscrizione e al gruppo di risorse fornito in precedenza.
  4. Dopo aver configurato le risorse Azure sottostanti, viene eseguita la logica del sotto-comando azd deploy che usa lo stesso manifest file Aspire.
  5. Nell'ambito della distribuzione, azd effettua una chiamata a dotnet publish utilizzando il supporto integrato per la pubblicazione di container di .NETper generare immagini di container.
  6. Dopo che azd ha creato le immagini del contenitore, le inserisce nel registro ACR, creato durante la fase di provisioning.
  7. Infine, una volta che l'immagine del container si trova nel Registro di Container di Azure, azd aggiorna la risorsa utilizzando ARM per iniziare a utilizzare la nuova versione dell'immagine del container.

Nota

azd consente anche di salvare il Bicep generato in una cartella infra nel progetto, di cui si può leggere di più nella sezione Generare Bicep dal modello app .NET.NET Aspire alla sezione.

Effettuare il provisioning e distribuire un'app iniziale .NET.NET Aspire

I passaggi descritti in questa sezione illustrano come creare un'app di avvio .NET Aspire e gestire il provisioning e la distribuzione delle risorse dell'app in Azure usando azd.

Crea la app iniziale .NET.NET Aspire

Creare un nuovo progetto .NET.NET Aspire usando il comando dotnet new. È anche possibile creare il progetto usando Visual Studio.

dotnet new aspire-starter --use-redis-cache -o AspireSample
cd AspireSample
dotnet run --project AspireSample.AppHost\AspireSample.AppHost.csproj

I comandi precedenti creano un nuovo progetto di .NET.NET Aspire basato sul modello di aspire-starter che include una dipendenza dalla cache Redis. Esegue il progetto .NET.NET Aspire che verifica che tutto funzioni correttamente.

Inizializzare il modello

  1. Aprire una nuova finestra del terminale e cd nella directory del progetto AppHost della soluzione .NET.NET Aspire.

  2. Eseguire il comando azd init per inizializzare il progetto con azd, che esamina la struttura di directory locale e determina il tipo di app.

    azd init
    

    Per altre informazioni sul comando azd init, vedere azd init.

  3. Selezionare Usa codice nella directory corrente quando ti viene richiesto di scegliere tra due opzioni di inizializzazione dell'app azd.

    ? How do you want to initialize your app?  [Use arrows to move, type to filter]
    > Use code in the current directory
      Select a template
    
  4. Dopo aver eseguito l'analisi della directory, azd chiede di confermare che è stato trovato il progetto di .NET.NET Aspire AppHost corretto. Selezionare l'opzione Conferma e continuare a inizializzare l'app.

    Detected services:
    
      .NET (Aspire)
      Detected in: D:\source\repos\AspireSample\AspireSample.AppHost\AspireSample.AppHost.csproj
    
    azd will generate the files necessary to host your app on Azure using Azure Container Apps.
    
    ? Select an option  [Use arrows to move, type to filter]
    > Confirm and continue initializing my app
      Cancel and exit
    
  5. Immettere un nome di ambiente, usato per assegnare un nome alle risorse di cui è stato effettuato il provisioning in Azure e gestire ambienti diversi, ad esempio dev e prod.

    Generating files to run your app on Azure:
    
      (✓) Done: Generating ./azure.yaml
      (✓) Done: Generating ./next-steps.md
    
    SUCCESS: Your app is ready for the cloud!
    You can provision and deploy your app to Azure by running the azd up command in this directory. For more information on configuring your app, see ./next-steps.md
    

azd genera un numero di file e li inserisce nella directory di lavoro. Questi file sono:

  • azure.yaml: descrive i servizi dell'app, ad esempio il progetto .NET Aspire AppHost, e le mappa alle risorse Azure.
  • .azure/config.json: file di configurazione che informa azd qual è l'ambiente attivo corrente.
  • .azure/aspireazddev/.env: contiene sovrascritture specifiche dell'ambiente.

Il file azure.yaml ha il seguente contenuto:

# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json

name: AspireSample
services:
  app:
    language: dotnet
    project: .\AspireSample.AppHost\AspireSample.AppHost.csproj
    host: containerapp

Denominazione delle risorse

Quando si creano nuove risorse Azure, è importante rispettare i requisiti di denominazione. Per Azure Container Apps, il nome deve avere una lunghezza di 2-32 caratteri e deve essere costituito da lettere minuscole, numeri e trattini. Il nome deve iniziare con una lettera e terminare con un carattere alfanumerico.

Per altre informazioni, vedere regole di denominazione e restrizioni per le risorse Azure.

Distribuzione iniziale

  1. Per distribuire il progetto .NET Aspire, autenticati in Azure AD per effettuare chiamate alle API di gestione delle risorse Azure.

    azd auth login
    

    Il comando precedente avvierà un browser per autenticare la sessione della riga di comando.

  2. Dopo l'autenticazione, eseguire il comando seguente dalla directory del progetto AppHost per provisionare e distribuire l'applicazione.

    azd up
    

    Importante

    Per eseguire il push delle immagini del container nel Registro Container di Azure (ACR), è necessario avere accesso Microsoft.Authorization/roleAssignments/write. A tale scopo, è possibile abilitare un utente amministratore nel Registro di sistema. Aprire il portale di , passare alla risorsa ACR / Impostazioni / Chiavi di accesso e quindi selezionare la casella di controllo utente amministratore . Per ulteriori informazioni, consultare Abilitare l'utente amministratore.

  3. Quando richiesto, selezionare la sottoscrizione e il percorso in cui distribuire le risorse. Dopo aver selezionato queste opzioni, verrà distribuito il progetto .NET.NET Aspire.

    By default, a service can only be reached from inside the Azure Container Apps environment it is running in. Selecting a service here will also allow it to be reached from the Internet.
    ? Select which services to expose to the Internet webfrontend
    ? Select an Azure Subscription to use:  1. <YOUR SUBSCRIPTION>
    ? Select an Azure location to use: 1. <YOUR LOCATION>
    
    Packaging services (azd package)
    
    
    Provisioning Azure resources (azd provision)
    Provisioning Azure resources can take some time.
    
    Subscription: <YOUR SUBSCRIPTION>
    Location: <YOUR LOCATION>
    
      You can view detailed progress in the Azure Portal:
      <LINK TO DEPLOYMENT>
    
      (✓) Done: Resource group: <YOUR RESOURCE GROUP>
      (✓) Done: Container Registry: <ID>
      (✓) Done: Log Analytics workspace: <ID>
      (✓) Done: Container Apps Environment: <ID>
    
    SUCCESS: Your application was provisioned in Azure in 1 minute 13 seconds.
    You can view the resources created under the resource group <YOUR RESOURCE GROUP> in Azure Portal:
    <LINK TO RESOURCE GROUP OVERVIEW>
    
    Deploying services (azd deploy)
    
      (✓) Done: Deploying service apiservice
      - Endpoint: <YOUR UNIQUE apiservice APP>.azurecontainerapps.io/
    
      (✓) Done: Deploying service webfrontend
      - Endpoint: <YOUR UNIQUE webfrontend APP>.azurecontainerapps.io/
    
    Aspire Dashboard: <LINK TO DEPLOYED .NET ASPIRE DASHBOARD>
    
    SUCCESS: Your up workflow to provision and deploy to Azure completed in 3 minutes 50 seconds.
    

    La riga di output finale del comando azd è un collegamento al portale di Azure che mostra tutte le risorse Azure distribuite:

    Screenshot del portale di Azure che mostra le risorse distribuite.

All'interno dell'applicazione vengono distribuiti tre contenitori:

  • webfrontend: contiene il codice del progetto web nel modello iniziale.
  • apiservice: contiene il codice del progetto del servizio API nel template di avvio.
  • cache: immagine del contenitore Redis per fornire una cache al front-end.

Proprio come nello sviluppo locale, la configurazione delle stringhe di connessione è stata gestita automaticamente. In questo caso, azd è stato responsabile dell'interpretazione del modello di applicazione e della sua conversione nei passaggi di distribuzione appropriati. Si consideri, ad esempio, la stringa di connessione e le variabili di individuazione del servizio inserite nel contenitore webfrontend in modo che sappia come connettersi alla cache Redis e apiservice.

Una screenshot delle variabili di ambiente dell'app container webfrontend.

Per altre informazioni su come i progetti .NET.NET Aspire gestiscono le stringhe di connessione e l'individuazione dei servizi, vedere .NET.NET Aspire panoramica dell'orchestrazione.

Distribuire gli aggiornamenti delle applicazioni

Quando viene eseguito il comando azd up, le risorse sottostanti di Azure vengono fornite e un'immagine del contenitore viene creata e distribuita alle app contenitore che ospitano il progetto .NET.NET Aspire. In genere, una volta avviato lo sviluppo e distribuite le risorse Azure, non sarà necessario effettuare il provisioning delle risorse Azure ogni volta che viene aggiornato il codice. Ciò vale soprattutto per il ciclo interno degli sviluppatori.

Per velocizzare la distribuzione delle modifiche al codice, azd supporta la distribuzione degli aggiornamenti del codice nell'immagine del contenitore. Questa operazione viene eseguita usando il comando azd deploy:

azd deploy
Deploying services (azd deploy)

  (✓) Done: Deploying service apiservice
  - Endpoint: <YOUR UNIQUE apiservice APP>.azurecontainerapps.io/

  (✓) Done: Deploying service webfrontend
  - Endpoint: <YOUR UNIQUE webfrontend APP>.azurecontainerapps.io/

Aspire Dashboard: <LINK TO DEPLOYED .NET ASPIRE DASHBOARD>

Non è necessario distribuire tutti i servizi ogni volta. azd comprende il modello di progetto .NET.NET Aspire, è possibile distribuire solo uno dei servizi specificati usando il comando seguente:

azd deploy webfrontend

Per ulteriori informazioni, vedere il riferimento Azure Developer CLI: azd deploy.

Distribuire gli aggiornamenti dell'infrastruttura

Ogni volta che cambia la struttura di dipendenza all'interno di un progetto di .NET.NET Aspire, azd deve eseguire di nuovo il provisioning delle risorse Azure sottostanti. Il comando azd provision viene usato per applicare queste modifiche all'infrastruttura.

Per visualizzare questa operazione in azione, aggiornare il file Program.cs nel progetto AppHost nel modo seguente:

var builder = DistributedApplication.CreateBuilder(args);

var cache = builder.AddRedis("cache");

// Add the locations database.
var locationsdb = builder.AddPostgres("db").AddDatabase("locations");

// Add the locations database reference to the API service.
var apiservice = builder.AddProject<Projects.AspireSample_ApiService>("apiservice")
    .WithReference(locationsdb);

builder.AddProject<Projects.AspireSample_Web>("webfrontend")
    .WithReference(cache)
    .WithReference(apiservice);

builder.Build().Run();

Salvare il file ed eseguire il comando seguente:

azd provision

Il comando azd provision aggiorna l'infrastruttura creando un'app contenitore per ospitare il database Postgres. Il comando azd provision non ha aggiornato le stringhe di connessione per il contenitore apiservice. Per fare in modo che le stringhe di connessione vengano aggiornate in modo che puntino al database di Postgres appena sottoposto a provisioning, è necessario richiamare nuovamente il comando azd deploy. In caso di dubbio, usare azd up sia per effettuare il provisioning che per la distribuzione.

Pulire le risorse

Ricorda di pulire le risorse Azure che hai creato durante questa procedura dettagliata. Poiché `azd conosce il gruppo di risorse in cui ha creato le risorse, può essere utilizzato per disattivare l'ambiente usando il comando seguente:

azd down

L'esecuzione del comando precedente può richiedere del tempo, ma quando è stato completato il gruppo di risorse e tutte le relative risorse devono essere eliminate.

Deleting all resources and deployed code on Azure (azd down)
Local application code is not deleted when running 'azd down'.

  Resource group(s) to be deleted:

    • <YOUR RESOURCE GROUP>: <LINK TO RESOURCE GROUP OVERVIEW>

? Total resources to delete: 7, are you sure you want to continue? Yes
Deleting your resources can take some time.

  (✓) Done: Deleting resource group: <YOUR RESOURCE GROUP>

SUCCESS: Your application was removed from Azure in 9 minutes 59 seconds.

Generare Bicep dal modello di progetto .NET.NET Aspire

Anche se i team di sviluppo sono liberi di usare azd up (o azd provision e azd deploy) comandi per le distribuzioni sia a scopo di sviluppo che di produzione, alcuni team possono scegliere di generare file Bicep che possono esaminare e gestire come parte del controllo della versione (in questo modo questi file Bicep possono essere referenziati come parte di una distribuzione più complessa Azure).

azd include la possibilità di fornire il Bicep utilizzato per il provisioning tramite il seguente comando:

azd config set alpha.infraSynth on
azd infra synth

Dopo aver eseguito questo comando nell'esempio di modello iniziale usato in questa guida, i file seguenti vengono creati nella directory del progetto AppHost:

  • infra/main.bicep: Indica il punto di ingresso principale per la distribuzione.
  • infra/main.parameters.json: utilizzato come parametri per il main Bicep (mappa alle variabili di ambiente definite nella cartella .azure).
  • infra/resources.bicep: definisce le risorse di Azure necessarie per supportare il modello di progetto .NET Aspire.
  • AspireSample.Web/manifests/containerApp.tmpl.yaml: definizione dell'applicazione contenitore per webfrontend.
  • AspireSample.ApiService/manifests/containerApp.tmpl.yaml: Definizione dell'applicazione container per apiservice.

Il file infra\resources.bicep non contiene alcuna definizione delle app contenitore stesse (ad eccezione delle app contenitore che sono dipendenze, ad esempio Redis e Postgres):

@description('The location used for all deployed resources')
param location string = resourceGroup().location

@description('Tags that will be applied to all resources')
param tags object = {}

var resourceToken = uniqueString(resourceGroup().id)

resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
  name: 'mi-${resourceToken}'
  location: location
  tags: tags
}

resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-07-01' = {
  name: replace('acr-${resourceToken}', '-', '')
  location: location
  sku: {
    name: 'Basic'
  }
  tags: tags
}

resource caeMiRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(containerRegistry.id, managedIdentity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d'))
  scope: containerRegistry
  properties: {
    principalId: managedIdentity.properties.principalId
    principalType: 'ServicePrincipal'
    roleDefinitionId:  subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')
  }
}

resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
  name: 'law-${resourceToken}'
  location: location
  properties: {
    sku: {
      name: 'PerGB2018'
    }
  }
  tags: tags
}

resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
  name: 'cae-${resourceToken}'
  location: location
  properties: {
    appLogsConfiguration: {
      destination: 'log-analytics'
      logAnalyticsConfiguration: {
        customerId: logAnalyticsWorkspace.properties.customerId
        sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey
      }
    }
  }
  tags: tags
}

resource cache 'Microsoft.App/containerApps@2023-05-02-preview' = {
  name: 'cache'
  location: location
  properties: {
    environmentId: containerAppEnvironment.id
    configuration: {
      service: {
        type: 'redis'
      }
    }
    template: {
      containers: [
        {
          image: 'redis'
          name: 'redis'
        }
      ]
    }
  }
  tags: union(tags, {'aspire-resource-name': 'cache'})
}

resource locations 'Microsoft.App/containerApps@2023-05-02-preview' = {
  name: 'locations'
  location: location
  properties: {
    environmentId: containerAppEnvironment.id
    configuration: {
      service: {
        type: 'postgres'
      }
    }
    template: {
      containers: [
        {
          image: 'postgres'
          name: 'postgres'
        }
      ]
    }
  }
  tags: union(tags, {'aspire-resource-name': 'locations'})
}
output MANAGED_IDENTITY_CLIENT_ID string = managedIdentity.properties.clientId
output AZURE_CONTAINER_REGISTRY_ENDPOINT string = containerRegistry.properties.loginServer
output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = managedIdentity.id
output AZURE_CONTAINER_APPS_ENVIRONMENT_ID string = containerAppEnvironment.id
output AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN string = containerAppEnvironment.properties.defaultDomain

Per altre informazioni sull'uso di Bicep per automatizzare le distribuzioni in Azure, vedi Che cos'è Bicep?

La definizione delle app contenitore dai progetti di servizio .NET è contenuta rispettivamente nei file containerApp/tmpl.ya ml nella directory manifests di ogni progetto. Di seguito è riportato un esempio del progetto webfrontend:

location: {{ .Env.AZURE_LOCATION }}
identity:
  type: UserAssigned
  userAssignedIdentities:
    ? "{{ .Env.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID }}"
    : {}
properties:
  environmentId: {{ .Env.AZURE_CONTAINER_APPS_ENVIRONMENT_ID }}
  configuration:
    activeRevisionsMode: single
    ingress:
      external: true
      targetPort: 8080
      transport: http
      allowInsecure: false
    registries:
    - server: {{ .Env.AZURE_CONTAINER_REGISTRY_ENDPOINT }}
      identity: {{ .Env.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID }}
  template:
    containers:
    - image: {{ .Env.SERVICE_WEBFRONTEND_IMAGE_NAME }}
      name: webfrontend
      env:
      - name: AZURE_CLIENT_ID
        value: {{ .Env.MANAGED_IDENTITY_CLIENT_ID }}
      - name: ConnectionStrings__cache
        value: {{ connectionString "cache" }}
      - name: OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES
        value: "true"
      - name: OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES
        value: "true"
      - name: services__apiservice__0
        value: http://apiservice.internal.{{ .Env.AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN }}
      - name: services__apiservice__1
        value: https://apiservice.internal.{{ .Env.AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN }}
tags:
  azd-service-name: webfrontend
  aspire-resource-name: webfrontend

Dopo aver eseguito il comando azd infra synth, quando azd provision e azd deploy vengono chiamati usano i file generati da Bicep e supportati.

Importante

Se azd infra synth viene chiamato di nuovo, sostituisce tutti i file modificati con quelli appena generati e richiede conferma prima di farlo.

Ambienti isolati per il debug

Poiché azd semplifica il provisioning di nuovi ambienti, è possibile che ogni membro del team abbia un ambiente ospitato nel cloud isolato per il debug del codice in un'impostazione che corrisponde strettamente alla produzione. Quando si esegue questa operazione, ogni membro del team deve creare il proprio ambiente usando il comando seguente:

azd env new

Verrà richiesta di nuovo all'utente le informazioni sulla sottoscrizione e sul gruppo di risorse e le successive azd up, azd provisione azd deploy chiamate useranno questo nuovo ambiente per impostazione predefinita. L'opzione --environment può essere applicata a questi comandi per passare da un ambiente all'altro.

Pulire le risorse

Esegui il seguente comando CLI di Azure per eliminare il gruppo di risorse quando le risorse Azure non sono più necessarie. L'eliminazione del gruppo di risorse comporta anche l'eliminazione delle risorse contenute all'interno di essa.

az group delete --name <your-resource-group-name>

Per altre informazioni, vedere Eseguire la pulizia delle risorse in Azure.