자습서: Docker Compose를 사용하여 다중 컨테이너 앱 만들기

이 자습서에서는 Visual Studio에서 컨테이너 도구를 사용할 때 둘 이상의 컨테이너를 관리하고 컨테이너 간에 통신하는 방법을 알아봅니다. 여러 컨테이너를 관리하려면 컨테이너 오케스트레이션이 필요하며 Docker Compose 또는 Service Fabric과 같은 오케스트레이터가 필요합니다. 이러한 절차에서는 Docker Compose를 사용합니다. Docker Compose는 개발 주기 과정에서 로컬 디버깅 및 테스트에 유용합니다.

이 자습서에서 만든 완성된 샘플은 Docker/ComposeSample 폴더의 GitHub에서 https://github.com/MicrosoftDocs/vs-tutorial-samples 찾을 수 있습니다.

필수 조건

  • Docker Desktop
  • 웹 개발, Azure 도구 워크로드 및/또는 .NET 플랫폼 간 개발 워크로드가 설치된 Visual Studio 2022 이 설치에는 .NET 8 개발 도구가 포함되어 있습니다.

웹 애플리케이션 프로젝트 만들기

Visual Studio에서 WebFrontEnd라는 이름의 ASP.NET Core 웹앱 프로젝트를 만들어 Razor 페이지를 사용해 애플리케이션을 만듭니다.

Screenshot showing Create ASP.NET Core Web App project.

Docker 지원 사용을 선택하지 마세요. 프로세스의 뒷부분에서 Docker 지원을 추가합니다.

Screenshot of the Additional information screen when creating a web project. The option to Enable Docker Support is not selected.

참고 항목

Visual Studio 2022 17.2 및 이후 버전에서는 이 프로젝트에 Azure Functions을 대신 사용할 수 있습니다.

Screenshot showing Create ASP.NET Core Web App project.

Docker 지원 사용을 선택하지 마세요. 프로세스의 뒷부분에서 Docker 지원을 추가합니다.

Screenshot of the Additional information screen when creating a web project. The option to Enable Docker Support is not selected.

Web API 프로젝트 만들기

동일한 솔루션에 프로젝트를 추가하고 이름을 MyWebAPI로 지정합니다. 프로젝트 형식으로 API를 선택하고 HTTPS에 대한 구성 확인란의 선택을 취소합니다. 이 설계에서는 클라이언트와의 통신에만 SSL을 사용하고, 동일한 웹 애플리케이션의 컨테이너 간 통신에는 사용하지 않습니다. WebFrontEnd는 HTTPS만 필요하며 이 예제의 코드에서는 해당 확인란의 선택을 취소했다고 가정합니다. 일반적으로 Visual Studio에서 사용하는 .NET 개발자 인증서는 컨테이너-컨테이너 요청이 아닌 외부-컨테이너 요청에 대해서만 지원됩니다.

Screenshot of creating the Web API project.

  1. 동일한 솔루션에 프로젝트를 추가하고 이름을 WebAPI로 지정합니다. 프로젝트 형식으로 API를 선택하고 HTTPS에 대한 구성 확인란의 선택을 취소합니다. 이 설계에서는 클라이언트와의 통신에만 SSL을 사용하고, 동일한 웹 애플리케이션의 컨테이너 간 통신에는 사용하지 않습니다. WebFrontEnd는 HTTPS만 필요하며 이 예제의 코드에서는 해당 확인란의 선택을 취소했다고 가정합니다. 일반적으로 Visual Studio에서 사용하는 .NET 개발자 인증서는 컨테이너-컨테이너 요청이 아닌 외부-컨테이너 요청에 대해서만 지원됩니다.

    Screenshot of creating the Web API project.

  2. Redis Cache에 대한 지원을 추가합니다. NuGet 패키지 Microsoft.Extensions.Caching.StackExchangeRedis(StackExchange.Redis 아님)를 추가합니다. Program.cs에서 var app = builder.Build() 바로 앞에 다음 줄을 추가합니다.

    builder.Services.AddStackExchangeRedisCache(options =>
       {
          options.Configuration = "redis:6379"; // redis is the container name of the redis service. 6379 is the default port
          options.InstanceName = "SampleInstance";
       });
    
  3. Microsoft.Extensions.Caching.DistributedMicrosoft.Extensions.Caching.StackExchangeRedis에 대한 Program.cs에 Using 지시문을 추가합니다.

    using Microsoft.Extensions.Caching.Distributed;
    using Microsoft.Extensions.Caching.StackExchangeRedis;
    
  4. Web API 프로젝트에서 기존 WeatherForecast.csControllers/WeatherForecastController.cs를 삭제하고 다음 내용이 포함된 Controllers, CounterController.cs 아래에 파일을 추가합니다.

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Caching.Distributed;
    using StackExchange.Redis;
    
    namespace WebApi.Controllers
    {
        [ApiController]
        [Route("[controller]")]
        public class CounterController : ControllerBase
        {
            private readonly ILogger<CounterController> _logger;
            private readonly IDistributedCache _cache;
    
            public CounterController(ILogger<CounterController> logger, IDistributedCache cache)
            {
                _logger = logger;
                _cache = cache;
            }
    
            [HttpGet(Name = "GetCounter")]
            public string Get()
            {
                string key = "Counter";
                string? result = null;
                try
                {
                    var counterStr = _cache.GetString(key);
                    if (int.TryParse(counterStr, out int counter))
                    {
                        counter++;
                    }
                    else
                    {
                        counter = 0;
                    }
                    result = counter.ToString();
                    _cache.SetString(key, result);
                }
                catch(RedisConnectionException)
                {
                    result = "Redis cache is not found.";
                }
                return result;
            }
        }
    }
    

    이 서비스는 페이지에 액세스할 때마다 카운터를 증가시키고 카운터를 Redis 캐시에 저장합니다.

Web API를 호출하는 코드 추가

  1. WebFrontEnd 프로젝트에서 Index.cshtml.cs 파일을 열고, OnGet 메서드를 다음 코드로 바꿉니다.

     public async Task OnGet()
     {
        ViewData["Message"] = "Hello from webfrontend";
    
        using (var client = new System.Net.Http.HttpClient())
        {
           // Call *mywebapi*, and display its response in the page
           var request = new System.Net.Http.HttpRequestMessage();
           request.RequestUri = new Uri("http://mywebapi/WeatherForecast");
           // request.RequestUri = new Uri("http://mywebapi/api/values/1"); // For ASP.NET 2.x, comment out previous line and uncomment this line.
           var response = await client.SendAsync(request);
           ViewData["Message"] += " and " + await response.Content.ReadAsStringAsync();
        }
     }
    

    참고 항목

    실제 코드에서는 모든 요청 후에 HttpClient를 삭제하면 안 됩니다. 모범 사례는 HttpClientFactory를 사용하여 복원력 있는 HTTP 요청 구현을 참조하세요.

  2. Index.cshtml 파일에 ViewData["Message"]를 표시할 줄을 추가하여 파일이 다음 코드와 같이 표시되도록 합니다.

    @page
    @model IndexModel
    @{
       ViewData["Title"] = "Home page";
    }
    
    <div class="text-center">
       <h1 class="display-4">Welcome</h1>
       <p>Learn about <a href="/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
       <p>@ViewData["Message"]</p>
    </div>
    
  3. (ASP.NET 2.x만 해당) 이제 Web API 프로젝트의 값 컨트롤러에 코드를 추가하여 webfrontend에서 추가한 호출에 대해 API에서 반환되는 메시지를 사용자 지정합니다.

    // GET api/values/5
    [HttpGet("{id}")]
    public ActionResult<string> Get(int id)
    {
       return "webapi (with value " + id + ")";
    }
    

    참고 항목

    .NET Core 3.1 이상에서는 이 추가 코드 대신 제공된 WeatherForecast API를 사용할 수 있습니다. 그러나 코드가 HTTPS 대신 호출을 수행하기 위해 HTTP를 UseHttpsRedirection 사용하므로 Web API 프로젝트에서 호출을 주석 처리해야 합니다.

          //app.UseHttpsRedirection();
    

Docker Compose 지원 추가

  1. WebFrontEnd 프로젝트에서 추가 > 컨테이너 오케스트레이터 지원을 선택합니다. Docker 지원 옵션 대화 상자가 나타납니다.

  2. Docker Compose를 선택합니다.

  3. 대상 OS(예: Linux)를 선택합니다.

    Screenshot of choosing the Target OS.

    Visual Studio에서 솔루션의 docker-compose 노드에 docker-compose.yml 파일과 .dockerignore 파일을 만들고, 해당 프로젝트가 굵은 글꼴로 표시되어 시작 프로젝트임을 나타냅니다.

    Screenshot of Solution Explorer with docker-compose project added.

    docker-compose.yml은 다음과 같이 표시됩니다.

    version: '3.4'
    
     services:
       webfrontend:
         image: ${DOCKER_REGISTRY-}webfrontend
         build:
           context: .
           dockerfile: WebFrontEnd/Dockerfile
    

    version 첫 번째 줄에 지정된 것은 Docker Compose 파일 버전입니다. 일반적으로 파일을 해석하는 방법을 이해하기 위해 도구에서 사용하므로 변경해서는 안 됩니다.

    .dockerignore 파일에는 Docker에서 컨테이너에 포함하지 않을 파일 형식과 확장명이 포함되어 있습니다. 이러한 파일은 일반적으로 개발 환경 및 소스 제어와 연결되며, 개발 중인 앱이나 서비스의 일부가 아닙니다.

    실행 중인 명령에 대한 자세한 내용은 출력 창의 컨테이너 도구 섹션을 참조하세요. 명령줄 도구 docker-compose가 런타임 컨테이너를 구성하고 만드는 데 사용되는 것을 확인할 수 있습니다.

  4. Web API 프로젝트에서 다시 프로젝트 노드를 마우스 오른쪽 단추로 클릭하고 추가>컨테이너 오케스트레이터 지원을 선택합니다. Docker Compose를 선택하고 동일한 대상 OS를 선택합니다.

    참고 항목

    이 단계에서 Visual Studio는 Dockerfile을 만들도록 제안합니다. Docker 지원이 이미 포함된 프로젝트에서 이 작업을 수행하면, 기존 Dockerfile을 덮어쓸지 여부를 묻는 메시지가 표시됩니다. Dockerfile의 변경 내용을 유지하려면 아니요를 선택합니다.

    Visual Studio에서 docker compose YML 파일을 일부 변경합니다. 이제 두 서비스가 모두 포함되었습니다.

    version: '3.4'
    
    services:
      webfrontend:
        image: ${DOCKER_REGISTRY-}webfrontend
        build:
          context: .
          dockerfile: WebFrontEnd/Dockerfile
    
      mywebapi:
        image: ${DOCKER_REGISTRY-}mywebapi
        build:
          context: .
          dockerfile: MyWebAPI/Dockerfile
    
  5. 컨테이너 오케스트레이션을 추가하는 대상에 해당하는 첫 번째 프로젝트는 실행하거나 디버그할 때 시작하도록 설정되었습니다. 프로젝트 속성에서 docker-compose 프로젝트의 시작 작업을 구성할 수 있습니다. docker-compose 프로젝트 노드에서 마우스 오른쪽 단추를 클릭하여 상황에 맞는 메뉴를 열고 속성을 선택하거나, Alt+Enter를 사용합니다. 다음 스크린샷은 여기서 사용된 솔루션에 대해 지정하려는 속성을 보여 줍니다. 예를 들어 서비스 URL 속성을 사용자 지정하여 로드되는 페이지를 변경할 수 있습니다.

    Screenshot of docker-compose project properties.

    시작될 때 표시되는 내용은 다음과 같습니다(.NET Core 2.x 버전).

    Screenshot of running web app.

    .NET 3.1용 웹앱은 JSON 형식의 날씨 데이터를 표시합니다.

  6. 이제 Web API 프로젝트가 아닌 WebFrontEnd에 디버거를 연결하기만 하면 됩니다. 메뉴 모음에서 시작 단추 옆의 드롭다운을 사용하여 디버그 옵션 메뉴를 표시할 수 있습니다. Docker Compose 시작 설정 관리를 선택합니다.

    Screenshot of Debug Manage Compose Settings menu item.

    Docker Compose 시작 설정 관리 대화 상자가 나타납니다. 이 대화 상자를 사용하면 디버거 연결 여부에 관계없이 시작되는 디버깅 세션 중에 시작되는 서비스의 하위 집합과 시작 서비스 및 URL을 제어할 수 있습니다. Compose 서비스의 하위 집합 시작을 참조하세요.

    Screenshot of Manage Docker Compose Launch Settings dialog box.

    새로 만들기를 선택하여 새 프로필을 만들고 이름을 Debug WebFrontEnd only로 지정합니다. 그런 다음 웹 API 프로젝트를 디버그하지 않고 시작으로 설정하고, WebFrontEnd 프로젝트를 디버그하고 시작으로 설정된 채로 두고 저장을 선택합니다.

    새 구성이 다음 F5 키에 대한 기본값으로 선택됩니다.

  7. F5 키를 눌러 예상대로 작동하는지 확인합니다.

