Compartilhar via


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 se comunicar entre eles ao usar as Ferramentas de Contêiner no Visual Studio. O gerenciamento de vários contêineres requer orquestração de contêiner e requer um orquestrador, como o Docker Compose. Para esses procedimentos, você usa o Docker Compose. O Docker Compose é ótimo para depuração local e teste no decorrer do ciclo de desenvolvimento.

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

Pré-requisitos

  • Área de Trabalho do Docker
  • Visual Studio com a carga de trabalho de desenvolvimento ASP.NET e web, carga de trabalho de desenvolvimento do Azure e/ou carga de trabalho de desenvolvimento multiplataforma do .NET instalada. Essa instalação inclui o SDK do .NET.
  • Área de Trabalho do Docker
  • Visual Studio com a carga de trabalho de desenvolvimento ASP.NET e web, carga de trabalho de desenvolvimento do Azure e/ou carga de trabalho de desenvolvimento multiplataforma do .NET instalada. Essa instalação inclui o SDK do .NET.

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.

Não selecione Habilitar suporte ao contêiner. Você adicionará suporte a contêiner posteriormente no processo.

Captura de tela da tela Informações Adicionais ao criar um projeto Web. A opção para habilitar o suporte ao contêiner não está selecionada.

Criar um projeto de API Web

  1. Adicione um projeto à mesma solução e chame-o myWebAPI. Selecione API como tipo de projeto e desmarque a caixa de seleção Configurar para HTTPS.

    Nota

    Nesse design, estamos usando apenas HTTPS para comunicação com o cliente, não para comunicação entre contêineres no mesmo aplicativo Web. Somente WebFrontEnd precisa de HTTPS e o código nos exemplos pressupõe que você desmarcou 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 a contêineres, não para solicitações de contêiner para contêiner.

    Captura de tela da criação do projeto de API Web.

  2. Adicione suporte para o Cache do Azure para Redis. Adicione o pacote NuGet Microsoft.Extensions.Caching.StackExchangeRedis (não StackExchange.Redis). Em Program.cs, adicione as seguintes linhas, pouco 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. Adicionar diretivas de uso 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 os WeatherForecast.cs e 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 armazena o contador no cache.

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()
    {
       // Call *mywebapi*, and display its response in the page
       using (var client = new System.Net.Http.HttpClient())
       {
          var request = new System.Net.Http.HttpRequestMessage();
    
          // A delay is a quick and dirty way to work around the fact that
          // the mywebapi service might not be immediately ready on startup.
          // See the text for some ideas on how you can improve this.
          // Uncomment if not using healthcheck (Visual Studio 17.13 or later)
          // await System.Threading.Tasks.Task.Delay(10000);
    
          // mywebapi is the service name, as listed in docker-compose.yml.
          // Docker Compose creates a default network with the services
          // listed in docker-compose.yml exposed as host names.
          // The port 8080 is exposed in the WebAPI Dockerfile.
          // If your WebAPI is exposed on port 80 (the default for HTTP, used
          // with earlier versions of the generated Dockerfile), change
          // or delete the port number here.
          request.RequestUri = new Uri("http://mywebapi:8080/Counter");
          var response = await client.SendAsync(request);
          string counter = await response.Content.ReadAsStringAsync();
          ViewData["Message"] = $"Counter value from cache :{counter}";
       }
    }
    

    Nota

    No código do mundo real, não é necessário descartar HttpClient após cada solicitação. Para obter práticas recomendadas, consulte Usar HttpClientFactory para implementar solicitações HTTP resilientes.

    O URI fornecido faz referência a um nome de serviço definido no arquivo docker-compose.yml. O Docker Compose configura uma rede padrão para comunicação entre contêineres usando os nomes de serviço listados como hosts.

    O código mostrado aqui funciona com o .NET 8 e posterior, que configura uma conta de usuário no Dockerfile sem privilégios de administrador e expõe a porta 8080 porque a porta http padrão 80 não está acessível sem privilégios elevados.

  2. No arquivo Index.cshtml, adicione uma linha para exibir ViewData["Message"] para 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 de API Web. Ele incrementa sempre que o usuário acessa ou atualiza a página.

