다음을 통해 공유


.NET Aspire 프로젝트를 Azure Container Apps에 배포하는 Azure Developer CLI 사용법 (심층 가이드)

Azure Developer CLI(azd)은 .NET.NET Aspire 프로젝트 배포를 지원하도록 확장되었습니다. 이 가이드를 사용하여 .NET Aspire 프로젝트를 Azure Container Apps를 사용해 Azure Developer CLI에 만들고 배포하는 과정을 안내합니다. 이 자습서에서는 다음 개념을 알아봅니다.

  • azd .NET 프로젝트에서 .NET Aspire 통합이 작동하는 방식 살펴보기
  • Azure를 사용하여 .NET Aspire 프로젝트에 필요한 azd 리소스를 제공하고 배포합니다.
  • azd 사용하여 Bicep 인프라 및 기타 템플릿 파일을 생성하세요.

필수 구성 요소

.NET .NET Aspire작업하려면 다음을 로컬에 설치해야 합니다.

자세한 내용은 .NET.NET Aspire 설정 및 도구.NET.NET Aspire SDK참조하세요.

또한 로컬에서 Azure Developer CLI설치해야 합니다. 일반적인 설치 옵션은 다음과 같습니다.

winget install microsoft.azd

Azure Developer CLI 통합 작동 방식

azd init 워크플로는 .NET.NET Aspire 프로젝트에 대한 사용자 지정 지원을 제공합니다. 다음 다이어그램에서는 이 흐름이 개념적으로 작동하는 방식과 azd 및 .NET.NET Aspire 통합되는 방법을 보여 줍니다.

.NET.NET Aspire 프로젝트를 배포할 때 'azd'의 내부 처리를 설명합니다.

  1. azd이 .NET.NET Aspire 프로젝트를 대상으로 지정할 때, 특수 명령(dotnet run --project AppHost.csproj --output-path manifest.json --publisher manifest)을 사용하여 AppHost를 시작하고 Aspire매니페스트 파일을 생성합니다.
  2. 매니페스트 파일은 azd provision 하위 명령 논리에 의해 심문되어 메모리 내 Bicep 파일만 생성합니다(기본적으로).
  3. Bicep 파일을 생성한 후 이전에 제공된 구독 및 리소스 그룹을 대상으로 하는 AzureARM API를 사용하여 배포가 트리거됩니다.
  4. 기본 Azure 리소스가 구성되면 동일한 azd deploy 매니페스트 파일을 사용하는 Aspire 하위 명령 논리가 실행됩니다.
  5. 배포 azddotnet publish의 기본 컨테이너 게시 지원을 사용하여 컨테이너 이미지를 생성하기 위해 .NET을 호출합니다.
  6. azd 컨테이너 이미지를 빌드하면 프로비저닝 단계에서 만든 ACR 레지스트리로 푸시합니다.
  7. 마지막으로 컨테이너 이미지가 ACR에 있으면 azd ARM을 사용하여 리소스를 업데이트하여 새 버전의 컨테이너 이미지 사용을 시작합니다.

메모

또한 생성된 Bicep을 프로젝트의 폴더로 출력할 수 있습니다. 이 폴더는 앱 모델 섹션에서 Bicep 생성 섹션에서 자세히 확인할 수 있습니다.

.NET .NET Aspire 시작 앱을 설정하고 배포합니다.

이 섹션의 단계에서는 .NET Aspire 시작 앱을 만들고 Azure사용하여 azd 앱 리소스 프로비저닝 및 배포를 처리하는 방법을 보여 줍니다.

.NET .NET Aspire 시작 앱 만들기

.NET 명령을 사용하여 새 .NET Aspiredotnet new 프로젝트를 만듭니다. Visual Studio사용하여 프로젝트를 만들 수도 있습니다.

dotnet new aspire-starter --use-redis-cache -o AspireSample
cd AspireSample
dotnet run --project AspireSample.AppHost\AspireSample.AppHost.csproj

이전 명령은 .NET 캐시에 대한 종속성을 포함하는 .NET Aspire 템플릿을 기반으로 새 aspire-starterRedis 프로젝트를 만듭니다. 모든 것이 올바르게 작동하는지 확인하는 .NET.NET Aspire 프로젝트를 실행합니다.