축하합니다. 사용자 지정 Docker Compose 프로필을 사용하여 Docker Compose 애플리케이션을 실행하고 있습니다.

  1. WebFrontEnd 프로젝트에서 Index.cshtml.cs 파일을 열고, OnGet 메서드를 다음 코드로 바꿉니다.

    public async Task OnGet()
    {
       using (var client = new System.Net.Http.HttpClient())
       {
          // Call *mywebapi*, and display its response in the page
          var request = new System.Net.Http.HttpRequestMessage();
          // webapi is the container name
          request.RequestUri = new Uri("http://webapi/Counter");
          var response = await client.SendAsync(request);
          string counter = await response.Content.ReadAsStringAsync();
          ViewData["Message"] = $"Counter value from cache :{counter}";
       }
    }
    

    참고 항목

    실제 코드에서는 모든 요청 후에 HttpClient를 삭제하면 안 됩니다. 모범 사례는 HttpClientFactory를 사용하여 복원력 있는 HTTP 요청 구현을 참조하세요.

  2. Index.cshtml 파일에 ViewData["Message"]를 표시할 줄을 추가하여 파일이 다음 코드와 같이 표시되도록 합니다.

    @page
    @model IndexModel
    @{
        ViewData["Title"] = "Home page";
    }
    
    <div class="text-center">
        <h1 class="display-4">Welcome</h1>
        <p>Learn about <a href="/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
        <p>@ViewData["Message"]</p>
    </div>
    

    이 코드는 Web API 프로젝트에서 반환된 카운터의 값을 표시합니다.

Docker Compose 지원 추가

  1. WebFrontEnd 프로젝트에서 추가 > 컨테이너 오케스트레이터 지원을 선택합니다. Docker 지원 옵션 대화 상자가 나타납니다.

  2. Docker Compose를 선택합니다.

  3. 대상 OS(예: Linux)를 선택합니다.

    Screenshot of choosing the Target OS.

    Visual Studio에서 솔루션의 docker-compose 노드에 docker-compose.yml 파일과 .dockerignore 파일을 만들고, 해당 프로젝트가 굵은 글꼴로 표시되어 시작 프로젝트임을 나타냅니다.

    Screenshot of Solution Explorer with docker-compose project added.

    docker-compose.yml은 다음과 같이 표시됩니다.

    version: '3.4'
    
     services:
       webfrontend:
         image: ${DOCKER_REGISTRY-}webfrontend
         build:
           context: .
           dockerfile: WebFrontEnd/Dockerfile
    

    version 첫 번째 줄에 지정된 것은 Docker Compose 파일 버전입니다. 일반적으로 파일을 해석하는 방법을 이해하기 위해 도구에서 사용하므로 변경해서는 안 됩니다.

    .dockerignore 파일에는 Docker에서 컨테이너에 포함하지 않을 파일 형식과 확장명이 포함되어 있습니다. 이러한 파일은 일반적으로 개발 환경 및 소스 제어와 연결되며, 개발 중인 앱이나 서비스의 일부가 아닙니다.

    실행 중인 명령에 대한 자세한 내용은 출력 창의 컨테이너 도구 섹션을 참조하세요. 명령줄 도구 docker-compose가 런타임 컨테이너를 구성하고 만드는 데 사용되는 것을 확인할 수 있습니다.

  4. Web API 프로젝트에서 다시 프로젝트 노드를 마우스 오른쪽 단추로 클릭하고 추가>컨테이너 오케스트레이터 지원을 선택합니다. Docker Compose를 선택하고 동일한 대상 OS를 선택합니다.

    참고 항목

    이 단계에서 Visual Studio는 Dockerfile을 만들도록 제안합니다. Docker 지원이 이미 포함된 프로젝트에서 이 작업을 수행하면, 기존 Dockerfile을 덮어쓸지 여부를 묻는 메시지가 표시됩니다. Dockerfile의 변경 내용을 유지하려면 아니요를 선택합니다.

    Visual Studio에서 docker compose YML 파일을 일부 변경합니다. 이제 두 서비스가 모두 포함되었습니다.

    version: '3.4'
    
    services:
      webfrontend:
        image: ${DOCKER_REGISTRY-}webfrontend
        build:
          context: .
          dockerfile: WebFrontEnd/Dockerfile
    
      mywebapi:
        image: ${DOCKER_REGISTRY-}mywebapi
        build:
          context: .
          dockerfile: MyWebAPI/Dockerfile
    
  5. Redis 캐시를 다음과 같이 docker.compose.yml 파일에 추가합니다.

    redis:
       image: redis
    

    들여쓰기는 다른 두 서비스와 동일한 수준에 있는지 확인합니다.

  6. 컨테이너 오케스트레이션을 추가하는 대상에 해당하는 첫 번째 프로젝트는 실행하거나 디버그할 때 시작하도록 설정되었습니다. 프로젝트 속성에서 docker-compose 프로젝트의 시작 작업을 구성할 수 있습니다. docker-compose 프로젝트 노드에서 바로 가기 메뉴를 마우스 오른쪽으로 클릭하여 연 다음 속성을 선택하거나, Alt+Enter를 사용합니다. 예를 들어 서비스 URL 속성을 사용자 지정하여 로드되는 페이지를 변경할 수 있습니다.

    Screenshot of docker-compose project properties.

  7. F5키를 누릅니다. 시작할 때 표시되는 내용은 다음과 같습니다.

    Screenshot of running web app.

  8. 컨테이너 창을 사용하여 컨테이너를 모니터링할 수 있습니다 . 창이 표시되지 않으면 검색 상자를 사용하고 Ctrl K, Ctrl++O를 누르거나 Ctrl Q+누릅니다. 기능 검색에서 목록에서 다른 Windows>컨테이너 보기를>검색containers하고 선택합니다.

  9. 솔루션 컨테이너 노드를 확장하고 Docker Compose 프로젝트의 노드를 선택하여 이 창의 로그 탭에서 결합된 로그를 봅니다.

    Screenshot showing viewing the Logs tab in the Containers window.

    개별 컨테이너의 노드를 선택하여 로그, 환경 변수, 파일 시스템 및 기타 세부 정보를 볼 수도 있습니다.

