Partilhar 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. A gestão de múltiplos contentores requer orquestração de contentores e necessita de um orquestrador, como o Docker Compose ou o Service Fabric. Para esses procedimentos, use o Docker Compose. O Docker Compose é ótimo para depuração e testes locais durante o ciclo de desenvolvimento.

O exemplo completo que você cria 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 a carga de trabalho de Desenvolvimento Web, Ferramentas do Azure, e/ou a carga de trabalho de Desenvolvimento entre Plataformas .NET instalada
  • Docker Desktop
  • pt-PT: Visual Studio 2022 com a carga de trabalho de Desenvolvimento Web, carga de trabalho das Ferramentas do Azure e/ou carga de trabalho de desenvolvimento entre plataformas .NET instalada. Esta instalação inclui as ferramentas de desenvolvimento do .NET 8.

Criar um projeto de aplicativo Web

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

Captura de tela mostrando o projeto de Aplicação Web ASP.NET Core.

Não selecione Ativar suporte ao Docker. Você adiciona suporte ao Docker mais tarde no processo.

Captura de tela da tela Informações adicionais ao criar um projeto Web. A opção Ativar suporte ao Docker não está selecionada.

Não selecione Ativar o suporte ao Docker. Você adiciona suporte ao Docker mais tarde no processo.

Captura de tela da tela Informações adicionais ao criar um projeto Web. A opção Ativar suporte ao Docker não está selecionada.

Criar um projeto de API Web

Adicione um projeto à mesma solução e chame-o MyWebAPI. Selecione API como o tipo de projeto e desmarque a caixa de seleção Configurar para HTTPS. Neste design, estamos usando apenas SSL para comunicação com o cliente, não para comunicação entre contêineres na mesma aplicação web. Somente 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 .NET usados pelo Visual Studio são suportados apenas para solicitações de contêiner externo, não para solicitações de contêiner para contêiner.

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

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

    Observação

    Neste 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ê tenha desmarcado essa caixa de seleção. Em geral, os certificados de desenvolvedor .NET usados pelo Visual Studio são suportados apenas para solicitações de contêiner externo, 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, imediatamente 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 de uso em Program.cs para Microsoft.Extensions.Caching.Distributed, Microsoft.Extensions.Caching.StackExchangeRedis.

    using Microsoft.Extensions.Caching.Distributed;
    using Microsoft.Extensions.Caching.StackExchangeRedis;
    
  4. No projeto de API da Web, exclua os WeatherForecast.cs existentes e Controllers/WeatherForecastController.cse adicione um arquivo em Controllers, 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()
     {
        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, você não deve descartar HttpClient após cada solicitação. Para obter práticas recomendadas, consulte Usar HttpClientFactory para implementar solicitações HTTP resilientes.

  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>
    
  3. (apenas ASP.NET 2.x) Agora, no projeto de API Web, adicione código ao controlador "Values" para personalizar a mensagem que a API retorna para a chamada que você adicionou em 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 da Web porque o código usa HTTP para fazer a chamada em vez de HTTPS.

          //app.UseHttpsRedirection();
    

Adicionar suporte ao Docker Compose

  1. No projeto WebFrontEnd, escolha Adicionar suporte do Orquestrador de Containers >. A caixa de diálogo Docker Support Options é exibida.

  2. Escolha Docker Compose.

  3. Escolha o seu SO de destino, por exemplo, Linux.

    Captura de tela da escolha do sistema operacional de destino.

    O Visual Studio cria um ficheiro docker-compose.yml e um ficheiro .dockerignore no nó docker-compose na solução, e esse projeto é apresentado em negrito, indicando que é o projeto de arranque.

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

    O docker-compose.yml aparece da seguinte forma:

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

    A especificada na primeira linha é a versãodo arquivo Docker Compose . Normalmente, não deve alterá-lo, uma vez que é utilizado pelas ferramentas para compreender como interpretar o ficheiro.

    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 estão associados ao ambiente de desenvolvimento e ao controle do código-fonte, não fazendo parte do aplicativo ou serviço que você está desenvolvendo.

    Consulte a seção Ferramentas de Contêiner do painel de saída para obter detalhes dos comandos que estão a ser executados. Você pode ver que a ferramenta de linha de comando chamada docker-compose é usada para configurar e criar os contentores de tempo de execução.

  4. No projeto de API Web, clique novamente com o botão direito do rato no nó do projeto e escolha>adicionar suporte para orquestrador de contentores. Escolha Docker Composee, em seguida, selecione o mesmo SO de destino.

    Observação

    Nesta etapa, o Visual Studio oferecerá a criação de um Dockerfile. Se você fizer isso em um projeto que já tenha suporte ao Docker, será perguntado se 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.

    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 arranque nas propriedades do projeto para o projeto de Docker Compose. No nó do Projeto Docker Compose, clique com o botão direito do mouse para abrir o menu de contexto e escolha Propriedadesou use Alt+Enter. A captura de tela a seguir mostra as propriedades desejadas para a solução usada aqui. Por exemplo, você pode alterar a página que é carregada personalizando a propriedade URL do Serviço .

    Captura de tela das propriedades do projeto Docker Compose.

    Aqui está o que você vê quando iniciado (a versão .NET Core 2.x):

    Captura de ecrã da aplicação Web em execução.

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

  6. Agora suponha que você esteja interessado apenas em ter o depurador anexado ao WebFrontEnd, não o projeto de API da Web. Na barra de menus, pode-se usar a lista suspensa ao lado do botão Iniciar para abrir um menu de opções de depuração; escolha Gerir Configurações de Lançamento do Docker Compose.

    Captura de tela do item de menu Depurar Gerenciar Configurações de Composição.

    A caixa de diálogo Manage Docker Compose Launch Settings é 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 dos serviços do Compose.

    pt-PT: Captura de tela da caixa de diálogo Gerir Configurações de Lançamento do Docker Compose.

    Escolha Novo para criar um novo perfil e nomeie-o Debug WebFrontEnd only. Em seguida, defina o projeto de API Web como Iniciar sem depurar, deixe o projeto WebFrontEnd definido 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 que funciona como 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()
    {
       // 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}";
       }
    }
    

    Observação

    No código do mundo real, você não deve 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 padrão HTTP 80 não é 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 aumenta sempre que o usuário acessa ou atualiza a página.

Adicionar suporte ao Docker Compose

  1. No projeto WebFrontEnd, escolha Adicionar suporte do Container Orchestrator >. A caixa de diálogo Docker Support Options é exibida.

  2. Escolha Docker Compose.

  3. Visual Studio 17.12 e posterior Escolha as opções de andaime para o projeto WebFrontEnd.

    Captura de ecrã mostrando a caixa de diálogo Opções de andaimes de contentores para o projeto WebFrontEnd.

    Visual Studio 17.11 e versões 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 ficheiro docker-compose.yml e um ficheiro .dockerignore no nó docker-compose na solução, e esse projeto é apresentado em negrito, o que mostra que é o projeto de arranque.

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

    O docker-compose.yml aparece da seguinte forma:

     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 estão associados ao ambiente de desenvolvimento e ao controle do código-fonte, não fazendo parte do aplicativo ou serviço que você está desenvolvendo.

    Consulte 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 contentores runtime.

  4. No projeto de API Web, clique novamente com o botão direito do mouse no nó do projeto e escolha Adicionar>Suporte para Orquestrador de Contentores. Escolha Docker Composee, em seguida, selecione o mesmo SO de destino.

    Observação

    Nesta etapa, o Visual Studio oferecerá a criação de um Dockerfile. Se você fizer isso em um projeto que já tenha suporte ao Docker, será perguntado se deseja substituir o Dockerfile existente. Se você fez alterações no Dockerfile que deseja manter, escolha não.

    Visual Studio faz algumas alterações em seu 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 dos 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 mywebapi esteja pronto para receber solicitações da Web. Se você estiver usando o Visual Studio 17.13 ou posterior, poderá usar os recursos de composição do Docker depends_on e healthcheck em docker-compose.yml para fazer com que os projetos comecem na sequência certa e que eles estejam prontos para atender às solicitações quando necessário. Consulte 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/ || 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 estás a utilizar não tiver o curl instalado, adiciona linhas ao estágio base do Dockerfile do MyWebAPI para o instalar. Este passo requer privilégios elevados, mas você pode restaurar os privilégios de usuário normais depois de instalá-lo como mostrado aqui (para as imagens Debian usadas neste exemplo):

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

    Observação

    Se você estiver usando uma distro Linux, como a Alpine, que não suporta 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 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 para depuração que oferece suporte aos recursos de dependência de serviço.

    Com estas alterações, o serviço webfrontend não será iniciado até que mywebapi seja iniciado e gere com sucesso um pedido 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ó Projeto Docker Compose, clique com o botão direito do mouse para abrir o menu de contexto e escolha Propriedadesou use Alt+Enter. Por exemplo, você pode alterar a página que é carregada personalizando a propriedade URL do Serviço .

    Captura de tela das propriedades do projeto Docker Compose.

  8. Pressione F5. Veja o que você vê quando é lançado:

    Captura de ecrã da aplicação Web em execução.

  9. Você pode monitorar os contêineres usando a janela Containers. Se não vir a janela, utilize a caixa de pesquisa, prima Ctrl+K, Ctrl+Oou prima Ctrl+Q. Em de pesquisa de recursos , procure containerse escolha Exibir>Outros Contêineres do Windows> na lista.

  10. Expanda o nó Contentores de Solução e escolha o nó para o seu projeto Docker Compose para visualizar logs combinados no separador Logs desta janela.

    Captura de ecrã a mostrar a visualização do separador Registos na janela Contentores.

    Você também pode selecionar o nó de um contentor individual para visualizar registos, variáveis de ambiente, o sistema de arquivos e outros detalhes.

Configurar perfis de inicialização

  1. Esta solução tem um Azure Cache para Redis, mas não é eficiente reconstruir o container de cache sempre que se inicia 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 menu, pode-se usar a lista pendente ao lado do botão iniciar para abrir um menu com opções de depuração. Selecione Gerir definições de lançamento do Docker Compose.

    Captura de tela do item de menu Depurar Gerenciar Configurações de Composição.

    A caixa de diálogo Manage Docker Compose Launch Settings é 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. Veja Iniciar um subconjunto de serviços de composição.

    Captura de ecrã da caixa de diálogo Gerir definições de lançamento do Docker Compose.

    Escolha Novo para criar um novo perfil e nomeie-o Start Redis. Em seguida, configure o contentor Redis como Iniciar sem depurar, deixe o outro definido como Não iniciare escolha Salvar.

    Captura de ecrã a mostrar a criação do perfil Redis que inicia exclusivamente 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 ecrã mostrando a criação do perfil Serviços que inicia os outros serviços.

    (Opcional) Crie um terceiro perfil Start All para começar tudo. Você pode escolher Iniciar sem depurar no Redis.

  2. Escolha Iniciar Redis do menu pendente na barra de ferramentas principal do Visual Studio. O contêiner Redis é compilado e iniciado sem depuração. Você pode usar a janela contêineres para ver se ela está em execução. Em seguida, escolha Start My Services na lista suspensa e pressione F5 para iniciá-los. Agora você pode manter o contêiner de cache em execução durante muitas 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óximos passos

Veja as opções para implantar seus contêineres de no Azure.

Ver também

Docker Compose

Ferramentas de contêiner