Tutorial: Criar um aplicativo de vários contêineres com o Docker Compose

Neste tutorial, você aprenderá a gerenciar mais de um contêiner e a se comunicar entre eles usando as Ferramentas de Contêiner no Visual Studio. O gerenciamento de vários contêineres requer a orquestração de contêiner e requer um orquestrador, como o Docker Compose ou o Service Fabric. Para esses procedimentos, você usa o Docker Compose. O Docker Compose é excelente para depuração e teste locais no decorrer do ciclo de desenvolvimento.

O exemplo concluído que você criará neste tutorial pode ser encontrado no GitHub, em https://github.com/MicrosoftDocs/vs-tutorial-samples na pasta docker/ComposeSample.

Pré-requisitos

  • Docker Desktop
  • Visual Studio 2019 com Desenvolvimento para a Web, a carga de trabalho das Ferramentas do Azure e/ou a carga de trabalho de Desenvolvimento multiplataforma do .NET instalada
  • Docker Desktop
  • Visual Studio 2022 com Desenvolvimento para a Web, a carga de trabalho das Ferramentas do Azure e/ou a carga de trabalho de Desenvolvimento multiplataforma do .NET instalada. Essa instalação inclui as ferramentas de desenvolvimento do .NET 8.

Criar um projeto de Aplicativo Web

No Visual Studio, crie um projeto de Aplicativo Web ASP.NET Core, chamado WebFrontEnd, para criar um aplicativo Web com páginas Razor.

Screenshot showing Create ASP.NET Core Web App project.

Não selecione Habilitar Suporte ao Docker. Adicione suporte ao Docker posteriormente no processo.

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

Observação

No Visual Studio 2022 17.2 e posteriores, você pode usar o Azure Functions para este projeto.

Screenshot showing Create ASP.NET Core Web App project.

Não selecione Habilitar Suporte ao Docker. Adicione suporte ao Docker posteriormente no processo.

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

Criar um projeto de API Web

Adicione um projeto à mesma solução e dê a ele o nome MyWebAPI. Selecione API como tipo de projeto e desmarque a caixa de seleção Configurar para HTTPS. Nesse design, estamos usando apenas SSL para comunicação com o cliente, não para comunicação entre contêineres no mesmo aplicativo Web. Apenas WebFrontEnd precisa de HTTPS, e o código nos exemplos pressupõe que você tenha desmarcado essa caixa de seleção. Em geral, os certificados de desenvolvedor do .NET usados pelo Visual Studio só têm suporte para solicitações externas para contêiner, não para solicitações de contêiner para contêiner.

Screenshot of creating the Web API project.

  1. Adicione um projeto à mesma solução e dê a ele o nome WebAPI. Selecione API como tipo de projeto e desmarque a caixa de seleção Configurar para HTTPS. Nesse design, estamos usando apenas SSL para comunicação com o cliente, não para comunicação entre contêineres no mesmo aplicativo Web. Apenas WebFrontEnd precisa de HTTPS, e o código nos exemplos pressupõe que você tenha desmarcado essa caixa de seleção. Em geral, os certificados de desenvolvedor do .NET usados pelo Visual Studio só têm suporte para solicitações externas para contêiner, não para solicitações de contêiner para contêiner.

    Screenshot of creating the Web API project.

  2. Adicione suporte para o Cache Redis. Adicione o pacote NuGet Microsoft.Extensions.Caching.StackExchangeRedis (não StackExchange.Redis). Em Program.cs, adicione as seguintes linhas, logo antes de 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. Adicione diretivas using em Program.cs para Microsoft.Extensions.Caching.Distributed e Microsoft.Extensions.Caching.StackExchangeRedis.

    using Microsoft.Extensions.Caching.Distributed;
    using Microsoft.Extensions.Caching.StackExchangeRedis;
    
  4. No projeto da API Web, exclua o WeatherForecast.cs e o Controllers/WeatherForecastController.cs existentes e adicione um arquivo em Controladores, CounterController.cs, com o seguinte conteúdo:

    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;
            }
        }
    }
    

    O serviço incrementa um contador sempre que a página é acessada e o armazena no cache Redis.