템플릿 초기화

  1. 새 터미널 창을 열고 cd 명령어를 사용하여 .NET 솔루션의 .NET Aspire 프로젝트 디렉터리로 이동하십시오.

  2. azd init 명령을 실행하여 로컬 디렉터리 구조를 검사하고 앱 유형을 결정하는 azd사용하여 프로젝트를 초기화합니다.

    azd init
    

    azd init 명령에 대한 자세한 내용은 azd init참조하세요.

  3. 두 개의 앱 초기화 옵션을 묻는 메시지가 표시되면 현재 디렉터리에서 코드를 사용하여 azd 선택합니다.

    ? How do you want to initialize your app?  [Use arrows to move, type to filter]
    > Use code in the current directory
      Select a template
    
  4. 디렉터리를 검사한 후 azd 올바른 .NET.NET AspireAppHost 프로젝트를 찾았는지 확인하라는 메시지를 표시합니다. 확인을 선택하고 앱 옵션을 계속 초기화합니다.

    Detected services:
    
      .NET (Aspire)
      Detected in: D:\source\repos\AspireSample\AspireSample.AppHost\AspireSample.AppHost.csproj
    
    azd will generate the files necessary to host your app on Azure using Azure Container Apps.
    
    ? Select an option  [Use arrows to move, type to filter]
    > Confirm and continue initializing my app
      Cancel and exit
    
  5. 환경 이름을 입력합니다. 이 이름은 Azure에서 프로비전된 리소스의 이름 지정과 devprod같은 다양한 환경 관리에 사용됩니다.

    Generating files to run your app on Azure:
    
      (✓) Done: Generating ./azure.yaml
      (✓) Done: Generating ./next-steps.md
    
    SUCCESS: Your app is ready for the cloud!
    You can provision and deploy your app to Azure by running the azd up command in this directory. For more information on configuring your app, see ./next-steps.md
    

azd 여러 파일을 생성하고 작업 디렉터리에 배치합니다. 이러한 파일은 다음과 같습니다.

  • .yaml: AppHost 프로젝트 과 같은 앱의 서비스를 설명하고 이를 리소스에 매핑합니다.
  • .azure/config.json: 현재 활성 환경을 azd에 알리는 구성 파일입니다.
  • .azure/aspireazddev/.env: 환경별 재정의를 포함합니다.

azure.yaml 파일에는 다음과 같은 내용이 있습니다.

# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json

name: AspireSample
services:
  app:
    language: dotnet
    project: .\AspireSample.AppHost\AspireSample.AppHost.csproj
    host: containerapp

리소스 이름 지정

새 Azure 리소스를 만들 때는 명명 요구 사항을 따르는 것이 중요합니다. Azure Container Apps경우 이름은 2~32자여야 하며 소문자, 숫자 및 하이픈으로 구성됩니다. 이름은 문자로 시작하고 영숫자로 끝나야 합니다.

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

초기 배포

  1. .NET Aspire 프로젝트를 배포하려면 Azure AD에 인증하여 Azure 리소스 관리 API를 호출합니다.

    azd auth login
    

    이전 명령은 명령줄 세션을 인증하는 브라우저를 시작합니다.

  2. 인증되면 AppHost 프로젝트 디렉터리에서 다음 명령을 실행하여 애플리케이션을 프로비전하고 배포합니다.

    azd up
    

    중요하다

    컨테이너 이미지를 Azure ACR(Container Registry)에 푸시하려면 Microsoft.Authorization/roleAssignments/write 액세스 권한이 있어야 합니다. 이 작업은 레지스트리에서 관리자 사용자 사용하도록 설정하여 수행할 수 있습니다. Azure 포털을 열고 ACR 리소스/설정/액세스 키로 이동한 다음 관리 사용자 확인란을 선택합니다. 자세한 내용은 관리 사용자사용하도록 설정하세요.

  3. 메시지가 표시되면 리소스를 배포할 구독 및 위치를 선택합니다. 이러한 옵션을 선택하면 .NET.NET Aspire 프로젝트가 배포됩니다.

    By default, a service can only be reached from inside the Azure Container Apps environment it is running in. Selecting a service here will also allow it to be reached from the Internet.
    ? Select which services to expose to the Internet webfrontend
    ? Select an Azure Subscription to use:  1. <YOUR SUBSCRIPTION>
    ? Select an Azure location to use: 1. <YOUR LOCATION>
    
    Packaging services (azd package)
    
    
    Provisioning Azure resources (azd provision)
    Provisioning Azure resources can take some time.
    
    Subscription: <YOUR SUBSCRIPTION>
    Location: <YOUR LOCATION>
    
      You can view detailed progress in the Azure Portal:
      <LINK TO DEPLOYMENT>
    
      (✓) Done: Resource group: <YOUR RESOURCE GROUP>
      (✓) Done: Container Registry: <ID>
      (✓) Done: Log Analytics workspace: <ID>
      (✓) Done: Container Apps Environment: <ID>
    
    SUCCESS: Your application was provisioned in Azure in 1 minute 13 seconds.
    You can view the resources created under the resource group <YOUR RESOURCE GROUP> in Azure Portal:
    <LINK TO RESOURCE GROUP OVERVIEW>
    
    Deploying services (azd deploy)
    
      (✓) Done: Deploying service apiservice
      - Endpoint: <YOUR UNIQUE apiservice APP>.azurecontainerapps.io/
    
      (✓) Done: Deploying service webfrontend
      - Endpoint: <YOUR UNIQUE webfrontend APP>.azurecontainerapps.io/
    
    Aspire Dashboard: <LINK TO DEPLOYED .NET ASPIRE DASHBOARD>
    
    SUCCESS: Your up workflow to provision and deploy to Azure completed in 3 minutes 50 seconds.
    

    azd 명령의 최종 출력 줄은 배포된 모든 Azure 리소스를 표시하는 Azure Portal에 대한 링크입니다.

    배포된 리소스를 보여 주는 Azure Portal의 스크린샷

