Implementación de Orleans en Azure Container Apps

En este tutorial, aprenderá a implementar una aplicación de ejemplo de carro de la compra de Orleans en Azure Container Apps. En este tutorial se amplía la funcionalidad de la aplicación de ejemplo de carro de la compra de Orleans, introducida en Implementación de Orleans en Azure App Service. La aplicación de ejemplo agrega la autenticación de negocio a consumidor (B2C) de Azure Active Directory (AAD) y la implementa en Azure Container Apps.

Aprenderá a realiza la implementación con la funcionalidad Acciones de GitHub, las CLI de .NET y Azure y Azure Bicep. Además, aprenderá a configurar la entrada HTTP de la aplicación contenedora.

En este tutorial, aprenderá a:

  • Implementación de una aplicación de Orleans en Azure Container Apps
  • Automatización de la implementación mediante Acciones de GitHub y Azure Bicep
  • Configurar la entrada HTTP.

Requisitos previos

Ejecución de la aplicación de forma local

Para ejecutar la aplicación localmente, bifurque el repositorio de Azure Samples: carro de la compra de Orleans en Azure Container Apps y clónelo en la máquina local. Una vez clonado, abra la solución en un IDE de su elección. Si usa Visual Studio, haga clic con el botón derecho en el proyecto Orleans.ShoppingCart.Silo y seleccione Establecer como proyecto de inicio y ejecute la aplicación. De lo contrario, puede ejecutar la aplicación mediante el siguiente comando de la CLI de .NET:

dotnet run --project Silo\Orleans.ShoppingCart.Silo.csproj

Para más información, vea Ejecución de dotnet. Con la aplicación en ejecución, se le presenta una página de aterrizaje que describe la funcionalidad de la aplicación. En la esquina superior derecha, verá un botón de inicio de sesión. Puede registrarse para obtener una cuenta o iniciar sesión si ya tiene una. Cuando haya iniciado sesión, puede recorrerla y probar sus funcionalidades libremente. Toda la funcionalidad de la aplicación cuando se ejecuta localmente se basa en la persistencia en memoria, la agrupación en clústeres locales y en el uso del paquete NuGet Bogus para generar productos falsos. Detenga la aplicación. Para ello, seleccione la opción Detener depuración en Visual Studio o presione Ctrl+C en la CLI de .NET.

AAD B2C

Aunque la enseñanza de los conceptos de autenticación está fuera del ámbito de este tutorial, puede aprender a crear un inquilino de Azure Active Directory B2C y, luego, puede registrar una aplicación web para consumirlo. En el caso de esta aplicación de ejemplo de carro de la compra, la dirección URL de Container Apps implementada resultante deberá registrarse en el inquilino de B2C. Para más información, consulte Autenticación y autorización de ASP.NET Core.

Importante

Una vez implementada la aplicación contenedora, deberá registrar la dirección URL de la aplicación en el inquilino de B2C. En la mayoría de los escenarios de producción, solo tendrá que registrar la dirección URL de la aplicación una vez, ya que lo normal es que no cambie.

Para ayudar a visualizar cómo la aplicación está aislada en el entorno de Azure Container Apps, consulte el diagrama siguiente:

Entrada HTTP de Azure Container Apps.

En el diagrama anterior, todo el tráfico entrante a la aplicación se canaliza mediante una entrada HTTP segura. El entorno de Azure Container Apps contiene una instancia de aplicación y esta contiene un host de ASP.NET Core que expone la funcionalidad de la aplicación de Blazor Server y Orleans.

Implementación en Azure Container Apps

Para implementar la aplicación en Azure Container Apps, el repositorio usa Acciones de GitHub. Para poder realizar esta implementación, necesitará algunos recursos de Azure y deberá configurar correctamente el repositorio de GitHub.

Antes de implementar la aplicación, debe crear un grupo de recursos de Azure (o bien puede optar por usar uno existente). Para crear un nuevo grupo de recursos de Azure, use uno de los siguientes artículos:

Anote el nombre del grupo de recursos que elija, ya que lo necesitará más adelante para implementar la aplicación.

