Bereitstellen von Orleans in Azure Container Apps

In diesem Tutorial erfahren Sie, wie Sie eine Orleans-Beispielanwendung für einen Warenkorb in Azure Container Apps bereitstellen. In diesem Tutorial wird die Funktionalität der Orleans-Beispiel-Warenkorb-App erweitert, die unter Bereitstellen von Orleans in Azure App Service eingeführt wurde. Die Beispiel-App fügt die Azure Active Directory (AAD) B2C-Authentifizierung (Business-to-Consumer) hinzu und wird in Azure Container Apps bereitgestellt.

Sie erfahren, wie Sie die Bereitstellung mithilfe von GitHub Actions, den .NET und Azure CLIs sowie Azure Bicep durchführen. Darüber hinaus erfahren Sie, wie Sie den HTTP-Eingang der Container-App konfigurieren.

In diesem Tutorial lernen Sie Folgendes:

  • Bereitstellen einer Orleans-Anwendung in Azure Container Apps
  • Automatisieren der Bereitstellung mit GitHub Actions und Azure Bicep
  • Konfigurieren des HTTP-Eingangs

Voraussetzungen

Lokales Ausführen der App

Um die App lokal auszuführen, forken Sie das Repository Azure-Beispiele: Orleans-Warenkorb in Azure Container Apps, und klonen Sie es auf Ihren lokalen Computer. Öffnen Sie die Projektmappe nach dem Klonen in einer IDE Ihrer Wahl. Wenn Sie Visual Studio verwenden, klicken Sie mit der rechten Maustaste auf das Projekt Orleans.ShoppingCart.Silo, und wählen Sie Als Startprojekt festlegen aus. Führen Sie dann die App aus. Andernfalls können Sie die App mit dem folgenden .NET CLI-Befehl ausführen:

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

Weitere Informationen finden Sie unter dotnet run. Bei Ausführung der App wird ihnen eine Landing Page angezeigt, auf der die Funktionalität der App erläutert wird. In der oberen rechten Ecke wird eine Anmeldeschaltfläche angezeigt. Sie können sich für ein Konto registrieren bzw. sich anmelden, wenn Sie bereits über ein Konto verfügen. Nach der Anmeldung können Sie die App erkunden und die Funktionen ausprobieren. Bei lokaler Ausführung basiert die gesamte Funktionalität der App auf In-Memory-Persistenz und lokalem Clustering. Zum Generieren von Pseudoprodukten wird das Bogus NuGet-Paket verwendet. Beenden Sie die App durch Auswählen der Option Debuggen beenden in Visual Studio oder durch Drücken von STRG+C in der .NET CLI.

AAD B2C

Die Erläuterung der Authentifizierungskonzepte würde den Rahmen dieses Tutorials übersteigen, aber Sie erfahren mehr über das Erstellen eines Azure Active Directory B2C-Mandanten und können dann eine Web-App registrieren, um ihn zu nutzen. Im Fall dieser Warenkorb-Beispiel-App muss die resultierende bereitgestellte Container Apps-URL im B2C-Mandanten registriert werden. Weitere Informationen hierzu finden Sie unter ASP.NET Core-Blazor-Authentifizierung und -Autorisierung.

Wichtig

Nachdem Ihre Container-App bereitgestellt wurde, müssen Sie die App-URL im B2C-Mandanten registrieren. In den meisten Produktionsszenarien müssen Sie die URL der App nur einmal registrieren, da sie sich nicht ändern sollte.

Sehen Sie sich das folgende Diagramm an, in dem veranschaulicht wird, wie die App innerhalb der Azure Container Apps-Umgebung isoliert ist:

HTTP-Eingang von Azure Container Apps

Im obigen Diagramm wird der gesamte eingehende Datenverkehr an die App über einen gesicherten HTTP-Eingang weitergeleitet. Die Azure Container Apps-Umgebung enthält eine App-Instanz, und die App-Instanz enthält einen ASP.NET Core-Host, der die Blazor Server- und Orleans-App-Funktionalität verfügbar macht.

Bereitstellen von Azure Container Apps

Um die App in Azure Container Apps bereitzustellen, verwendet das Repository GitHub Actions. Bevor diese Bereitstellung erfolgen kann, benötigen Sie einige Azure-Ressourcen, und Sie müssen das GitHub-Repository ordnungsgemäß konfigurieren.

Vor der Bereitstellung der App müssen Sie eine Azure-Ressourcengruppe erstellen (oder sie können eine vorhandene verwenden). Verwenden Sie einen der folgenden Artikel, um eine neue Azure-Ressourcengruppe zu erstellen:

Notieren Sie sich den von Ihnen ausgewählten Ressourcengruppennamen. Sie benötigen ihn später, um die App bereitzustellen.

Erstellen eines Dienstprinzipals

Um die Bereitstellung der App zu automatisieren, müssen Sie einen Dienstprinzipal erstellen. Hierbei handelt es sich um ein Microsoft-Konto, das über die Berechtigung zum Verwalten von Azure-Ressourcen in Ihrem Namen verfügt.

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

Die erstellten JSON-Anmeldeinformationen sehen in etwa wie folgt aus, enthalten jedoch die tatsächlichen Werten für Ihren Client, Ihr Abonnement und Ihren Mandanten:

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

Kopieren Sie die Ausgabe des Befehls in die Zwischenablage, und fahren Sie mit dem nächsten Schritt fort.

Erstellen eines GitHub-Geheimnisses.

GitHub bietet einen Mechanismus zum Erstellen verschlüsselter Geheimnisse. Die von Ihnen erstellten Geheimnisse können in GitHub Actions-Workflows verwendet werden. Es wird gezeigt, wie die Bereitstellung der App mithilfe von GitHub Actions in Verbindung mit Azure Bicep automatisiert werden kann. Bicep ist eine domänenspezifische Sprache (Domain-Specific Language, DSL), die eine deklarative Syntax zur Bereitstellung von Azure-Ressourcen verwendet. Weitere Informationen finden Sie unter Was ist Bicep? Mithilfe der Ausgabe aus dem Schritt Erstellen eines Dienstprinzipals müssen Sie ein GitHub-Geheimnis namens AZURE_CREDENTIALS mit den JSON-formatierten Anmeldeinformationen erstellen.

Wählen Sie im GitHub-Repository Einstellungen>Geheimnisse>Neues Geheimnis erstellen aus. Geben Sie den Namen AZURE_CREDENTIALS ein, und fügen Sie die JSON-Anmeldeinformationen aus dem vorherigen Schritt in das Feld Wert ein.

GitHub-Repository: Einstellungen > Geheimnisse

Weitere Informationen finden Sie unter GitHub: Verschlüsselte Geheimnisse.

Vorbereiten der Azure-Bereitstellung

Die App muss für die Bereitstellung gepackt werden. Im Projekt Orleans.ShoppingCart.Silos definieren wir ein Target-Element, das nach dem Publish-Schritt ausgeführt wird. Dadurch wird das Veröffentlichungsverzeichnis in einer Datei silo.zip komprimiert:

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

Es gibt viele Möglichkeiten, eine .NET-App in Azure Container Apps bereitzustellen. In diesem Tutorial verwenden Sie GitHub Actions, Azure Bicep sowie die .NET und die Azure CLI. Betrachten Sie die Datei ./github/workflows/deploy.yml im Stammverzeichnis des GitHub-Repositorys:

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

Der vorherige GitHub-Workflow führt Folgendes aus:

  • Veröffentlichen der Warenkorb-App mit dem Befehl dotnet publish als ZIP-Datei
  • Anmelden bei Azure mit den Anmeldeinformationen aus dem Schritt Erstellen eines Dienstprinzipals
  • Auswerten der Datei acr.bicep und Starten einer Bereitstellungsgruppe mit az deployment group create
  • Abrufen des ACR-Anmeldeservers (Azure Container Registry) aus der Bereitstellungsgruppe
  • Anmelden bei ACR mit dem Repositorygeheimnis AZURE_CREDENTIALS
  • Erstellen und Veröffentlichen des Siloimage in ACR
  • Auswerten der Datei main.bicep und Starten einer Bereitstellungsgruppe mit az deployment group create
  • Bereitstellen des Silos
  • Abmelden von Azure

Der Workflow wird durch einen Push an den Mainbranch ausgelöst. Weitere Informationen finden Sie unter GitHub Actions und .NET.

Tipp

Wenn beim Ausführen des Workflows Probleme auftreten, müssen Sie möglicherweise überprüfen, ob für den Dienstprinzipal alle erforderlichen Anbieternamespaces registriert sind. Die folgenden Anbieternamespaces sind erforderlich:

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

Weitere Informationen finden Sie unter Beheben von Fehlern bei der Registrierung von Ressourcenanbietern.

