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
- Un account GitHub
- Leggere un'introduzione a Orleans
- .NET 6 SDK
- L'interfaccia della riga di comando di Azure
- Ambiente di sviluppo integrato .NET (IDE)
- È possibile usare Visual Studio o Visual Studio Code
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:
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 .
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:
- Pubblicare l'app carrello acquisti come file zip usando il comando dotnet publish .
- Accedere ad Azure usando le credenziali dal passaggio Crea un'entità servizio .
- Valutare il file acr.bicep e avviare un gruppo di distribuzione usando az deployment group create.
- Ottenere il server di accesso Registro Azure Container (ACR) dal gruppo di distribuzione.
- Accedere a Registro Azure Container usando il segreto dei repository
AZURE_CREDENTIALS
. - Compilare e pubblicare l'immagine silo nel Registro Azure Container.
- Valutare il file main.bicep e avviare un gruppo di distribuzione usando az deployment group create.
- Distribuire il silo
- Disconnessione di Azure.
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 resource
oggetto . 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 web
PerGB2018
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 module
contenitore. 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:
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: