Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 25

Multi-Service Docker Compose Fundamentals

Recommended resources

Olá! Bem-vindo ao nosso primeiro passo no mundo das aplicações multi-contêiner.

Introdução

Na lição anterior, você justificou com sucesso por que os volumes são a escolha profissional para persistir os dados de um banco de dados. Até agora, gerenciamos cada contêiner — seja uma API ou um banco de dados — como uma entidade isolada, usando comandos como docker run e docker volume create.

No entanto, aplicações do mundo real raramente são um único serviço. Elas são ecossistemas: uma API .NET que se comunica com um banco de dados PostgreSQL, que por sua vez pode interagir com um cache Redis. Gerenciar o ciclo de vida, a rede e a configuração de cada um desses contêineres individualmente com docker run torna-se rapidamente complexo e propenso a erros.

É aqui que entra o Docker Compose. Nesta lição, você aprenderá a usar essa ferramenta poderosa para definir e executar uma aplicação básica com múltiplos serviços a partir de um único arquivo de configuração. Este é o seu primeiro passo para orquestrar aplicações completas, uma habilidade fundamental para o desenvolvimento moderno.


Por que precisamos do Docker Compose?

Imagine uma aplicação .NET composta por uma API, um banco de dados e um frontend. Para executá-la com o Docker, você precisaria:

  1. Criar uma rede customizada para que os contêineres se comuniquem.
  2. Iniciar o contêiner do banco de dados, conectando-o à rede.
  3. Iniciar o contêiner da API, conectando-o à mesma rede e passando as credenciais do banco de dados como variáveis de ambiente.
  4. Iniciar o contêiner do frontend, também na mesma rede.

Cada passo envolveria um comando docker run longo e complexo. O Docker Compose simplifica tudo isso, permitindo que você descreva toda a sua aplicação em um arquivo declarativo.

O vídeo a seguir introduz esse problema e a solução que o Docker Compose oferece de uma forma bem didática.

What is Docker Compose? Multiple Containers & Docker Networking

Para entender o "porquê" por trás do Docker Compose, assista à introdução deste vídeo de Monis Yousuf, "What is Docker Compose?". Ele ilustra perfeitamente a complexidade de gerenciar múltiplos serviços e apresenta o Docker Compose como a solução.

Assista do início até 03:10. Preste atenção no exemplo da aplicação de três camadas (frontend, backend, database) e como a necessidade de orquestração surge naturalmente.


A Anatomia de um Arquivo docker-compose.yml

O coração do Docker Compose é um arquivo de configuração chamado docker-compose.yml. Ele usa a sintaxe YAML para definir os serviços que compõem sua aplicação. Vamos construir um exemplo para uma API .NET e um banco de dados PostgreSQL.

Um arquivo docker-compose.yml tem uma estrutura básica:

version: '3.8' # Especifica a versão da sintaxe do Compose

services:
  # Aqui definiremos nossos serviços (contêineres)

A seção services é onde a mágica acontece. Cada chave sob services é um serviço individual.

Definindo o Serviço da API

Vamos definir nosso primeiro serviço: a API .NET. Dado que você tem um Dockerfile para sua API (como aprendeu no Módulo 2), a definição pode ser assim:

services:
  webapi: # O nome do nosso serviço. Pode ser o que você quiser.
    build:
      context: . # O diretório onde o Dockerfile está localizado (o 'build context')
      dockerfile: MyApi/Dockerfile # O nome do Dockerfile (se não for 'Dockerfile')
    ports:
      - "8080:80" # Mapeia a porta 8080 do host para a porta 80 do contêiner
  • build: Diz ao Compose para construir uma imagem a partir de um Dockerfile. Você especifica o context (o diretório do projeto) e opcionalmente o dockerfile se ele não estiver na raiz do contexto.
  • ports: Funciona exatamente como o flag -p no docker run. Ele expõe a porta do contêiner para a sua máquina host.

Definindo o Serviço do Banco de Dados

Agora, vamos adicionar um banco de dados PostgreSQL. Para isso, não precisamos construir uma imagem, pois podemos usar a imagem oficial do Docker Hub.

services:
  webapi:
    # ... (definição da API acima)

  database: # O nome do nosso serviço de banco de dados
    image: postgres:16-alpine # Usa a imagem 'postgres' versão 16, variante 'alpine'
    environment:
      - POSTGRES_USER=myuser
      - POSTGRES_PASSWORD=mypassword
      - POSTGRES_DB=mydatabase
    ports:
      - "5432:5432" # Mapeia a porta padrão do Postgres para o host
  • image: Diz ao Compose para puxar a imagem especificada de um registro (Docker Hub, por padrão).
  • environment: Permite definir variáveis de ambiente dentro do contêiner, exatamente como o flag --env ou -e no docker run. Isso é essencial para configurar o banco de dados.

O vídeo a seguir, focado em .NET, demonstra de forma prática como adicionar esses serviços (API, PostgreSQL e até um Redis) em um arquivo docker-compose.yml.

Docker Compose with .NET 8, PostgreSQL, and Redis (step by step)

Este vídeo de Milan Jovanović, "Docker Compose with .NET 8, PostgreSQL, and Redis", é extremamente relevante para você. Ele mostra passo a passo como construir um arquivo docker-compose.yml para um ambiente de desenvolvimento .NET.

Assista aos seguintes segmentos: a explicação inicial do serviço da API (02:16 a 03:04), como adicionar o serviço PostgreSQL (03:04 a 05:17) e como adicionar o serviço Redis (08:43 a 09:04). Observe como as chaves build, image, ports e environment são usadas.

Docker Compose Tutorial: Configuration and Architecture
Esta imagem ilustra bem a relação: à esquerda, um arquivo `docker-compose.yml` com a definição de múltiplos serviços (`nginx`, `flask`, `postgres`), e à direita, a arquitetura resultante com os contêineres se comunicando em uma rede interna.

Executando e Conectando os Serviços

Com o arquivo docker-compose.yml pronto no diretório raiz do seu projeto, você pode iniciar toda a aplicação com um único comando:

docker-compose up

ou, para a versão mais recente da CLI do Docker:

docker compose up

Este comando irá:

  1. Ler o arquivo docker-compose.yml.
  2. Criar uma rede padrão para os serviços se comunicarem.
  3. Construir a imagem da webapi (se ainda não existir ou se você usar o flag --build).
  4. Puxar a imagem do postgres.
  5. Iniciar os contêineres para ambos os serviços.

Como a API se conecta ao Banco de Dados?

Este é um ponto crucial e onde a mágica do Compose brilha, algo que seu background em redes vai apreciar.

Quando o docker compose up é executado, o Docker cria uma rede bridge customizada e anexa todos os contêineres definidos no arquivo a essa rede. Dentro dessa rede, o Docker fornece um servidor DNS interno. Esse DNS resolve o nome de um serviço (por exemplo, database) para o endereço IP interno do contêiner correspondente.

Isso significa que, de dentro do contêiner webapi, você pode se conectar ao banco de dados usando database como o nome do host.

Portanto, sua string de conexão no appsettings.json da sua API .NET não seria localhost, mas sim o nome do serviço:

{
  "ConnectionStrings": {
    "DefaultConnection": "Host=database;Port=5432;Database=mydatabase;Username=myuser;Password=mypassword;"
  }
}

O vídeo a seguir explica esse mecanismo de rede e descoberta de serviços de forma excelente.

What is Docker Compose? Multiple Containers & Docker Networking

Para entender "como funciona" a comunicação entre contêineres, assista a este segmento do vídeo de Monis Yousuf. Ele explica o conceito da rede interna e da resolução de nomes por DNS que o Docker Compose configura automaticamente.