Creación de una entidad de servicio

Para automatizar la implementación de la aplicación, deberá crear una entidad de servicio. Se trata de una cuenta de Microsoft que tiene permiso para administrar los recursos de Azure en su nombre.

az ad sp create-for-rbac --sdk-auth --role Contributor \
  --name "<display-name>"  --scopes /subscriptions/<your-subscription-id>

Las credenciales JSON creadas tendrán un aspecto similar al siguiente, pero con valores reales para el cliente, la suscripción y el inquilino:

{
  "clientId": "<your client id>",
  "clientSecret": "<your client secret>",
  "subscriptionId": "<your subscription id>",
  "tenantId": "<your tenant id>",
  "activeDirectoryEndpointUrl": "https://login.microsoftonline.com/",
  "resourceManagerEndpointUrl": "https://brazilus.management.azure.com",
  "activeDirectoryGraphResourceId": "https://graph.windows.net/",
  "sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
  "galleryEndpointUrl": "https://gallery.azure.com",
  "managementEndpointUrl": "https://management.core.windows.net"
}

Copie el resultado del comando en el portapapeles y continúe con el paso siguiente.

Creación de un secreto de GitHub

GitHub proporciona un mecanismo para crear secretos cifrados. Los secretos que cree están disponibles para usarlos en flujos de trabajo de Acciones de GitHub. Verá cómo se pueden usar Acciones de GitHub para automatizar la implementación de la aplicación, junto con Azure Bicep. Bicep es un lenguaje específico de dominio (DSL) que usa una sintaxis declarativa para implementar recursos de Azure. Para obtener más información, consulte ¿Qué es Bicep? Con el resultado del paso Crear una entidad de servicio, deberá crear un secreto de GitHub denominado AZURE_CREDENTIALS con las credenciales con formato JSON.

En el repositorio de GitHub, seleccione Configuración>Secretos>Crear un nuevo secreto. Introduzca el nombre AZURE_CREDENTIALS y pegue las credenciales JSON del paso anterior en el campo Valor.

Repositorio GitHub: Configuración > Secretos

Para más información, consulte GitHub: Secretos cifrados.

Preparación para la implementación de Azure

La aplicación tendrá que empaquetarse para la implementación. En el proyecto Orleans.ShoppingCart.Silos, se define un elemento Target que se ejecuta después del paso Publish. Esto comprimirá el directorio publish en el archivo silo.zip:

<Target Name="ZipPublishOutput" AfterTargets="Publish">
    <Delete Files="$(ProjectDir)\..\silo.zip" />
    <ZipDirectory SourceDirectory="$(PublishDir)" DestinationFile="$(ProjectDir)\..\silo.zip" />
</Target>

Hay muchas maneras de implementar una aplicación .NET en Azure Container Apps. En este tutorial, se usarán Acciones de GitHub, Azure Bicep y las CLI de .NET y Azure. Considere el archivo ./github/workflows/deploy.yml en la raíz del repositorio de GitHub:

name: Deploy to Azure Container Apps

on:
  push:
    branches:
    - main