Adicionar código para chamar a API Web

  1. No projeto WebFrontEnd, abra o arquivo Index.cshtml.cs e substitua o método OnGet pelo código a seguir.

     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();
        }
     }
    

    Observação

    No código do mundo real, não descarte HttpClient após cada solicitação. Para conhecer as melhores práticas, consulte Usar HttpClientFactory para implementar solicitações HTTP resilientes.

  2. No arquivo Index.cshtml, adicione uma linha para exibir ViewData["Message"] de modo que o arquivo se pareça com o seguinte código:

    @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. (Somente ASP.NET 2.x) Agora, no projeto de API Web, adicione código ao controlador Valores para personalizar a mensagem retornada pela API para a chamada que você adicionou de webfrontend.

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

    Observação

    No .NET Core 3.1 e posterior, você pode usar a API WeatherForecast fornecida em vez desse código extra. No entanto, você precisa comentar a chamada para UseHttpsRedirection no projeto de API Web porque o código usa HTTP para fazer a chamada em vez de HTTPS.

          //app.UseHttpsRedirection();
    

Adicionar suporte para Docker Compose

  1. No projeto WebFrontEnd, escolha Adicionar > Suporte ao Orquestrador de Contêineres. A caixa de diálogo Opções de Suporte do Docker é exibida.

  2. Escolha Docker Compose.

  3. Escolha o sistema operacional de destino, por exemplo, Linux.

    Screenshot of choosing the Target OS.

    O Visual Studio cria um arquivo docker-compose.yml e um arquivo .dockerignore no nó docker-compose da solução, e esse projeto aparece com a fonte em negrito, o que mostra que é o projeto de inicialização.

    Screenshot of Solution Explorer with docker-compose project added.

    O docker-compose.yml é exibido da seguinte maneira:

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

    O version especificado na primeira linha é a versão do arquivo do Docker Compose. Normalmente, você não deve alterá-lo, pois ele é usado pelas ferramentas para entender como interpretar o arquivo.

    O arquivo .dockerignore contém tipos de arquivo e extensões que você não deseja que o Docker inclua no contêiner. Em geral, esses arquivos estão associados ao ambiente de desenvolvimento e ao controle do código-fonte, e não fazem parte do aplicativo ou serviço que você está desenvolvendo.

    Examine a seção Ferramentas de Contêiner do painel de saída para obter detalhes dos comandos que estão sendo executados. Você pode ver que a ferramenta de linha de comando docker-compose é usada para configurar e criar os contêineres de runtime.

  4. No projeto da API Web, clique novamente com o botão direito do mouse no nó do projeto e escolha Adicionar>Suporte ao Orquestrador de Contêineres. Escolha Docker Compose e selecione o mesmo sistema operacional de destino.

    Observação

    Nesta etapa, o Visual Studio oferecerá para criar um Dockerfile. Se você fizer isso em um projeto que já tem suporte para o Docker, será confirmado se você deseja substituir o Dockerfile existente. Se você fez alterações no Dockerfile que deseja manter, escolha não.

    O Visual Studio faz algumas alterações no arquivo YML do Docker Compose. Agora, ambos os serviços estão incluídos.

    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. O primeiro projeto ao qual você adiciona orquestração de contêiner é configurado para ser iniciado quando você executa ou depura. Você pode configurar a ação de inicialização nas Propriedades do Projeto para o projeto docker-compose. No nó do projeto docker-compose, clique com o botão direito do mouse para abrir o menu de contexto e escolha Propriedades, ou use Alt+Enter. A captura de tela a seguir mostra as propriedades que você deseja para a solução usada aqui. Por exemplo, você pode alterar a página carregada personalizando a propriedade URL do Serviço.

    Screenshot of docker-compose project properties.

    Você vê o seguinte após a inicialização (a versão do .NET Core 2.x):

    Screenshot of running web app.

    O aplicativo Web para .NET 3.1 mostra os dados meteorológicos no formato JSON.

  6. Agora, suponha que você só tenha interesse em anexar o depurador ao WebFrontEnd, não ao projeto da API Web. Na barra de menus, você pode usar a lista suspensa ao lado do botão Iniciar para abrir um menu de opções de depuração; escolha Gerenciar Configurações de Inicialização do Docker Compose.

    Screenshot of Debug Manage Compose Settings menu item.

    A caixa de diálogo Gerenciar Configurações de Inicialização do Docker Compose é exibida. Com essa caixa de diálogo, você pode controlar qual subconjunto de serviços é iniciado durante uma sessão de depuração, que são iniciados com ou sem o depurador anexado, e o serviço de inicialização e a URL. Consulte Iniciar um subconjunto de serviços do Compose.

    Screenshot of Manage Docker Compose Launch Settings dialog box.

    Escolha Novo para criar um perfil e dê a ele o nome Debug WebFrontEnd only. Em seguida, defina o projeto da API Web como Iniciar sem depurar, deixe o projeto WebFrontEnd configurado para começar com a depuração e escolha Salvar.

    A nova configuração é escolhida como padrão para o próximo F5.

  7. Pressione F5 para confirmar se funciona conforme o esperado.

Parabéns, você está executando um aplicativo Docker Compose com um perfil personalizado do Docker Compose.

  1. No projeto WebFrontEnd, abra o arquivo Index.cshtml.cs e substitua o método OnGet pelo código a seguir.

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

    Observação

    No código do mundo real, não descarte HttpClient após cada solicitação. Para conhecer as melhores práticas, consulte Usar HttpClientFactory para implementar solicitações HTTP resilientes.

  2. No arquivo Index.cshtml, adicione uma linha para exibir ViewData["Message"] de modo que o arquivo se pareça com o seguinte código:

    @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>
    

    Esse código exibe o valor do contador retornado do projeto da API Web.

Adicionar suporte para Docker Compose

  1. No projeto WebFrontEnd, escolha Adicionar > Suporte ao Orquestrador de Contêineres. A caixa de diálogo Opções de Suporte do Docker é exibida.

  2. Escolha Docker Compose.

  3. Escolha o sistema operacional de destino, por exemplo, Linux.

    Screenshot of choosing the Target OS.

    O Visual Studio cria um arquivo docker-compose.yml e um arquivo .dockerignore no nó docker-compose da solução, e esse projeto aparece com a fonte em negrito, o que mostra que é o projeto de inicialização.

    Screenshot of Solution Explorer with docker-compose project added.

    O docker-compose.yml é exibido da seguinte maneira:

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

    O version especificado na primeira linha é a versão do arquivo do Docker Compose. Normalmente, você não deve alterá-lo, pois ele é usado pelas ferramentas para entender como interpretar o arquivo.

    O arquivo .dockerignore contém tipos de arquivo e extensões que você não deseja que o Docker inclua no contêiner. Em geral, esses arquivos estão associados ao ambiente de desenvolvimento e ao controle do código-fonte, e não fazem parte do aplicativo ou serviço que você está desenvolvendo.

    Examine a seção Ferramentas de Contêiner do painel de saída para obter detalhes dos comandos que estão sendo executados. Você pode ver que a ferramenta de linha de comando docker-compose é usada para configurar e criar os contêineres de runtime.

  4. No projeto da API Web, clique novamente com o botão direito do mouse no nó do projeto e escolha Adicionar>Suporte ao Orquestrador de Contêineres. Escolha Docker Compose e selecione o mesmo sistema operacional de destino.

    Observação

    Nesta etapa, o Visual Studio oferecerá para criar um Dockerfile. Se você fizer isso em um projeto que já tem suporte para o Docker, será confirmado se você deseja substituir o Dockerfile existente. Se você fez alterações no Dockerfile que deseja manter, escolha não.

    O Visual Studio faz algumas alterações no arquivo YML do Docker Compose. Agora, ambos os serviços estão incluídos.

    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. Adicione o cache Redis ao arquivo docker.compose.yml:

    redis:
       image: redis
    

    Verifique se o recuo está no mesmo nível que os outros dois serviços.

  6. O primeiro projeto ao qual você adiciona orquestração de contêiner é configurado para ser iniciado quando você executa ou depura. Você pode configurar a ação de inicialização nas Propriedades do Projeto para o projeto docker-compose. No nó do projeto docker-compose, clique com o botão direito do mouse para abrir o menu de contexto e escolha Propriedades, ou use Alt+Enter. Por exemplo, você pode alterar a página carregada personalizando a propriedade URL do Serviço.

    Screenshot of docker-compose project properties.

  7. Pressione F5. Você vê o seguinte quando ele é iniciado:

    Screenshot of running web app.

  8. Você pode monitorar os contêineres usando a janela Contêineres. Se você não vir a janela, use a caixa de pesquisa, pressione Ctrl+K, Ctrl+O ou pressione Ctrl+Q. Em Pesquisa de recursos, procure containers e escolha Exibir>Outro Windows>Contêineres na lista.

  9. Expanda o nó Contêineres de Solução e escolha o nó do projeto Docker Compose para exibir os logs combinados na guia Logs do desta janela.

    Screenshot showing viewing the Logs tab in the Containers window.

    Você também pode selecionar o nó de um contêiner individual para exibir logs, variáveis de ambiente, o sistema de arquivos e outros detalhes.