이 애플리케이션 내에 세 개의 컨테이너가 배포됩니다.

  • webfrontend: 시작 템플릿의 웹 프로젝트의 코드를 포함합니다.
  • apiservice: API 서비스 프로젝트의 코드를 시작 템플릿에 포함합니다.
  • cache: 프런트 엔드에 캐시를 제공하는 Redis 컨테이너 이미지입니다.

로컬 개발과 마찬가지로 연결 문자열의 구성이 자동으로 처리됩니다. 이 경우 azd 애플리케이션 모델을 해석하고 적절한 배포 단계로 변환하는 작업을 담당했습니다. 예를 들어, webfrontend 컨테이너에 삽입되어 Redis 캐시 및 apiservice에 연결하는 방법을 알려주는 연결 문자열과 서비스 검색 변수를 고려하세요.

webfrontend 컨테이너 앱의 환경 변수 스크린샷입니다.

.NET .NET Aspire 프로젝트에서 연결 문자열 및 서비스 검색을 처리하는 방법에 대한 자세한 내용은 .NET.NET Aspire 오케스트레이션 개요참조하세요.

애플리케이션 업데이트 배포

명령이 실행되면 기본 리소스가 프로비전된 컨테이너 이미지가 빌드되고 프로젝트를 호스트하는 컨테이너 앱에 배포됩니다. 일반적으로 개발이 진행 중이고 Azure 리소스가 배포되면 코드가 업데이트될 때마다 Azure 리소스를 프로비전할 필요가 없습니다. 이는 개발자 내부 루프에 특히 해당합니다.

코드 변경 배포 속도를 높이기 위해 azd 컨테이너 이미지에 코드 업데이트 배포를 지원합니다. 이 작업은 azd deploy 명령을 사용하여 수행됩니다.

azd deploy
Deploying services (azd deploy)

  (✓) Done: Deploying service apiservice
  - Endpoint: <YOUR UNIQUE apiservice APP>.azurecontainerapps.io/

  (✓) Done: Deploying service webfrontend
  - Endpoint: <YOUR UNIQUE webfrontend APP>.azurecontainerapps.io/

Aspire Dashboard: <LINK TO DEPLOYED .NET ASPIRE DASHBOARD>

매번 모든 서비스를 배포할 필요는 없습니다. azd .NET .NET Aspire 프로젝트 모델을 이해하므로 다음 명령을 사용하여 지정된 서비스 중 하나만 배포할 수 있습니다.

azd deploy webfrontend

자세한 내용은 Azure Developer CLI을 참조하세요. azd deploy.

인프라 업데이트 배포

.NET .NET Aspire 프로젝트 내의 종속성 구조가 변경되면 azd 기본 Azure 리소스를 다시 프로비전해야 합니다. azd provision 명령은 인프라에 이러한 변경 내용을 적용하는 데 사용됩니다.

이 작업이 작동하는지 확인하려면 AppHost 프로젝트의 Program.cs 파일을 다음으로 업데이트합니다.

var builder = DistributedApplication.CreateBuilder(args);

var cache = builder.AddRedis("cache");

// Add the locations database.
var locationsdb = builder.AddPostgres("db").AddDatabase("locations");

// Add the locations database reference to the API service.
var apiservice = builder.AddProject<Projects.AspireSample_ApiService>("apiservice")
    .WithReference(locationsdb);