env:
  UNIQUE_APP_NAME: orleanscart
  SILO_IMAGE_NAME: orleanscart-silo
  AZURE_RESOURCE_GROUP_NAME: orleans-resourcegroup
  AZURE_RESOURCE_GROUP_LOCATION: eastus

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3

    - name: Setup .NET 6.0
      uses: actions/setup-dotnet@v3
      with:
        dotnet-version: 6.0.x

    - name: .NET publish shopping cart app
      run: dotnet publish ./Silo/Orleans.ShoppingCart.Silo.csproj --configuration Release

    - name: Login to Azure
      uses: azure/login@v1
      with:
        creds: ${{ secrets.AZURE_CREDENTIALS }}

    - name: Flex ACR Bicep
      run: |
        az deployment group create \
          --resource-group ${{ env.AZURE_RESOURCE_GROUP_NAME }} \
          --template-file '.github/workflows/flex/acr.bicep' \
          --parameters location=${{ env.AZURE_RESOURCE_GROUP_LOCATION }}

    - name: Get ACR Login Server
      run: |
        ACR_NAME=$(az deployment group show -g ${{ env.AZURE_RESOURCE_GROUP_NAME }} -n acr \
        --query properties.outputs.acrName.value | tr -d '"')
        echo "ACR_NAME=$ACR_NAME" >> $GITHUB_ENV
        ACR_LOGIN_SERVER=$(az deployment group show -g ${{ env.AZURE_RESOURCE_GROUP_NAME }} -n acr \
        --query properties.outputs.acrLoginServer.value | tr -d '"')
        echo "ACR_LOGIN_SERVER=$ACR_LOGIN_SERVER" >> $GITHUB_ENV

    - name: Prepare Docker buildx
      uses: docker/setup-buildx-action@v1

    - name: Login to ACR
      run: |
        access_token=$(az account get-access-token --query accessToken -o tsv)
        refresh_token=$(curl https://${{ env.ACR_LOGIN_SERVER }}/oauth2/exchange -v \
        -d "grant_type=access_token&service=${{ env.ACR_LOGIN_SERVER }}&access_token=$access_token" | jq -r .refresh_token)
        # The null GUID 0000... tells the container registry that this is an ACR refresh token during the login flow
        docker login -u 00000000-0000-0000-0000-000000000000 \
        --password-stdin ${{ env.ACR_LOGIN_SERVER }} <<< "$refresh_token"

    - name: Build and push Silo image to registry
      uses: docker/build-push-action@v2
      with:
        push: true
        tags: ${{ env.ACR_LOGIN_SERVER }}/${{ env.SILO_IMAGE_NAME }}:${{ github.sha }}
        file: Silo/Dockerfile

    - name: Flex ACA Bicep
      run: |
        az deployment group create \
          --resource-group ${{ env.AZURE_RESOURCE_GROUP_NAME }} \
          --template-file '.github/workflows/flex/main.bicep' \
          --parameters location=${{ env.AZURE_RESOURCE_GROUP_LOCATION }} \
            appName=${{ env.UNIQUE_APP_NAME }} \
            acrName=${{ env.ACR_NAME }} \
            repositoryImage=${{ env.ACR_LOGIN_SERVER }}/${{ env.SILO_IMAGE_NAME }}:${{ github.sha }} \
          --debug

    - name: Get Container App URL
      run: |
        ACA_URL=$(az deployment group show -g ${{ env.AZURE_RESOURCE_GROUP_NAME }} \
        -n main --query properties.outputs.acaUrl.value | tr -d '"')
        echo $ACA_URL

    - name: Logout of Azure
      run: az logout

El flujo de trabajo de GitHub anterior hará lo siguiente:

  • Publicar la aplicación de carro de la compra como archivo ZIP mediante el comando dotnet publish.
  • Iniciar sesión en Azure con las credenciales del paso Crear una entidad de servicio.
  • Evalúe el archivo acr.bicep e inicie un grupo de implementación mediante az deployment group create.
  • Obtenga el servidor de inicio de sesión de Azure Container Registry (ACR) del grupo de implementación.
  • Inicie sesión en ACR mediante el secreto AZURE_CREDENTIALS de los de repositorios.
  • Compile y publique la imagen de silo en el ACR.
  • Evaluar el archivo main.bicep e iniciar un grupo de implementación mediante az deployment group create.
  • Implementación del silo
  • Cierre sesión en Azure.

El flujo de trabajo se desencadena con una inserción en la rama main. Para más información, consulte Acciones de GitHub y .NET.

Sugerencia

Si tiene problemas al ejecutar el flujo de trabajo, es posible que tenga que comprobar que la entidad de servicio tiene registrados todos los espacios de nombres de proveedor necesarios. A continuación se detallan los espacio de nombres de proveedor necesarios:

  • Microsoft.App
  • Microsoft.ContainerRegistry
  • Microsoft.Insights
  • Microsoft.OperationalInsights
  • Microsoft.Storage

Para más información, consulte el artículo Resoluión de errores de registro del proveedor de recursos.

