Compartir a través de


Implementación de un proyecto de .NET Aspire en Azure Container Apps mediante el Azure Developer CLI (guía detallada)

El Azure Developer CLI (azd) se ha ampliado para admitir la implementación de proyectos de .NET.NET Aspire. Use esta guía para recorrer el proceso de creación e implementación de un proyecto de .NET Aspire para Azure Container Apps mediante el Azure Developer CLI. En este tutorial, aprenderá los conceptos siguientes:

  • Explorar cómo funciona la integración de azd con proyectos de .NET.NET Aspire
  • Provisión e implementación de recursos en Azure para un proyecto de .NET Aspire mediante azd
  • Generar infraestructura Bicep y otros archivos de plantilla con azd

Prerrequisitos

Para trabajar con .NET.NET Aspire, necesita lo siguiente instalado localmente:

Para obtener más información, consulte configuración y herramientas de .NET.NET Aspirey sdk de .NET.NET Aspire.

También deberá tener instalado el Azure Developer CLIlocalmente. Entre las opciones de instalación comunes se incluyen las siguientes:

winget install microsoft.azd

¿Cómo funciona la integración de Azure Developer CLI?

El flujo de trabajo de azd init proporciona compatibilidad personalizada con proyectos de .NET.NET Aspire. En el diagrama siguiente se muestra cómo funciona conceptualmente este flujo y cómo se integran azd y .NET.NET Aspire:

Ilustración del procesamiento interno de

  1. Cuando azd tiene como destino un proyecto de .NET.NET Aspire, inicia AppHost con un comando especial (dotnet run --project AppHost.csproj --output-path manifest.json --publisher manifest), que genera el archivo de manifiesto Aspire.
  2. El archivo de manifiesto es interrogado por la lógica del subcomando azd provision para generar archivos Bicep solo en memoria, de forma predeterminada.
  3. Después de generar los archivos de Bicep, se inicia una implementación utilizando las API de ARM de Azure, que tienen como objetivo la suscripción y el grupo de recursos proporcionados anteriormente.
  4. Una vez configurados los recursos de Azure subyacentes, se ejecuta la lógica de sub command azd deploy que usa el mismo archivo de manifiesto Aspire.
  5. Como parte del despliegue, azd realiza una llamada a dotnet publish utilizando el soporte integrado de publicación de contenedores de .NETpara generar imágenes de contenedores.
  6. Una vez que azd ha creado las imágenes de contenedor, las inserta en el registro de ACR que se creó durante la fase de aprovisionamiento.
  7. Por último, una vez que la imagen de contenedor está en ACR, azd actualiza el recurso mediante ARM para empezar a usar la nueva versión de la imagen de contenedor.

Nota

azd también le permite generar Bicep en una carpeta infra del proyecto, que puede obtener más información en la sección Generación de Bicep desde .NET.NET Aspire modelo de aplicación.

Aprovisionamiento e implementación de una aplicación inicial para .NET.NET Aspire

Los pasos de esta sección muestran cómo crear una aplicación de inicio de .NET Aspire y controlar el aprovisionamiento e implementar los recursos de la aplicación en Azure mediante azd.

Crear la aplicación inicial .NET.NET Aspire

Cree un nuevo proyecto de .NET.NET Aspire mediante el comando dotnet new. También puede crear el proyecto mediante Visual Studio.

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

Los comandos anteriores crean un nuevo proyecto de .NET.NET Aspire basado en la plantilla de aspire-starter que incluye una dependencia en caché de Redis. Ejecuta el proyecto .NET.NET Aspire que comprueba que todo funciona correctamente.

Inicialización de la plantilla

  1. Abra una nueva ventana de terminal y cd en el directorio del proyecto AppHost de la solución de .NET.NET Aspire.

  2. Ejecute el comando azd init para inicializar el proyecto con azd, que inspeccionará la estructura del directorio local y determinará el tipo de aplicación.

    azd init
    

    Para obtener más información sobre el comando azd init, vea azd init.

  3. Seleccione Usar código en el directorio actual cuando azd le pida dos opciones de inicialización de la aplicación.

    ? 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. Después de examinar el directorio, le pedirá que confirme que encontró el proyecto de AppHost correcto . Seleccione la opción Confirmar y continuar con la inicialización de mi 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. Escriba un nombre de entorno, que se usa para asignar un nombre a los recursos aprovisionados en Azure y administrar entornos diferentes, como dev y 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 una serie de archivos y los coloca en el directorio de trabajo. Estos archivos son:

  • azure .yaml: describe los servicios de la aplicación, como .NET Aspire proyecto AppHost, y los asigna a Azure recursos.
  • .azure/config.json: archivo de configuración que informa azd cuál es el entorno activo actual.
  • .azure/aspireazddev/.env: contiene invalidaciones específicas del entorno.