Adicionar suporte ao 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. Visual Studio 17.12 e posterior Escolha as opções de estruturação para o projeto WebFrontEnd.

    Captura de tela mostrando a caixa de diálogo Opções de Scaffolding do Contêiner para o projeto WebFrontEnd.

    Visual Studio 17.11 e anteriores Escolha seu sistema operacional de destino, por exemplo, Linux.

    Captura de tela da escolha do sistema operacional de destino.

    O Visual Studio cria um arquivo docker-compose.yml e um arquivo .dockerignore no nó docker-compose na solução. Esse projeto é mostrado em negrito, indicando que é o projeto de inicialização.

    Captura de tela do Gerenciador de Soluções com o projeto Docker Compose adicionado.

    O docker-compose.yml aparece da seguinte maneira:

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

    O arquivo .dockerignore contém tipos de arquivo e extensões que você não deseja que o Docker inclua no contêiner. Esses arquivos geralmente são associados ao ambiente de desenvolvimento e ao controle do código-fonte, 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 do Docker Compose e selecione o mesmo sistema operacional de destino.

    Nota

    Nesta etapa, o Visual Studio se oferecerá para criar um Dockerfile. Se você fizer isso em um projeto que já tem suporte ao Docker, será solicitado que você queira 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 docker-compose. Agora ambos os serviços estão incluídos.

    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 ao arquivo docker-compose.yml:

    redis:
       image: redis
    

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

  6. (Visual Studio 17.13 ou posterior) Os serviços dependentes demonstram um problema comum. A solicitação HTTP na página principal do front-end pode ser executada imediatamente na inicialização do aplicativo, antes que o serviço de mywebapi esteja pronto para receber solicitações da Web. Se você estiver usando o Visual Studio 17.13 ou posterior, poderá usar os recursos do Docker Compose depends_on e healthcheck em docker-compose.yml para fazer com que os projetos comecem na sequência certa e tê-los prontos para atender às solicitações quando necessário. Confira Docker Compose – Ordem de inicialização.

    services:
      webfrontend:
         image: ${DOCKER_REGISTRY-}webfrontend
         depends_on:
            mywebapi:
              condition: service_healthy
         build:
            context: .
            dockerfile: WebFrontEnd/Dockerfile
    
      mywebapi:
         image: ${DOCKER_REGISTRY-}mywebapi
         depends_on:
            redis:
              condition: service_started
         healthcheck:
            test: curl --fail http://mywebapi:8080/Counter || exit 1
            interval: 20s
            timeout: 20s
            retries: 5
         build:
            context: .
            dockerfile: MyWebAPI/Dockerfile
    
      redis:
         image: redis
    

    Neste exemplo, a verificação de integridade usa curl para verificar se o serviço está pronto para processar solicitações. Se a imagem que você está usando não tiver curl instalada, adicione linhas ao estágio base do Dockerfile MyWebAPI para instalá-la. Esta etapa requer privilégios elevados, mas você pode restaurar os privilégios normais do usuário depois de instalá-lo, conforme mostrado aqui (para as imagens de Debian usadas neste exemplo):

    USER root
    RUN apt-get update && apt-get install -y curl
    USER $APP_UID
    

    Nota

    Se você estiver usando uma distribuição do Linux, como o Alpine, que não dá suporte a apt-get, tente RUN apk --no-cache add curl em vez disso.

    Esses recursos do Docker Compose exigem uma configuração de propriedade no arquivo de projeto do Docker Compose (.dcproj). Defina a propriedade DependencyAwareStart como true:

    <PropertyGroup>
       <!-- existing properties -->
       <DependencyAwareStart>true</DependencyAwareStart>
    </PropertyGroup>
    

    Essa propriedade ativa uma maneira diferente de iniciar os contêineres no modo de depuração, que é compatível com os recursos de dependência de serviço.

    Com essas alterações, o serviço webfrontend não será iniciado até mywebapi iniciar e lidar com êxito com uma solicitação da Web.

  7. 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 da URL de Serviço.

    captura de tela das propriedades do projeto do Docker Compose.

  8. Pressione F5. Veja o que você vê quando iniciado:

    Captura de tela da execução do aplicativo Web.

  9. 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+Oou pressione Ctrl+Q. Em Pesquisa de recursos, procure containers e escolha Exibir>Outro Windows>Contêineres na lista.

  10. 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.

    Captura de tela mostrando a visualização da guia Logs na janela Contêineres.

    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 do Azure para Redis, mas não é eficiente recompilar o contêiner do cache 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 do Azure para Redis. Crie um segundo perfil para iniciar os outros serviços. O segundo perfil pode usar o contêiner de cache 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.

    Captura de tela do item de menu Configurações de depuração e gerenciamento do Compose.

    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 é iniciada com ou sem o depurador anexado, e o serviço de inicialização e a URL. Confira Iniciar um subconjunto de serviços do Compose.

    Captura de tela da caixa de diálogo Gerenciar Configurações de Inicialização do Docker Compose.

    Escolha Novo para criar um novo perfil e nomeie-o 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.

    Captura de tela mostrando a criação do perfil Redis que inicia apenas o serviço Redis.

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

    Captura de tela mostrando a criação do perfil de Serviços que inicia os outros serviços.

    (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. O contêiner do Redis é compilado e iniciado sem depuração. 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 de cache em execução em várias sessões de depuração subsequentes. Sempre que você usa Iniciar Meus Serviços, esses serviços usam o mesmo contêiner de cache.

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 seus contêineres de no Azure. Se você estiver pronto para implantar nos Aplicativos de Contêiner do Azure, consulte Implantar um aplicativo multicontener nos Aplicativos de Contêiner do Azure.

Consulte também

Docker Compose

Ferramentas de Contêiner