시작 프로필 설정

  1. 이 솔루션에는 Redis Cache가 있지만 디버깅 세션을 시작할 때마다 Redis 캐시 컨테이너를 다시 빌드하는 것은 효율적이지 않습니다. 이러한 상황을 방지하려면 몇 가지 시작 프로필을 설정할 수 있습니다. 하나의 프로필을 만들어 Redis 캐시를 시작합니다. 다른 서비스를 시작하는 두 번째 프로필을 만듭니다. 두 번째 프로필은 이미 실행 중인 Redis 캐시 컨테이너를 사용할 수 있습니다. 메뉴 모음에서 시작 단추 옆의 드롭다운을 사용하여 디버깅 옵션이 있는 메뉴를 열 수 있습니다. Docker Compose 시작 설정 관리를 선택합니다.

    Screenshot of Debug Manage Compose Settings menu item.

    Docker Compose 시작 설정 관리 대화 상자가 나타납니다. 이 대화 상자를 사용하면 디버거 연결 여부에 관계없이 시작되는 디버깅 세션 중에 시작되는 서비스의 하위 집합과 시작 서비스 및 URL을 제어할 수 있습니다. Compose 서비스의 하위 집합 시작을 참조하세요.

    Screenshot of Manage Docker Compose Launch Settings dialog box.

    새로 만들기를 선택하여 새 프로필을 만들고 이름을 Start Redis로 지정합니다. 그런 다음, Redis 컨테이너를 디버깅하지 않고 시작으로 설정하고 다른 집합을 시작 안 함으로 두고 저장을 선택합니다.

    Screenshot showing creating the Redis profile that starts the Redis service only.

    그런 다음, Redis를 시작하지 않고 나머지 두 서비스를 시작하는 또 다른 프로필인 Start My Services를 만듭니다.

    Screenshot showing creating the Services profile that starts the other services.

    (선택 사항) 모든 것을 시작하는 세 번째 프로필 Start All을 만듭니다. Redis에 대해 디버깅하지 않고 시작을 선택할 수 있습니다.

  2. 기본 Visual Studio 도구 모음의 드롭다운 목록에서 Redis 시작을 선택합니다. Redis 컨테이너는 디버깅하지 않고 빌드되고 시작됩니다. 컨테이너 창을 사용하여 실행 중인지 확인할 수 있습니다. 그런 다음, 드롭다운 목록에서 내 서비스 시작을 선택하고 F5 키를 눌러 시작합니다. 이제 여러 후속 디버그 세션에서 Redis 캐시 컨테이너를 계속 실행할 수 있습니다. Start My Services를 사용할 때마다 해당 서비스는 동일한 Redis 캐시 컨테이너를 사용합니다.

축하합니다. 사용자 지정 Docker Compose 프로필을 사용하여 Docker Compose 애플리케이션을 실행하고 있습니다.

다음 단계

Azure에 컨테이너를 배포하는 옵션을 확인합니다.

참고 항목

Docker Compose
컨테이너 도구