Azure impone restricciones de nomenclatura y convenciones para los recursos. Debe actualizar los valores del archivo deploy.yml para lo siguiente:

  • UNIQUE_APP_NAME
  • SILO_IMAGE_NAME
  • AZURE_RESOURCE_GROUP_NAME
  • AZURE_RESOURCE_GROUP_LOCATION

Establecer estos valores en el nombre de la aplicación único y el nombre y la ubicación del grupo de recursos de Azure.

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

Exploración de las plantillas de Bicep

Cuando se ejecute el comando az deployment group create, evaluará una referencia de archivo .bicep determinada. Este archivo contiene información declarativa que detalla los recursos de Azure que quiere implementar. Una manera de pensar en este paso es que aprovisiona todos los recursos para la implementación.

Importante

Si va a usar Visual Studio Code, la experiencia de creación de Bicep se mejora con la extensión de Bicep.

El primer archivo de Bicep que se evalúa es el archivo acr.bicep. Este archivo contiene los detalles del recurso de servidor de inicio de sesión de Azure Container Registry (ACR):

param location string = resourceGroup().location

resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' = {
  name: toLower('${uniqueString(resourceGroup().id)}acr')
  location: location
  sku: {
    name: 'Basic'
  }
  properties: {
    adminUserEnabled: true
  }
}

output acrLoginServer string = acr.properties.loginServer
output acrName string = acr.name

Este archivo Bicep genera el servidor de inicio de sesión de ACR y el nombre correspondiente. El siguiente archivo de Bicep encontrado contiene más de un único objeto resource. Considere el archivo main.bicep compuesto principalmente por definiciones de module de delegación:

param appName string
param acrName string
param repositoryImage string
param location string = resourceGroup().location

resource acr 'Microsoft.ContainerRegistry/registries@2021-09-01' existing = {
  name: acrName
}

module env 'environment.bicep' = {
  name: 'containerAppEnvironment'
  params: {
    location: location
    operationalInsightsName: '${appName}-logs'
    appInsightsName: '${appName}-insights'
  }
}

var envVars = [
  {
    name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
    value: env.outputs.appInsightsInstrumentationKey
  }
  {
    name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
    value: env.outputs.appInsightsConnectionString
  }
  {
    name: 'ORLEANS_AZURE_STORAGE_CONNECTION_STRING'
    value: storageModule.outputs.connectionString
  }
  {
    name: 'ASPNETCORE_FORWARDEDHEADERS_ENABLED'
    value: 'true'
  }
]

module storageModule 'storage.bicep' = {
  name: 'orleansStorageModule'
  params: {
    name: '${appName}storage'
    location: location
  }
}

module siloModule 'container-app.bicep' = {
  name: 'orleansSiloModule'
  params: {
    appName: appName
    location: location
    containerAppEnvironmentId: env.outputs.id
    repositoryImage: repositoryImage
    registry: acr.properties.loginServer
    registryPassword: acr.listCredentials().passwords[0].value
    registryUsername: acr.listCredentials().username
    envVars: envVars
  }
}

output acaUrl string = siloModule.outputs.acaUrl

El archivo Bicep anterior:

  • Hace referencia a un recurso de ACR existing. Para más información, consulte Recursos existentes en Azure Bicep.
  • Define un objeto module env que delega en el archivo de definición environment.bicep.
  • Define un objeto module storageModule que delega en el archivo de definición storage.bicep.
  • Declara varios objetos envVars compartidos que usa el módulo de silo.
  • Define un objeto module siloModule que delega en el archivo de definición de container-app.bicep.
  • Genera la dirección URL de ACA (esta podría usarse para actualizar un URI de redirección del registro de aplicación de AAD B2C existente).

El archivo main.bicep delega en otros archivos de Bicep. El primero es en el archivo environment.bicep:

param operationalInsightsName string
param appInsightsName string
param location string

resource appInsights 'Microsoft.Insights/components@2020-02-02' = {
  name: appInsightsName
  location: location
  kind: 'web'
  properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logs.id
  }
}