builder.AddProject<Projects.AspireSample_Web>("webfrontend")
    .WithReference(cache)
    .WithReference(apiservice);

builder.Build().Run();

파일을 저장하고 다음 명령을 실행합니다.

azd provision

azd provision 명령은 Postgres 데이터베이스를 호스트하는 컨테이너 앱을 만들어 인프라를 업데이트합니다. azd provision 명령은 apiservice 컨테이너에 대한 연결 문자열을 업데이트하지 않았습니다. 새로 프로비전된 Postgres 데이터베이스를 가리키도록 연결 문자열을 업데이트하려면 azd deploy 명령을 다시 호출해야 합니다. 의심스러운 경우 azd up을 사용하여 프로비전과 배포에 활용하세요.

리소스 정리

이 워크스루 중에 만든 Azure 리소스를 정리해야 합니다. `azd`는 리소스를 생성한 리소스 그룹을 알고 있으므로, 다음 명령어를 사용하여 환경을 종료할 수 있습니다.

azd down

이전 명령을 실행하는 데 다소 시간이 걸릴 수 있지만, 완료되면 리소스 그룹과 모든 해당 리소스를 삭제해야 합니다.

Deleting all resources and deployed code on Azure (azd down)
Local application code is not deleted when running 'azd down'.

  Resource group(s) to be deleted:

    • <YOUR RESOURCE GROUP>: <LINK TO RESOURCE GROUP OVERVIEW>

? Total resources to delete: 7, are you sure you want to continue? Yes
Deleting your resources can take some time.

  (✓) Done: Deleting resource group: <YOUR RESOURCE GROUP>

SUCCESS: Your application was removed from Azure in 9 minutes 59 seconds.

.NET .NET Aspire 프로젝트 모델에서 Bicep 생성

개발 팀은 개발 및 프로덕션용으로 배포에 azd up(또는 azd provisionazd deploy) 명령을 자유롭게 사용할 수 있지만, 일부 팀은 버전 제어의 일부로 검토하고 관리할 수 있는 Bicep 파일을 생성하도록 선택할 수 있습니다(또한 이러한 Bicep 파일을 더 복잡한 Azure 배포의 일부로 참조할 수 있음).

azd에는 다음 명령을 통해 프로비전하는 데 사용하는 Bicep을 출력할 수 있는 기능이 포함되어 있습니다.

azd config set alpha.infraSynth on
azd infra synth

이 가이드에 사용된 시작 템플릿 예제에서 이 명령을 실행한 후 AppHost 프로젝트 디렉터리에 다음 파일이 만들어집니다.

  • infra/main.bicep: 배포의 주요 진입점을 나타냅니다.
  • infra/main.parameters를 .json: 주 Bicep의 매개 변수로 사용됩니다(.azure 폴더에 정의된 환경 변수에 매핑됨).
  • infra/resources.bicep: Azure 프로젝트 모델을 지원하기 위해 필요한 .NET Aspire 리소스를 정의합니다.
  • AspireSample.Web/manifests/containerApp.tmpl.yaml: webfrontend대한 컨테이너 앱 정의입니다.
  • AspireSample.ApiService/manifests/containerApp.tmpl.yaml: apiservice대한 컨테이너 앱 정의입니다.

인프라\resources.bicep 파일에는 컨테이너 앱 자체에 대한 정의가 포함되어 있지 않습니다(Redis 및 Postgres같은 종속성인 컨테이너 앱을 제외하고).

@description('The location used for all deployed resources')
param location string = resourceGroup().location

@description('Tags that will be applied to all resources')
param tags object = {}

var resourceToken = uniqueString(resourceGroup().id)

resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
  name: 'mi-${resourceToken}'
  location: location
  tags: tags
}

resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-07-01' = {
  name: replace('acr-${resourceToken}', '-', '')
  location: location
  sku: {
    name: 'Basic'
  }
  tags: tags
}

resource caeMiRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(containerRegistry.id, managedIdentity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d'))
  scope: containerRegistry
  properties: {
    principalId: managedIdentity.properties.principalId
    principalType: 'ServicePrincipal'
    roleDefinitionId:  subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')
  }
}

resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
  name: 'law-${resourceToken}'
  location: location
  properties: {
    sku: {
      name: 'PerGB2018'
    }
  }
  tags: tags
}

resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
  name: 'cae-${resourceToken}'
  location: location
  properties: {
    appLogsConfiguration: {
      destination: 'log-analytics'
      logAnalyticsConfiguration: {
        customerId: logAnalyticsWorkspace.properties.customerId
        sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey
      }
    }
  }
  tags: tags
}

