Distribuire Orleans in App Azure Container

In questa esercitazione si apprenderà come distribuire un'applicazione carrello acquisti di esempio Orleans in App Azure Container. Questa esercitazione espande la funzionalità dell'app carrello acquisti di esempioOrleans, introdotta in Distribuisci Orleans in Servizio app di Azure. L'app di esempio aggiunge l'autenticazione business-to-consumer (AAD) di Azure Active Directory (B2C) e viene distribuita in App Azure Container.

Si apprenderà come distribuire usando GitHub Actions, .NET e Azure CLIs e Azure Bicep. Si apprenderà anche come configurare l'ingresso HTTP dell'app contenitore.

In questa esercitazione verranno illustrate le procedure per:

  • Distribuire un'applicazione Orleans in App Azure Container
  • Automatizzare la distribuzione con GitHub Actions e Azure Bicep
  • Configurare l'ingresso HTTP

Prerequisiti

Eseguire l'app in locale

Per eseguire l'app in locale, eseguire il fork degli esempi di Azure: Orleans carrello acquisti nel repository di App azure Container e clonarlo nel computer locale. Dopo aver clonato, aprire la soluzione in un IDE scelto. Se si usa Visual Studio, fare clic con il pulsante destro del mouse su Orleans. Il progetto ShoppingCart.Silo e selezionare Imposta come progetto di avvio, quindi eseguire l'app. In caso contrario, è possibile eseguire l'app usando il comando dell'interfaccia della riga di comando .NET seguente:

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

Per altre informazioni, vedere dotnet run. Con l'app in esecuzione, viene visualizzata una pagina di destinazione che illustra la funzionalità dell'app. Nell'angolo in alto a destra verrà visualizzato un pulsante di accesso. È possibile iscriversi per un account o accedere se si ha già un account. Dopo aver eseguito l'accesso, è possibile spostarsi in giro e si è liberi di testare le relative funzionalità. Tutte le funzionalità dell'app durante l'esecuzione in locale si basano sulla persistenza in memoria, sul clustering locale e usa il pacchetto NuGet bogus per generare prodotti falsi. Arrestare l'app selezionando l'opzione Arresta debug in Visual Studio o premendo CTRL+C nell'interfaccia della riga di comando .NET.

AAD B2C

Durante l'insegnamento dei concetti di autenticazione oltre l'ambito di questa esercitazione, è possibile apprendere come creare un tenant B2C di Azure Active Directory e quindi registrare un'app Web per usarla. Nel caso di questa app del carrello acquisti, l'URL delle app contenitore distribuite risultante dovrà essere registrato nel tenant B2C. Per altre informazioni, vedere ASP.NET Core l'autenticazione e l'autorizzazione di Blazor.

Importante

Dopo la distribuzione dell'app contenitore, è necessario registrare l'URL dell'app nel tenant B2C. Nella maggior parte degli scenari di produzione è necessario registrare l'URL dell'app una sola volta perché non dovrebbe cambiare.

Per visualizzare come l'app è isolata all'interno dell'ambiente App Azure Container, vedere il diagramma seguente:

Ingresso HTTP di App Azure Container.

Nel diagramma precedente, tutto il traffico in ingresso all'app viene instradato tramite un ingresso HTTP protetto. L'ambiente App Azure Container contiene un'istanza di app e l'istanza dell'app contiene un host ASP.NET Core, che espone la funzionalità Blazor Server e Orleans app.

Distribuire in App Azure Container

Per distribuire l'app in App Azure Container, il repository usa GitHub Actions. Prima che questa distribuzione possa essere eseguita, saranno necessarie alcune risorse di Azure e sarà necessario configurare correttamente il repository GitHub.

Prima di distribuire l'app, è necessario creare un gruppo di risorse di Azure oppure scegliere di usare uno esistente. Per creare un nuovo gruppo di risorse di Azure, usare uno degli articoli seguenti:

Prendere nota del nome del gruppo di risorse scelto, sarà necessario in un secondo momento per distribuire l'app.

Creare un'entità servizio

Per automatizzare la distribuzione dell'app, è necessario creare un'entità servizio. Si tratta di un account Microsoft che dispone dell'autorizzazione per gestire le risorse di Azure per conto dell'utente.

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

Le credenziali JSON create avranno un aspetto simile al seguente, ma con valori effettivi per il client, la sottoscrizione e il tenant:

{
  "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"
}

Copiare l'output del comando negli Appunti e continuare con il passaggio successivo.

Creare un segreto GitHub

GitHub fornisce un meccanismo per la creazione di segreti crittografati. I segreti creati sono disponibili per l'uso nei flussi di lavoro GitHub Actions. Si vedrà come è possibile usare GitHub Actions per automatizzare la distribuzione dell'app, in combinazione con Azure Bicep. Bicep è un linguaggio specifico del dominio che usa una sintassi dichiarativa per distribuire le risorse di Azure. Per altre informazioni, vedere Informazioni su Bicep. Usando l'output dal passaggio Crea un'entità servizio , è necessario creare un segreto GitHub denominato AZURE_CREDENTIALS con le credenziali formattate JSON.

All'interno del repository GitHub selezionare Impostazioni>segreti>Crea un nuovo segreto. Immettere il nome AZURE_CREDENTIALS e incollare le credenziali JSON dal passaggio precedente nel campo Valore .

Repository GitHub: Segreti delle impostazioni >

Per altre informazioni, vedere GitHub: Segreti crittografati.

Preparare la distribuzione di Azure

L'app deve essere in pacchetto per la distribuzione. Orleans.ShoppingCart.Silos Nel progetto viene definito un Target elemento eseguito dopo il Publish passaggio. Verrà zip la directory di pubblicazione in un file disilo.zip :

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

Esistono molti modi per distribuire un'app .NET in App Azure Container. In questa esercitazione si usano GitHub Actions, Azure Bicep e .NET e clI di Azure. Considerare il file ./github/workflow/deploy.yml nella radice del repository 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

Il flusso di lavoro GitHub precedente:

Il flusso di lavoro viene attivato da un push nel ramo principale . Per altre informazioni, vedere GitHub Actions e .NET.

Suggerimento

Se si verificano problemi durante l'esecuzione del flusso di lavoro, potrebbe essere necessario verificare che l'entità servizio disponga di tutti gli spazi dei nomi del provider necessari registrati. Sono necessari gli spazi dei nomi del provider seguenti:

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

Per altre informazioni, vedere Risolvere gli errori per la registrazione del provider di risorse.

Azure impone restrizioni di denominazione e convenzioni per le risorse. È necessario aggiornare i valori del file deploy.yml per quanto segue:

  • UNIQUE_APP_NAME
  • SILO_IMAGE_NAME
  • AZURE_RESOURCE_GROUP_NAME
  • AZURE_RESOURCE_GROUP_LOCATION

Impostare questi valori sul nome dell'app univoca e sul nome e sulla posizione del gruppo di risorse di Azure.

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

Esplorare i modelli Bicep

Quando viene eseguito il az deployment group create comando, valuterà un riferimento al file con estensione bicep specificato. Questo file contiene informazioni dichiarative che specificano le risorse di Azure da distribuire. Un modo per pensare a questo passaggio è che effettua il provisioning di tutte le risorse per la distribuzione.

Importante

Se si usa Visual Studio Code, l'esperienza di creazione bicep viene migliorata quando si usa l'estensione Bicep.

Il primo file Bicep valutato è il file acr.bicep . Questo file contiene i dettagli delle risorse del server di accesso del server di accesso di Registro Azure Container (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

Questo file bicep restituisce il server di accesso del Registro Azure Container e il nome corrispondente. Il file Bicep successivo rilevato contiene più di un singolo resourceoggetto . Considerare il file main.bicep costituito principalmente dalle definizioni di delega module :

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

Il file Bicep precedente:

  • existing Per altre informazioni, vedere Azure Bicep: Risorse esistenti.
  • Definisce un oggetto module env che delega il file di definizione environment.bicep .
  • Definisce un oggetto module storageModule che delega il file di definizione storage.bicep .
  • Dichiara diversi condivisi envVars usati dal modulo silo.
  • Definisce un oggetto module siloModule che delega il file di definizione container-app.bicep .
  • Restituisce l'URL di ACA (questo potrebbe essere usato per aggiornare un URI di reindirizzamento dell'app B2C esistente).

I delegati main.bicep vengono eseguiti in diversi altri file Bicep. Il primo è il file 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

Questo file bicep definisce le risorse di Azure Log Analytics e Application Insights. La appInsights risorsa è un tipo e la logs risorsa è un webPerGB2018 tipo. Sia la appInsights risorsa che la logs risorsa vengono sottoposte a provisioning nella posizione del gruppo di risorse. La appInsights risorsa è collegata alla logs risorsa tramite la WorkspaceResourceId proprietà . In questo bicep sono definiti tre output, usati più avanti da App modulecontenitore. Si esaminerà quindi il file 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}'

Il file Bicep precedente definisce quanto segue:

  • Due parametri per il nome del gruppo di risorse e il nome dell'app.
  • Definizione resource storage dell'account di archiviazione.
  • output Singolo che costruisce la stringa di connessione per l'account di archiviazione.

L'ultimo file Bicep è il file 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

L'estensione di Visual Studio Code precedente per Bicep include un visualizzatore. Tutti questi file Bicep vengono visualizzati come segue:

Orleans: App di esempio del carrello acquisti Bicep provisioning visualizzatore di rendering.

Riepilogo

Durante l'aggiornamento del codice sorgente e push le modifiche apportate al main ramo del repository, verrà eseguito il flusso di lavoro deploy.yml . Esegue il provisioning delle risorse di Azure definite nei file Bicep e distribuisce l'applicazione. Le revisioni vengono registrate automaticamente nel Registro Azure Container.

Oltre al visualizzatore dall'estensione Bicep, la pagina del gruppo di risorse portale di Azure sarà simile all'esempio seguente dopo il provisioning e la distribuzione dell'applicazione:

Portale di Azure: Orleans risorse dell'app di esempio di carrello per Azure Container Apps.

Vedi anche