Azure erzwingt Namenseinschränkungen und -konventionen für Ressourcen. Sie müssen die Werte der Datei deploy.yml folgendermaßen aktualisieren:

  • UNIQUE_APP_NAME
  • SILO_IMAGE_NAME
  • AZURE_RESOURCE_GROUP_NAME
  • AZURE_RESOURCE_GROUP_LOCATION

Legen Sie diese Werte auf Ihren eindeutigen App-Namen und Ihren Azure-Ressourcengruppennamen und -standort fest.

Weitere Informationen finden Sie unter Benennungsregeln und -einschränkungen für Azure-Ressourcen.

Erkunden der Bicep-Vorlagen

Bei Ausführung des Befehls az deployment group create wird ein bestimmter .bicep-Dateiverweis ausgewertet. Diese Datei enthält deklarative Informationen zu den Azure-Ressourcen, die Sie bereitstellen möchten. Diesen Schritt kann man sich beispielsweise so vorstellen, dass darin alle Ressourcen für die Bereitstellung bereitgestellt werden.

Wichtig

Wenn Sie Visual Studio Code verwenden, wird die Bicep-Erstellung durch die Bicep-Erweiterung verbessert.

Als erste Bicep-Datei wird die Datei acr.bicep ausgewertet. Diese Datei enthält die Ressourcendetails des ACR-Anmeldeservers (Azure Container Registry):

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

Diese Bicep-Datei gibt den ACR-Anmeldeserver und den entsprechenden Namen aus. Die nächste gefundene Bicep-Datei enthält mehr als nur eine einzelne resource. Betrachten Sie die Datei main.bicep, die hauptsächlich aus delegierenden module-Definitionen besteht:

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

Die vorherige Bicep-Datei:

  • Verweist auf eine existing-ACR-Ressource. Weitere Informationen finden Sie unter Azure Bicep: Vorhandene Ressourcen.
  • Definiert eine module env, die an die Definitionsdatei environment.bicep delegiert.
  • Definiert ein module storageModule, das an die Definitionsdatei storage.bicep delegiert.
  • Deklariert mehrere freigegebene envVars, die vom Silomodul verwendet werden.
  • Definiert ein module siloModule, das an die Definitionsdatei container-app.bicep delegiert.
  • Gibt die ACA-URL aus. (Diese kann möglicherweise verwendet werden, um den Umleitungs-URI einer vorhandenen AAD B2C-App-Registrierung zu aktualisieren.)

Die Datei main.bicep delegiert an mehrere andere Bicep-Dateien. Die erste ist die Datei 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

Diese Bicep-Datei definiert die Azure Log Analytics- und Application Insights-Ressourcen. Die appInsights-Ressource ist ein web-Typ, und die logs-Ressource ist ein PerGB2018-Typ. Sowohl die appInsights-Ressource als auch die logs-Ressource werden am Standort der Ressourcengruppe bereitgestellt. Die appInsights-Ressource ist über die WorkspaceResourceId-Eigenschaft mit der logs-Ressource verknüpft. In dieser Bicep-Datei sind drei Ausgaben definiert, die später vom Container Apps-module verwendet werden. Als Nächstes sehen wir uns die Datei storage.bicep an:

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}'

Die vorangehende Bicep-Datei definiert Folgendes:

  • Zwei Parameter für den Ressourcengruppennamen und den App-Namen.
  • Die resource storage-Definition für das Speicherkonto.
  • Ein einzelnes output-Element, das die Verbindungszeichenfolge für das Speicherkonto erstellt.

Die letzte Bicep-Datei ist die Datei 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

Die oben genannte Visual Studio Code-Erweiterung für Bicep enthält eine Schnellansicht. All diese Bicep-Dateien werden wie folgt visualisiert:

Orleans: Schnellansichtsrendering für Bicep-Bereitstellung einer Warenkorb-Beispiel-App

Zusammenfassung

Wenn Sie den Quellcode aktualisieren und Änderungen per push an den main-Branch des Repositorys übertragen, wird der Deploy.yml-Workflow ausgeführt. Er stellt die in den Bicep-Dateien definierten Azure-Ressourcen und dann die Anwendung bereit. Revisionen werden automatisch in Ihrer Azure Container Registry-Instanz registriert.

Zusätzlich zur Schnellansicht der Bicep-Erweiterung sieht die Seite der Ressourcengruppe im Azure-Portal nach dem Bereitstellen der Anwendung in etwa wie folgendermaßen aus:

Azure-Portal: Ressourcen der Orleans-Warenkorb-Beispiel-App für Azure Container Apps

Siehe auch