Azure Container Apps에 배포 Orleans

이 자습서에서는 Azure Container Apps에 예제 Orleans 쇼핑 카트 애플리케이션을 배포하는 방법을 알아봅니다. 이 자습서에서는 Azure App Service 배포Orleans에 도입된 샘플 Orleans 쇼핑 카트 앱의 기능을 확장합니다. 샘플 앱은 AAD(Azure Active Directory) B2C(Business-to-Consumer) 인증을 추가하고 Azure Container Apps에 배포합니다.

GitHub Actions, .NET, Azure CLI 및 Azure Bicep을 사용하여 배포하는 방법을 알아봅니다. 또한 컨테이너 앱의 HTTP 수신을 구성하는 방법에 대해 알아봅니다.

이 자습서에서는 다음 작업 방법을 알아봅니다.

  • Orleans Azure Container Apps에 애플리케이션 배포
  • GitHub Actions 및 Azure Bicep을 사용하여 배포 자동화
  • HTTP 수신 구성

사전 요구 사항

로컬에서 앱 실행하기

앱을 로컬로 실행하려면 Azure Container Apps 리포지토리 에서 Azure 샘플: Orleans 쇼핑 카트를 포크하고 로컬 컴퓨터에 복제합니다. 복제된 후 선택한 IDE에서 솔루션을 엽니다. Visual Studio를 사용하는 경우 를 마우스 오른쪽 단추로 Orleans클릭합니다. ShoppingCart.Silo 프로젝트를 선택하고 시작 프로젝트로 설정을 선택한 다음, 앱을 실행합니다. 그렇지 않으면 다음 .NET CLI 명령을 사용하여 앱을 실행할 수 있습니다.

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

자세한 내용은 dotnet 실행을 참조하세요. 앱이 실행되면 앱의 기능을 설명하는 방문 페이지가 표시됩니다. 오른쪽 위 모서리에 로그인 단추가 표시됩니다. 계정에 등록하거나 계정이 이미 있는 경우 로그인할 수 있습니다. 로그인하면 탐색할 수 있으며 해당 기능을 자유롭게 테스트할 수 있습니다. 로컬로 실행할 때 앱의 모든 기능은 메모리 내 지속성, 로컬 클러스터링에 의존하며 Bogus NuGet 패키지를 사용하여 모조 제품을 생성합니다. Visual Studio에서 디버깅 중지 옵션을 선택하거나 .NET CLI에서 Ctrl+C를 눌러 앱을 중지합니다.

AAD B2C

인증 개념을 교육하는 것은 이 자습서의 범위를 벗어나지만 Azure Active Directory B2C 테넌트를 만드는 방법을 알아본 다음, 웹앱을 등록하여 사용할 수 있습니다. 이 쇼핑 카트 예제 앱의 경우 배포된 Container Apps의 URL을 B2C 테넌트에 등록해야 합니다. 자세한 내용은 ASP.NET Core Blazor 인증 및 권한 부여를 참조하세요.

중요

컨테이너 앱을 배포한 후에는 B2C 테넌트에 앱의 URL을 등록해야 합니다. 대부분의 프로덕션 시나리오에서는 변경되지 않아야 하므로 앱의 URL을 한 번만 등록하면 됩니다.

Azure Container Apps 환경 내에서 앱이 격리되는 방식을 시각화하려면 다음 다이어그램을 참조하세요.

Azure Container Apps HTTP 수신.

앞의 다이어그램에서 앱에 대한 모든 인바운드 트래픽은 보안 HTTP 수신을 통해 유입됩니다. Azure Container Apps 환경에는 앱 instance 포함되며, 앱 instance Blazor Server 및 Orleans 앱 기능을 노출하는 ASP.NET Core 호스트가 포함되어 있습니다.

Azure Container Apps에 배포

Azure Container Apps에 앱을 배포하기 위해 리포지토리는 GitHub Actions를 사용합니다. 이 배포를 수행하려면 몇 가지 Azure 리소스가 필요하며 GitHub 리포지토리를 올바르게 구성해야 합니다.

앱을 배포하기 전에 Azure 리소스 그룹을 만들어야 합니다(또는 기존 리소스 그룹을 사용하도록 선택할 수 있음). 새 Azure 리소스 그룹을 만들려면 다음 문서 중 하나를 사용합니다.

선택한 리소스 그룹 이름을 기록해 둡니다. 나중에 앱을 배포하는 데 필요합니다.

서비스 주체 만들기

앱 배포를 자동화하려면 서비스 주체를 만들어야 합니다. 사용자를 대신하여 Azure 리소스를 관리할 수 있는 권한이 있는 Microsoft 계정입니다.

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

만든 JSON 자격 증명은 다음과 비슷하지만 클라이언트, 구독 및 테넌트에 대한 실제 값이 있습니다.

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

명령의 출력을 클립보드에 복사하고 다음 단계로 계속 진행합니다.

GitHub 비밀을 만듭니다.

GitHub는 암호화된 비밀을 만드는 메커니즘을 제공합니다. 만든 비밀은 GitHub Actions 워크플로에서 사용할 수 있습니다. GitHub Actions 사용하여 Azure Bicep와 함께 앱 배포를 자동화하는 방법을 살펴보겠습니다. Bicep는 선언적 구문을 사용하여 Azure 리소스를 배포하는 DSL(도메인 특정 언어)입니다. 자세한 내용은 Bicep이란?을 참조하세요. 서비스 주체 만들기 단계의 출력을 사용하여 JSON 형식 자격 증명으로 AZURE_CREDENTIALS라는 GitHub 비밀을 만들어야 합니다.

GitHub 리포지토리 내에서 설정>비밀>새 비밀 만들기를 선택합니다. AZURE_CREDENTIALS 이름을 입력하고 이전 단계의 JSON 자격 증명을 필드에 붙여넣습니다.

GitHub 리포지토리: 설정 > 비밀

자세한 내용은 GitHub: 암호화된 비밀을 참조하세요.

Azure 배포 준비

배포를 위해 앱을 패키징해야 합니다. Orleans.ShoppingCart.Silos 프로젝트에서는 Publish 단계 후에 실행되는 Target 요소를 정의합니다. 그러면 게시 디렉터리가 silo.zip 파일로 압축됩니다.

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

Azure Container Apps에 .NET 앱을 배포하는 방법에는 여러 가지가 있습니다. 이 자습서에서는 GitHub Actions, Azure Bicep, .NET 및 Azure CLI를 사용합니다. GitHub 리포지토리의 루트에 있는 ./github/workflows/deploy.yml 파일을 고려합니다.

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

이전 GitHub 워크플로는 다음과 같습니다.

  • dotnet publish 명령을 사용하여 쇼핑 카트 앱을 zip 파일로 게시합니다.
  • 서비스 주체 만들기 단계의 자격 증명을 사용하여 Azure에 로그인합니다.
  • acr.bicep 파일을 평가하고 az deployment group create를 사용하여 배포 그룹을 시작합니다.
  • 배포 그룹에서 ACR(Azure Container Registry) 로그인 서버를 가져옵니다.
  • 리포지토리 AZURE_CREDENTIALS 비밀을 사용하여 ACR에 로그인합니다.
  • 사일로 이미지를 빌드하고 ACR에 게시합니다.
  • main.bicep 파일을 평가하고 az deployment group create를 사용하여 배포 그룹을 시작합니다.
  • 사일로 배포
  • Azure에서 로그아웃합니다.

워크플로는 기본 분기로 푸시하여 트리거됩니다. 자세한 내용은 GitHub Actions 및 .NET을 참조하세요.

워크플로를 실행할 때 문제가 발생하는 경우 서비스 주체에 필요한 모든 공급자 네임스페이스가 등록되어 있는지 확인해야 할 수 있습니다. 다음 공급자 네임스페이스가 필요합니다.

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

자세한 내용은 리소스 공급자 등록 오류 해결을 참조하세요.

Azure는 리소스에 대한 명명 제한 및 규칙을 적용합니다. 다음에 대한 deploy.yml 파일 값을 업데이트해야 합니다.

  • UNIQUE_APP_NAME
  • SILO_IMAGE_NAME
  • AZURE_RESOURCE_GROUP_NAME
  • AZURE_RESOURCE_GROUP_LOCATION

이러한 값을 고유한 앱 이름과 Azure 리소스 그룹 이름 및 위치로 설정합니다.

자세한 내용은 Azure 리소스에 대한 명명 규칙 및 제한 사항을 참조하세요.

Bicep 템플릿 살펴보기

az deployment group create 명령을 실행하면 지정된 .bicep 파일 참조를 평가합니다. 이 파일에는 배포하려는 Azure 리소스를 자세히 설명하는 선언적 정보가 포함되어 있습니다. 이 단계를 생각하는 한 가지 방법은 배포를 위해 모든 리소스를 프로비전하는 것입니다.

중요

Visual Studio Code를 사용하는 경우 Bicep 확장을 사용할 때 Bicep 작성 환경이 향상됩니다.

평가되는 첫 번째 Bicep 파일은 acr.bicep 파일입니다. 이 파일에는 ACR(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

이 bicep 파일은 ACR 로그인 서버와 해당 이름을 출력합니다. 발견된 다음 Bicep 파일에는 단 하나 이상의 resource가 포함되어 있습니다. 주로 module 정의를 위임하는 것으로 구성된 main.bicep 파일을 고려합니다.

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

이전 Bicep 파일:

  • existing ACR 리소스를 참조합니다. 자세한 내용은 Azure Bicep: 기존 리소스를 참조하세요.
  • environment.bicep 정의 파일에 위임하는 module env를 정의합니다.
  • storage.bicep 정의 파일에 위임하는 module storageModule를 정의합니다.
  • 사일로 모듈에서 사용되는 여러 공유 envVars를 선언합니다.
  • container-app.bicep 정의 파일에 위임하는 module siloModule를 정의합니다.
  • ACA URL을 출력합니다(기존 AAD B2C 앱 등록의 리디렉션 URI를 업데이트하는 데 잠재적으로 사용될 수 있음).

main.bicep은 다른 여러 Bicep 파일에 위임합니다. 첫 번째는 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

이 bicep 파일은 Azure Log Analytics 및 Application Insights 리소스를 정의합니다. appInsights 리소스는 web 형식이고 logs 리소스는 PerGB2018 형식입니다. appInsights 리소스와 logs 리소스는 모두 리소스 그룹의 위치에 프로비전됩니다. appInsights 리소스는 WorkspaceResourceId 속성을 통해 logs 리소스에 연결됩니다. 이 bicep에는 나중에 Container Apps module에서 사용하는 세 가지 출력이 정의되어 있습니다. 다음으로, 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}'

위의 Bicep 파일은 다음을 정의합니다.

  • 리소스 그룹 이름과 앱 이름에 대한 두 개의 매개 변수입니다.
  • 스토리지 계정에 대한 resource storage 정의입니다.
  • 스토리지 계정에 대한 연결 문자열을 생성하는 단일 output입니다.

마지막 Bicep 파일은 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

앞서 언급한 Bicep용 Visual Studio Code 확장에는 시각화 도우미가 포함되어 있습니다. 이러한 모든 Bicep 파일은 다음과 같이 시각화됩니다.

Orleans: 쇼핑 카트 샘플 앱 Bicep 프로비저닝 시각화 도우미 렌더링.

요약

소스 코드를 업데이트하고 push가 리포지토리의 main 분기로 변경되면 deploy.yml 워크플로가 실행됩니다. Bicep 파일에 정의된 Azure 리소스를 프로비전하고 애플리케이션을 배포합니다. 수정 버전은 Azure Container Registry 자동으로 등록됩니다.

Bicep 확장의 시각화 도우미 외에도 Azure Portal 리소스 그룹 페이지는 애플리케이션을 프로비전하고 배포한 후 다음 예제와 유사하게 표시됩니다.

Azure Portal: Orleans Azure Container Apps에 대한 쇼핑 카트 샘플 앱 리소스입니다.

추가 정보