Azure App Service에 Orleans 배포
이 자습서에서는 Azure App Service에 Orleans 쇼핑 카트 앱을 배포하는 방법을 알아봅니다. 이 자습서에서는 다음 기능을 지원하는 샘플 애플리케이션을 안내합니다.
쇼핑 카트: 플랫폼 간 프레임워크 지원 및 확장 가능한 분산 애플리케이션 기능을 위해 Orleans를 사용하는 간단한 쇼핑 카트 애플리케이션입니다.
- 인벤토리 관리: 제품 인벤토리를 편집 및/또는 만듭니다.
- 상점 인벤토리: 구매 가능한 제품을 탐색하고 카트에 추가합니다.
- 카트: 카트의 모든 항목에 대한 요약을 보고 이러한 항목을 관리합니다. 각 항목의 수량을 제거하거나 변경합니다.
앱 및 해당 기능을 이해한 후 GitHub Actions, .NET 및 Azure CLI, Azure Bicep을 사용하여 Azure App Service에 앱을 배포하는 방법을 알아봅니다. 또한 Azure 내에서 앱에 대한 가상 네트워크를 구성하는 방법에 대해 알아봅니다.
이 자습서에서는 다음 작업 방법을 알아봅니다.
- Azure App Service에 Orleans 애플리케이션 배포
- GitHub Actions 및 Azure Bicep을 사용하여 배포 자동화
- Azure 내에서 앱에 대한 가상 네트워크 구성
사전 요구 사항
- GitHub 계정
- Orleans 소개 읽기
- .NET 7 SDK
- Azure CLI
- .NET IDE(통합 개발 환경)
- Visual Studio 또는 Visual Studio Code를 자유롭게 사용하세요.
로컬에서 앱 실행하기
앱을 로컬로 실행하려면 Azure 샘플: Azure App Service의 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를 눌러 앱을 중지합니다.
쇼핑 카트 앱 내부
Orleans는 분산 애플리케이션을 빌드하기 위한 안정적이고 확장 가능한 프레임워크입니다. 이 자습서에서는 Orleans를 사용하여 빌드된 간단한 쇼핑 카트 앱을 Azure App Service에 배포합니다. 앱은 인벤토리를 관리하고, 카트에 항목을 추가 및 제거하고, 사용 가능한 제품을 쇼핑하는 기능을 제공합니다. 클라이언트는 서버 호스팅 모델에서 Blazor를 사용하여 빌드됩니다. 앱은 다음과 같이 설계됩니다.
위의 다이어그램은 클라이언트가 서버 쪽 Blazor 앱임을 보여줍니다. 해당 Orleans 조직을 사용하는 여러 서비스로 구성됩니다. 각 서비스는 다음과 같이 Orleans 조직과 쌍을 이룹니다.
InventoryService
: 인벤토리가 제품 범주별로 분할된IInventoryGrain
을 사용합니다.ProductService
: 단일 제품이IProductGrain
에 의해 단일 조직 인스턴스에 테더링되는Id
를 사용합니다.ShoppingCartService
: 클라이언트 사용에 관계없이 단일 사용자에게 하나의 쇼핑 카트 인스턴스만 있는IShoppingCartGrain
을 사용합니다.
솔루션에는 3개의 프로젝트가 포함되어 있습니다.
Orleans.ShoppingCart.Abstractions
: 앱의 모델 및 인터페이스를 정의하는 클래스 라이브러리입니다.Orleans.ShoppingCart.Grains
: 앱의 비즈니스 논리를 구현하는 조직을 정의하는 클래스 라이브러리입니다.Orleans.ShoppingCart.Silos
: Orleans 사일로를 호스트하는 서버 쪽 Blazor 앱입니다.
클라이언트 사용자 환경
쇼핑 카트 클라이언트 앱에는 각각 다른 사용자 환경을 나타내는 여러 페이지가 있습니다. 앱의 UI는 MudBlazor NuGet 패키지를 사용하여 빌드됩니다.
홈페이지
사용자가 앱의 용도를 이해하고 각 탐색 메뉴 항목에 컨텍스트를 추가할 수 있는 몇 가지 간단한 구입니다.
상점 인벤토리 페이지
구매 가능한 모든 제품을 표시하는 페이지입니다. 이 페이지에서 카트에 항목을 추가할 수 있습니다.
빈 카트 페이지
카트에 아무것도 추가하지 않은 경우 페이지에서 카트에 항목이 없음을 나타내는 메시지를 렌더링합니다.
상점 인벤토리 페이지에 있는 동안 카트에 추가된 항목
상점 인벤토리 페이지에 있는 동안 카트에 항목을 추가하면 항목이 카트에 추가되었음을 나타내는 메시지가 앱에 표시됩니다.
제품 관리 페이지
사용자는 이 페이지에서 인벤토리를 관리할 수 있습니다. 제품을 인벤토리에서 추가, 편집 및 제거할 수 있습니다.
제품 관리 페이지 만들기 새 대화 상자
사용자가 새 제품 만들기 단추를 클릭하면 앱에 사용자가 새 제품을 만들 수 있는 대화 상자가 표시됩니다.
카트 페이지의 항목
카트에 항목이 있으면 항목을 확인하고, 수량을 변경하고, 카트에서 제거할 수도 있습니다. 사용자에게 카트에 있는 항목 요약과 세전 총 비용이 표시됩니다.
중요
이 앱이 개발 환경에서 로컬로 실행되면 앱은 localhost 클러스터링, 메모리 내 스토리지 및 로컬 사일로를 사용합니다. 또한 Bogus NuGet 패키지를 사용하여 자동으로 생성되는 모조 데이터로 인벤토리를 시드합니다. 이는 모두 기능을 보여주기 위한 의도적인 것입니다.
Azure App Service에 배포
일반적인 Orleans 애플리케이션은 조직이 상주하는 서버 프로세스(사일로)의 클러스터와 외부 요청을 수신하고, 조직 메서드 호출로 전환하여 결과를 반환하는 클라이언트 프로세스 세트(일반적으로 웹 서버)로 구성됩니다. 따라서 Orleans 애플리케이션을 실행하기 위해 가장 먼저 해야 할 일은 사일로 클러스터를 시작하는 것입니다. 테스트를 위해 클러스터는 단일 사일로로 구성될 수 있습니다.
참고
신뢰할 수 있는 프로덕션 배포의 경우 내결함성 및 크기 조정을 위해 클러스터에 둘 이상의 사일로가 필요합니다.
앱을 배포하기 전에 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: 암호화된 비밀을 참조하세요.
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 App Service에 .NET 앱을 배포하는 방법에는 여러 가지가 있습니다. 이 자습서에서는 GitHub Actions, Azure Bicep, .NET 및 Azure CLI를 사용합니다. GitHub 리포지토리의 루트에 있는 ./github/workflows/deploy.yml 파일을 고려합니다.
name: Deploy to Azure App Service
on:
push:
branches:
- main
env:
UNIQUE_APP_NAME: cartify
AZURE_RESOURCE_GROUP_NAME: orleans-resourcegroup
AZURE_RESOURCE_GROUP_LOCATION: centralus
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET 7.0
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.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 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 }} \
--debug
- name: Webapp deploy
run: |
az webapp deploy --name ${{ env.UNIQUE_APP_NAME }} \
--resource-group ${{ env.AZURE_RESOURCE_GROUP_NAME }} \
--clean true --restart true \
--type zip --src-path silo.zip --debug
- name: Staging deploy
run: |
az webapp deploy --name ${{ env.UNIQUE_APP_NAME }} \
--slot ${{ env.UNIQUE_APP_NAME }}stg \
--resource-group ${{ env.AZURE_RESOURCE_GROUP_NAME }} \
--clean true --restart true \
--type zip --src-path silo.zip --debug
이전 GitHub 워크플로는 다음과 같습니다.
- dotnet publish 명령을 사용하여 쇼핑 카트 앱을 zip 파일로 게시합니다.
- 서비스 주체 만들기 단계의 자격 증명을 사용하여 Azure에 로그인합니다.
- main.bicep 파일을 평가하고 az deployment group create를 사용하여 배포 그룹을 시작합니다.
- az webapp deploy를 사용하여 Azure App Service에 silo.zip 파일을 배포합니다.
- 스테이징에 대한 추가 배포도 구성됩니다.
워크플로는 기본 분기로 푸시하여 트리거됩니다. 자세한 내용은 GitHub Actions 및 .NET을 참조하세요.
팁
워크플로를 실행할 때 문제가 발생하는 경우 서비스 주체에 필요한 모든 공급자 네임스페이스가 등록되어 있는지 확인해야 할 수 있습니다. 다음 공급자 네임스페이스가 필요합니다.
Microsoft.Web
Microsoft.Network
Microsoft.OperationalInsights
Microsoft.Insights
Microsoft.Storage
자세한 내용은 리소스 공급자 등록 오류 해결을 참조하세요.
Azure는 리소스에 대한 명명 제한 및 규칙을 적용합니다. 다음에 대한 deploy.yml 파일 값을 업데이트해야 합니다.
UNIQUE_APP_NAME
AZURE_RESOURCE_GROUP_NAME
AZURE_RESOURCE_GROUP_LOCATION
이러한 값을 고유한 앱 이름과 Azure 리소스 그룹 이름 및 위치로 설정합니다.
자세한 내용은 Azure 리소스에 대한 명명 규칙 및 제한 사항을 참조하세요.
Bicep 템플릿 살펴보기
az deployment group create
명령이 실행되면 main.bicep 파일을 평가합니다. 이 파일에는 배포하려는 Azure 리소스가 포함되어 있습니다. 이 단계를 생각하는 한 가지 방법은 배포를 위해 모든 리소스를 프로비전하는 것입니다.
중요
Visual Studio Code를 사용하는 경우 Bicep 확장을 사용할 때 bicep 작성 환경이 향상됩니다.
리소스 또는 모듈(리소스 컬렉션)을 포함하는 여러 bicep 파일이 있습니다. main.bicep 파일은 진입점이며 주로 module
정의로 구성됩니다.
param appName string
param location string = resourceGroup().location
module storageModule 'storage.bicep' = {
name: 'orleansStorageModule'
params: {
name: '${appName}storage'
location: location
}
}
module logsModule 'logs-and-insights.bicep' = {
name: 'orleansLogModule'
params: {
operationalInsightsName: '${appName}-logs'
appInsightsName: '${appName}-insights'
location: location
}
}
resource vnet 'Microsoft.Network/virtualNetworks@2021-05-01' = {
name: '${appName}-vnet'
location: location
properties: {
addressSpace: {
addressPrefixes: [
'172.17.0.0/16',
'192.168.0.0/16'
]
}
subnets: [
{
name: 'default'
properties: {
addressPrefix: '172.17.0.0/24'
delegations: [
{
name: 'delegation'
properties: {
serviceName: 'Microsoft.Web/serverFarms'
}
}
]
}
}
{
name: 'staging'
properties: {
addressPrefix: '192.168.0.0/24'
delegations: [
{
name: 'delegation'
properties: {
serviceName: 'Microsoft.Web/serverFarms'
}
}
]
}
}
]
}
}
module siloModule 'app-service.bicep' = {
name: 'orleansSiloModule'
params: {
appName: appName
location: location
vnetSubnetId: vnet.properties.subnets[0].id
stagingSubnetId: vnet.properties.subnets[1].id
appInsightsConnectionString: logsModule.outputs.appInsightsConnectionString
appInsightsInstrumentationKey: logsModule.outputs.appInsightsInstrumentationKey
storageConnectionString: storageModule.outputs.connectionString
}
}
위의 bicep 파일은 다음을 정의합니다.
- 리소스 그룹 이름과 앱 이름에 대한 두 개의 매개 변수입니다.
- 스토리지 계정을 정의하는
storageModule
정의입니다. - Azure Log Analytics 및 Application Insights 리소스를 정의하는
logsModule
정의입니다. - 가상 네트워크를 정의하는
vnet
리소스입니다. - Azure App Service 정의하는
siloModule
정의입니다.
한 가지 매우 중요한 resource
는 Virtual Network에 대한 것입니다. vnet
리소스를 사용하면 Azure App Service가 Orleans 클러스터와 통신할 수 있습니다.
bicep 파일에서 module
이 발견될 때마다 리소스 정의가 포함된 다른 bicep 파일을 통해 평가됩니다. 처음 발견된 모듈은 storageModule
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 파일은 param
키워드를 사용하여 선언된 매개 변수를 허용합니다. 마찬가지로 output
키워드를 사용하여 출력을 선언할 수도 있습니다. 스토리지 resource
는 Microsoft.Storage/storageAccounts@2021-08-01
형식 및 버전에 따라 다릅니다. 리소스 그룹의 위치에 StorageV2
및 Standard_LRS
SKU로 프로비전됩니다. 스토리지 bicep은 연결 문자열을 output
으로 정의합니다. 이 connectionString
은 나중에 사일로 bicep에서 스토리지 계정에 연결하는 데 사용됩니다.
다음으로, logs-and-insights.bicep 파일은 Azure Log Analytics 및 Application Insights 리소스를 정의합니다.
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'
}
}
}
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에는 두 개의 출력이 정의되어 있으며 나중에 App Service module
에서 사용됩니다.
마지막으로 app-service.bicep 파일은 Azure App Service 리소스를 정의합니다.
param appName string
param location string
param vnetSubnetId string
param stagingSubnetId string
param appInsightsInstrumentationKey string
param appInsightsConnectionString string
param storageConnectionString string
resource appServicePlan 'Microsoft.Web/serverfarms@2021-03-01' = {
name: '${appName}-plan'
location: location
kind: 'app'
sku: {
name: 'S1'
capacity: 1
}
}
resource appService 'Microsoft.Web/sites@2021-03-01' = {
name: appName
location: location
kind: 'app'
properties: {
serverFarmId: appServicePlan.id
virtualNetworkSubnetId: vnetSubnetId
httpsOnly: true
siteConfig: {
vnetPrivatePortsCount: 2
webSocketsEnabled: true
netFrameworkVersion: 'v6.0'
appSettings: [
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: appInsightsInstrumentationKey
}
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: appInsightsConnectionString
}
{
name: 'ORLEANS_AZURE_STORAGE_CONNECTION_STRING'
value: storageConnectionString
}
{
name: 'ORLEANS_CLUSTER_ID'
value: 'Default'
}
]
alwaysOn: true
}
}
}
resource stagingSlot 'Microsoft.Web/sites/slots@2022-03-01' = {
name: '${appName}stg'
location: location
properties: {
serverFarmId: appServicePlan.id
virtualNetworkSubnetId: stagingSubnetId
siteConfig: {
http20Enabled: true
vnetPrivatePortsCount: 2
webSocketsEnabled: true
netFrameworkVersion: 'v7.0'
appSettings: [
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: appInsightsInstrumentationKey
}
{
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
value: appInsightsConnectionString
}
{
name: 'ORLEANS_AZURE_STORAGE_CONNECTION_STRING'
value: storageConnectionString
}
{
name: 'ORLEANS_CLUSTER_ID'
value: 'Staging'
}
]
alwaysOn: true
}
}
}
resource slotConfig 'Microsoft.Web/sites/config@2021-03-01' = {
name: 'slotConfigNames'
parent: appService
properties: {
appSettingNames: [
'ORLEANS_CLUSTER_ID'
]
}
}
resource appServiceConfig 'Microsoft.Web/sites/config@2021-03-01' = {
parent: appService
name: 'metadata'
properties: {
CURRENT_STACK: 'dotnet'
}
}
이 bicep 파일은 Azure App Service .NET 7 애플리케이션으로 구성합니다. appServicePlan
리소스와 appService
리소스는 모두 리소스 그룹의 위치에 프로비전됩니다. appService
리소스는 1
용량이 있는 S1
SKU를 사용하도록 구성됩니다. 또한 리소스는 vnetSubnetId
서브넷을 사용하고 HTTPS를 사용하도록 구성됩니다. 또한 appInsightsInstrumentationKey
계측 키, appInsightsConnectionString
연결 문자열 및 storageConnectionString
연결 문자열을 구성합니다. 쇼핑 카트 앱에서 사용됩니다.
앞서 언급한 Bicep용 Visual Studio Code 확장에는 시각화 도우미가 포함되어 있습니다. 이러한 모든 bicep 파일은 다음과 같이 시각화됩니다.
스테이징 환경
배포 인프라는 단기, 테스트 중심 및 변경할 수 없는 버려진 환경인 스테이징 환경에 배포할 수 있습니다. 이러한 환경은 배포를 프로덕션으로 승격하기 전에 테스트하는 데 매우 유용합니다.
참고
App Service Windows에서 실행되는 경우 각 App Service 별도의 App Service 플랜에 있어야 합니다. 또는 이러한 구성을 방지하려면 대신 App Service on Linux 사용할 수 있으며 이 문제가 해결됩니다.
요약
소스 코드를 업데이트하고 push
가 리포지토리의 main
분기로 변경되면 deploy.yml 워크플로가 실행됩니다. bicep 파일에 정의된 리소스를 제공하고 애플리케이션을 배포합니다. 애플리케이션을 확장하여 인증과 같은 새로운 기능을 포함하거나 애플리케이션의 여러 인스턴스를 지원할 수 있습니다. 이 워크플로의 주요 목적은 한 단계로 리소스를 프로비전하고 배포하는 기능을 보여 주는 것입니다.
bicep 확장의 시각화 도우미 외에도 Azure Portal 리소스 그룹 페이지는 애플리케이션을 프로비전하고 배포한 후 다음 예제와 유사하게 표시됩니다.
참고 항목
.NET