Assista da marca de 09:06 a 12:11. Foque na explicação sobre como o nome do serviço se torna o endereço (hostname) dentro da rede do Docker, eliminando a necessidade de expor todas as portas para o host.

Test your understanding!

Você está montando um docker-compose.yml para uma nova aplicação. Ela consiste em dois serviços:

  1. Um serviço de API .NET chamado products-api.
  2. Um serviço de cache Redis chamado products-cache, que usa a imagem redis:latest e escuta na porta 6379 dentro do contêiner.

Sua API precisa se conectar ao Redis. Qual seria o valor do host na string de conexão do Redis dentro da configuração da sua API?

Show answer

O valor do host seria products-cache.

Graças à rede e ao DNS internos do Docker Compose, o contêiner products-api pode encontrar o contêiner products-cache simplesmente usando o nome do serviço como hostname. A string de conexão completa seria algo como products-cache:6379.


Exemplo Completo e Leitura Recomendada

Para consolidar tudo o que vimos, aqui está um exemplo completo de um arquivo docker-compose.yml para uma API .NET e um banco de dados SQL Server, extraído de um artigo muito útil.

version: '3.8'

services:
  # Serviço da aplicação ASP.NET Core
  todo-app:
    build: . # Constrói a imagem a partir do Dockerfile no diretório atual
    ports:
      - "8090:80" # Mapeia a porta 8090 do host para a 80 do contêiner
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      # A string de conexão aponta para o serviço 'mssql-server'
      - ConnectionStrings__DefaultConnection=Server=mssql-server,1433;Initial Catalog=TodoListDb;User ID=SA;Password=YourStrong(!)Password;TrustServerCertificate=true

  # Serviço do banco de dados SQL Server
  mssql-server:
    image: mcr.microsoft.com/mssql/server:2022-latest # Usa a imagem oficial do SQL Server
    environment:
      - ACCEPT_EULA=Y
      - MSSQL_PID=Express
      - SA_PASSWORD=YourStrong(!)Password
    ports:
      - "1433:1433" # Expõe a porta do SQL Server para o host (útil para ferramentas de DB)

Para uma análise detalhada deste arquivo e mais contexto, recomendo fortemente a leitura do artigo a seguir.

Deploying ASP.NET Core Applications with Docker—Part 2

Este artigo da Telerik, 'Deploying ASP.NET Core Applications with Docker—Part 2', oferece um exemplo completo e comentado de um arquivo docker-compose.yml para uma aplicação ASP.NET Core e um banco de dados SQL Server. É uma excelente referência textual.

Leia as seções 'Creating the Docker Compose File' e a análise detalhada do código que se segue. Foque em como cada serviço (mssql-server e todo-app) é definido e como as chaves image, build, environment e ports são utilizadas.


Conclusão

Parabéns! Você aprendeu os fundamentos do Docker Compose e como definir uma aplicação multi-serviços. Esta é uma mudança de paradigma em relação ao gerenciamento de contêineres individuais e a base para a maioria dos fluxos de trabalho de desenvolvimento com Docker.

Principais Pontos:

  • Docker Compose orquestra aplicações multi-contêiner a partir de um único arquivo docker-compose.yml.
  • Cada contêiner é definido como um serviço, podendo ser construído a partir de um Dockerfile (build) ou puxado de um registro (image).
  • Configurações como mapeamento de portas (ports) e variáveis de ambiente (environment) são definidas de forma declarativa para cada serviço.
  • O comando docker compose up inicia toda a stack, criando uma rede interna que permite a comunicação entre os serviços usando seus nomes como hostnames.

Na próxima lição, vamos aprimorar nosso docker-compose.yml. Aprenderemos a integrar formalmente as redes customizadas e os volumes nomeados que você estudou no módulo anterior, tornando nossa aplicação ainda mais robusta e organizada. Também exploraremos outros comandos do ciclo de vida, como docker-compose down.