Configurar perfis de inicialização

  1. Essa solução tem um Cache Redis, mas não é eficiente recompilar o contêiner do cache Redis toda vez que você iniciar uma sessão de depuração. Para evitar essa situação, você pode configurar alguns perfis de inicialização. Crie um perfil para iniciar o cache Redis. Crie um segundo perfil para iniciar os outros serviços. O segundo perfil pode usar o contêiner de cache Redis que já está em execução. Na barra de menus, você pode usar a lista suspensa ao lado do botão iniciar para abrir um menu com opções de depuração. Selecione Gerenciar Configurações de Inicialização do Docker Compose.

    Screenshot of Debug Manage Compose Settings menu item.

    A caixa de diálogo Gerenciar Configurações de Inicialização do Docker Compose é exibida. Com essa caixa de diálogo, você pode controlar qual subconjunto de serviços é iniciado durante uma sessão de depuração, que são iniciados com ou sem o depurador anexado, e o serviço de inicialização e a URL. Consulte Iniciar um subconjunto de serviços do Compose.

    Screenshot of Manage Docker Compose Launch Settings dialog box.

    Escolha Novo para criar um perfil e dê a ele o nome Start Redis. Em seguida, configure o contêiner Redis para Iniciar sem depuração, deixe o outro definido como Não iniciar e escolha Salvar.

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

    Em seguida, crie outro perfil Start My Services que não inicia o Redis, mas inicia os outros dois serviços.

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

    (Opcional) Crie um terceiro perfil Start All para iniciar tudo. Você pode escolher Iniciar sem depuração para o Redis.

  2. Escolha Iniciar Redis na lista suspensa na barra de ferramentas principal do Visual Studio e pressione F5. O contêiner do Redis é compilado e iniciado. Use a janela Contêineres para ver que ele está em execução. Em seguida, escolha Iniciar meus Serviços na lista suspensa e pressione F5 para iniciá-los. Agora, você pode manter o contêiner do cache Redis em execução em várias sessões de depuração subsequentes. Sempre que você usar Iniciar meus Serviços, esses serviços usam o mesmo contêiner do cache Redis.

Parabéns, você está executando um aplicativo Docker Compose com um perfil personalizado do Docker Compose.

Próximas etapas

Examine as opções para implantar contêineres no Azure.

Confira também

Docker Compose
Ferramentas de Contêiner