resource logs 'Microsoft.OperationalInsights/workspaces@2021-06-01' = {
  name: operationalInsightsName
  location: location
  properties: {
    retentionInDays: 30
    features: {
      searchVersion: 1
    }
    sku: {
      name: 'PerGB2018'
    }
  }
}

resource env 'Microsoft.App/managedEnvironments@2022-03-01' = {
  name: '${resourceGroup().name}env'
  location: location
  properties: {
    appLogsConfiguration: {
      destination: 'log-analytics'
      logAnalyticsConfiguration: {
        customerId: logs.properties.customerId
        sharedKey: logs.listKeys().primarySharedKey
      }
    }
  }
}

output id string = env.id
output appInsightsInstrumentationKey string = appInsights.properties.InstrumentationKey
output appInsightsConnectionString string = appInsights.properties.ConnectionString

Este archivo de bicep define los recursos de Azure Log Analytics y Application Insights. El recurso appInsights es un tipo web y el recurso logs es un tipo PerGB2018. Tanto el recurso appInsights como el recurso logs se aprovisionan en la ubicación del grupo de recursos. El recurso appInsights está vinculado al recurso logs a través de la propiedad WorkspaceResourceId. Hay tres resultados definidos en este Bicep, que más adelante usa el objeto module de Container Apps. A continuación, echemos un vistazo al archivo storage.bicep:

param name string
param location string

resource storage 'Microsoft.Storage/storageAccounts@2021-08-01' = {
  name: name
  location: location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
}

var key = listKeys(storage.name, storage.apiVersion).keys[0].value
var protocol = 'DefaultEndpointsProtocol=https'
var accountBits = 'AccountName=${storage.name};AccountKey=${key}'
var endpointSuffix = 'EndpointSuffix=${environment().suffixes.storage}'

output connectionString string = '${protocol};${accountBits};${endpointSuffix}'

El archivo Bicep anterior define lo siguiente:

  • Dos parámetros para el nombre del grupo de recursos y el nombre de la aplicación.
  • La definición de resource storage de la cuenta de almacenamiento.
  • Un único objeto output que construye la cadena de conexión de la cuenta de almacenamiento.

El último archivo de Bicep es el archivo container-app.bicep:

param appName string
param location string
param containerAppEnvironmentId string
param repositoryImage string = 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
param envVars array = []
param registry string
param registryUsername string
@secure()
param registryPassword string

resource containerApp 'Microsoft.App/containerApps@2022-03-01' = {
  name: appName
  location: location
  properties: {
    managedEnvironmentId: containerAppEnvironmentId
    configuration: {
      activeRevisionsMode: 'multiple'
      secrets: [
        {
          name: 'container-registry-password'
          value: registryPassword
        }
      ]
      registries: [
        {
          server: registry
          username: registryUsername
          passwordSecretRef: 'container-registry-password'
        }
      ]
      ingress: {
        external: true
        targetPort: 80
      }
    }
    template: {
      revisionSuffix: uniqueString(repositoryImage, appName)
      containers: [
        {
          image: repositoryImage
          name: appName
          env: envVars
        }
      ]
      scale: {
        minReplicas: 1
        maxReplicas: 1
      }
    }
  }
}

output acaUrl string = containerApp.properties.configuration.ingress.fqdn

La extensión de Visual Studio Code mencionada anteriormente para Bicep incluye un visualizador. Todos estos archivos Bicep se visualizan de la siguiente manera:

Orleans: representación del visualizador de aprovisionamiento de Bicep de la aplicación de ejemplo de carro de la compra.

Resumen

A medida que actualice el código fuente y push cambie a la rama main del repositorio, se ejecutará el flujo de trabajo deploy.yml. Se aprovisionan los recursos de Azure definidos en los archivos de Bicep y se implementa la aplicación. Las revisiones se registran automáticamente en Azure Container Registry.

Además del visualizador de la extensión de Bicep, la página del grupo de recursos de Azure Portal tendría un aspecto similar al ejemplo siguiente después de aprovisionar e implementar la aplicación:

Azure Portal: recursos de la aplicación de ejemplo de carro de la compra de Orleans para Azure Container Apps.

Consulte también