El archivo .yaml tiene el siguiente contenido:

# 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

Nomenclatura de recursos

Al crear nuevos recursos Azure, es importante seguir los requisitos de nomenclatura. Para Azure Container Apps, el nombre debe tener entre 2 y 32 caracteres y consistir en letras minúsculas, números y guiones. El nombre debe comenzar con una letra y terminar con un carácter alfanumérico.

Para obtener más información, consulte Reglas y restricciones de nomenclatura para Azure recursos.

Implementación inicial

  1. Para implementar el proyecto de .NET Aspire, autentíquese en Azure AD para llamar a las API de administración de recursos de Azure.

    azd auth login
    

    El comando anterior iniciará un explorador para autenticar la sesión de línea de comandos.

  2. Una vez autenticado, ejecute el siguiente comando desde el directorio del proyecto AppHost para aprovisionar e implementar la aplicación.

    azd up
    

    Importante

    Para insertar imágenes de contenedor en la Azure Container Registry (ACR), debe tener acceso Microsoft.Authorization/roleAssignments/write. Esto se puede lograr habilitando un usuario administrador de en el registro. Abra el portal de , vaya al recurso de ACR/ Configuración /Claves de acceso y, a continuación, active la casilla usuario administrador de . Para obtener más información, consulte Habilitar usuario administrador.

  3. Cuando se le solicite, seleccione la suscripción y la ubicación en la que se deben implementar los recursos. Una vez seleccionadas estas opciones, se implementará el proyecto de .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 línea final de salida del comando azd es un vínculo al portal de Azure que muestra todos los recursos de Azure implementados:

    Captura de pantalla de Azure Portal en la que se muestran los recursos implementados.

En esta aplicación se implementan tres contenedores:

  • webfrontend: contiene código del proyecto web en la plantilla de inicio.
  • apiservice: contiene código del proyecto de servicio de API en la plantilla de inicio.
  • cache: una imagen de contenedor de Redis para proporcionar una memoria caché al front-end.

Al igual que en el desarrollo local, la configuración de cadenas de conexión se ha controlado automáticamente. En este caso, azd era responsable de interpretar el modelo de aplicación y traducirlo a los pasos de implementación adecuados. Por ejemplo, considere las variables de detección de servicio y cadena de conexión que se insertan en el contenedor de webfrontend para que sepa cómo conectarse a la memoria caché de Redis y apiservice.

Captura de pantalla de las variables de entorno en la aplicación contenedora webfrontend.

Para obtener más información sobre cómo los proyectos .NET.NET Aspire manejan las cadenas de conexión y el descubrimiento de servicios, véase la introducción a la orquestación .NET.NET Aspire.

Implementación de actualizaciones de aplicaciones

Cuando se ejecuta el comando azd up, los recursos de Azure subyacentes se aprovisionan y se compila una imagen de contenedor y se implementan en las aplicaciones contenedoras que hospedan el proyecto de .NET.NET Aspire. Normalmente, una vez que el desarrollo está en curso y se implementan los recursos Azure, no será necesario aprovisionar recursos Azure cada vez que se actualice el código; esto es especialmente cierto para el ciclo de desarrollo del programador.

Para acelerar la implementación de cambios de código, azd admite la implementación de actualizaciones de código en la imagen de contenedor. Esto se hace mediante el 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>

No es necesario implementar todos los servicios cada vez. azd entiende el modelo de proyecto de .NET.NET Aspire, es posible implementar solo uno de los servicios especificados mediante el siguiente comando:

azd deploy webfrontend

Para obtener más información, consulte la referencia Azure Developer CLI: azd deploy.

Implementación de actualizaciones de infraestructura

Siempre que cambie la estructura de dependencias de un proyecto de .NET.NET Aspire, azd debe volver a aprovisionar los recursos de Azure subyacentes. El comando azd provision se usa para aplicar estos cambios a la infraestructura.

Para ver esto en acción, actualice el archivo Program.cs en el proyecto AppHost a lo siguiente:

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();

Guarde el archivo y emita el siguiente comando:

azd provision

El comando azd provision actualiza la infraestructura mediante la creación de una aplicación contenedora para hospedar la base de datos de Postgres. El comando azd provision no actualizó las cadenas de conexión del contenedor de apiservice. Para que las cadenas de conexión se actualicen para que apunten a la base de datos Postgres recién aprovisionada, el comando azd deploy debe invocarse de nuevo. En caso de duda, use azd up para aprovisionar y desplegar.

Limpieza de recursos

Recuerde limpiar los recursos de Azure que ha creado durante este tutorial. Dado que 'azd' conoce el grupo de recursos en el que creó los recursos, se puede utilizar para desactivar el entorno utilizando el siguiente comando:

azd down

El comando anterior puede tardar algún tiempo en ejecutarse, pero cuando se complete el grupo de recursos y se deben eliminar todos sus recursos.

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.

Generar Bicep a partir del modelo de proyecto .NET.NET Aspire

Aunque los equipos de desarrollo pueden usar comandos de azd up (o azd provision y azd deploy) para sus implementaciones con fines de desarrollo y producción, algunos equipos pueden optar por generar archivos de Bicep que pueden revisar y administrar como parte del control de versiones (esto también permite que se haga referencia a estos archivos de Bicep como parte de una implementación de Azure más compleja).

azd incluye la capacidad de generar el Bicep que utiliza para el aprovisionamiento mediante el siguiente comando:

azd config set alpha.infraSynth on
azd infra synth

Después de ejecutar este comando en el ejemplo de plantilla de inicio que se usa en esta guía, se crean los siguientes archivos en el directorio del proyecto AppHost:

  • infra/main.bicep: Representa el punto de entrada principal de la implementación.
  • infra/main.parameters.json: se usa como los parámetros para el principal Bicep (se asigna a las variables de entorno definidas en la carpeta .azure).
  • infra/resources.bicep: Define los recursos de Azure necesarios para apoyar el modelo de proyecto de .NET Aspire.
  • AspireSample.Web/manifests/containerApp.tmpl.yaml: la definición de la aplicación contenedora para webfrontend.
  • AspireSample.ApiService/manifests/containerApp.tmpl.yaml: la definición de la aplicación contenedora para apiservice.

El archivo infra\resources.bicep no contiene ninguna definición de las aplicaciones de contenedor en sí mismas (con la excepción de las aplicaciones de contenedor que son dependencias, como Redis y 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

Para obtener más información sobre el uso de Bicep para automatizar las implementaciones en Azure consulte ¿Qué es Bicep?

La definición de las aplicaciones de contenedor de los proyectos de servicio de .NET se incluye en los archivos containerApp/tmpl.yaml en el directorio manifests de cada proyecto, respectivamente. Este es un ejemplo del proyecto de 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

Después de ejecutar el comando azd infra synth, cuando se llama azd provision y azd deploy usan Bicep y admiten archivos generados.

Importante

Si se vuelve a llamar a azd infra synth, reemplaza los archivos modificados por los generados recientemente y le pide confirmación antes de hacerlo.

Entornos aislados para la depuración

Dado que azd facilita el aprovisionamiento de nuevos entornos, es posible que cada miembro del equipo tenga un entorno aislado hospedado en la nube para depurar código en una configuración que coincida estrechamente con la producción. Al hacer esto, cada miembro del equipo debe crear su propio entorno mediante el siguiente comando:

azd env new

Esto le pedirá al usuario información de suscripción y grupo de recursos de nuevo y las siguientes azd up, azd provisiony azd deploy invocaciones usarán este nuevo entorno de forma predeterminada. El modificador --environment se puede aplicar a estos comandos para cambiar entre entornos.

Limpieza de recursos

Ejecute el siguiente comando Azure CLI para eliminar el grupo de recursos cuando ya no necesite los recursos de Azure que creó. Al eliminar el grupo de recursos también se eliminan los recursos contenidos en él.

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

Para obtener más información, consulte Limpieza de recursos en Azure.