resource cache 'Microsoft.App/containerApps@2023-05-02-preview' = {
  name: 'cache'
  location: location
  properties: {
    environmentId: containerAppEnvironment.id
    configuration: {
      service: {
        type: 'redis'
      }
    }
    template: {
      containers: [
        {
          image: 'redis'
          name: 'redis'
        }
      ]
    }
  }
  tags: union(tags, {'aspire-resource-name': 'cache'})
}

resource locations 'Microsoft.App/containerApps@2023-05-02-preview' = {
  name: 'locations'
  location: location
  properties: {
    environmentId: containerAppEnvironment.id
    configuration: {
      service: {
        type: 'postgres'
      }
    }
    template: {
      containers: [
        {
          image: 'postgres'
          name: 'postgres'
        }
      ]
    }
  }
  tags: union(tags, {'aspire-resource-name': 'locations'})
}
output MANAGED_IDENTITY_CLIENT_ID string = managedIdentity.properties.clientId
output AZURE_CONTAINER_REGISTRY_ENDPOINT string = containerRegistry.properties.loginServer
output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = managedIdentity.id
output AZURE_CONTAINER_APPS_ENVIRONMENT_ID string = containerAppEnvironment.id
output AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN string = containerAppEnvironment.properties.defaultDomain

Bicep을 사용하여 Azure에 배포를 자동화하는 것에 대한 자세한 내용은 Bicep이 무엇인지 확인해 보세요.

.NET 서비스 프로젝트의 컨테이너 앱 정의는 각 프로젝트의 디렉터리에 있는 manifests 파일에 포함되어 있습니다. 다음은 webfrontend 프로젝트의 예입니다.

location: {{ .Env.AZURE_LOCATION }}
identity:
  type: UserAssigned
  userAssignedIdentities:
    ? "{{ .Env.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID }}"
    : {}
properties:
  environmentId: {{ .Env.AZURE_CONTAINER_APPS_ENVIRONMENT_ID }}
  configuration:
    activeRevisionsMode: single
    ingress:
      external: true
      targetPort: 8080
      transport: http
      allowInsecure: false
    registries:
    - server: {{ .Env.AZURE_CONTAINER_REGISTRY_ENDPOINT }}
      identity: {{ .Env.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID }}
  template:
    containers:
    - image: {{ .Env.SERVICE_WEBFRONTEND_IMAGE_NAME }}
      name: webfrontend
      env:
      - name: AZURE_CLIENT_ID
        value: {{ .Env.MANAGED_IDENTITY_CLIENT_ID }}
      - name: ConnectionStrings__cache
        value: {{ connectionString "cache" }}
      - name: OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES
        value: "true"
      - name: OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES
        value: "true"
      - name: services__apiservice__0
        value: http://apiservice.internal.{{ .Env.AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN }}
      - name: services__apiservice__1
        value: https://apiservice.internal.{{ .Env.AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN }}
tags:
  azd-service-name: webfrontend
  aspire-resource-name: webfrontend

azd infra synth 명령을 실행한 후 azd provisionazd deploy 호출되면 Bicep을 사용하고 생성된 파일을 지원합니다.

중요하다

azd infra synth 다시 호출되는 경우 수정된 파일을 새로 생성된 파일로 바꾸고 확인을 요청하는 메시지를 표시합니다.

디버깅을 위한 격리된 환경

azd 새 환경을 쉽게 프로비전할 수 있으므로 각 팀 구성원이 프로덕션과 밀접하게 일치하는 설정에서 코드를 디버깅하기 위한 격리된 클라우드 호스팅 환경을 가질 수 있습니다. 이 작업을 수행할 때 각 팀 구성원은 다음 명령을 사용하여 고유한 환경을 만들어야 합니다.

azd env new

그러면 사용자에게 구독 및 리소스 그룹 정보를 다시 입력하라는 메시지가 표시되고 이후 azd up, azd provisionazd deploy 호출은 기본적으로 이 새 환경을 사용합니다. --environment 스위치를 이러한 명령에 적용하여 환경 간에 전환할 수 있습니다.

리소스 정리

만든 Azure 리소스가 더 이상 필요하지 않은 경우 다음 Azure CLI 명령을 실행하여 리소스 그룹을 삭제합니다. 리소스 그룹을 삭제하면 리소스 그룹에 포함된 리소스도 삭제됩니다.

az group delete --name <your-resource-group-name>

자세한 내용은 리소스 정리를 Azure에서 참조하세요.