Nesta aula, vamos explorar um dos pilares do Docker: as redes. Entender como os containers se comunicam, como expor serviços para o mundo externo e como isolar ambientes é essencial para projetar aplicações robustas e seguras. Docker oferece um modelo de rede flexível e poderoso, que permite desde a comunicação simples entre dois containers até a criação de redes overlay complexas para clusters. Vamos mergulhar nos conceitos fundamentais, nos tipos de rede e nas práticas recomendadas.

Dominar as redes no Docker é como aprender o sistema circulatório de um organismo: tudo depende de como as informações fluem. Você verá que, com alguns comandos e flags, é possível conectar, desconectar, expor e isolar containers de forma elegante. Ao final desta aula, você será capaz de projetar a arquitetura de rede das suas aplicações containerizadas com confiança.

Tipos de rede

O Docker fornece, por padrão, cinco drivers de rede: bridge, host, none, overlay e macvlan. Cada um atende a diferentes necessidades de comunicação e isolamento. O driver mais comum é o bridge, que cria uma rede privada interna no host, permitindo que os containers conversem entre si e com o host, mas não com o mundo externo, a menos que seja feito o mapeamento de portas.

O driver host remove o isolamento de rede entre o container e o host, fazendo com que o container compartilhe a pilha de rede do host. Isso pode ser útil para desempenho, mas reduz o isolamento. O driver none desabilita completamente a rede, deixando o container isolado. Já o overlay é usado em clusters Docker Swarm para conectar containers em diferentes hosts. Por fim, o macvlan permite atribuir um endereço MAC e IP real ao container, integrando-o diretamente na rede física.

Para listar as redes existentes, use docker network ls. Para inspecionar uma rede específica, docker network inspect. Vamos ver um exemplo de criação de uma rede bridge personalizada:

docker network create --driver bridge minha-rede

Você pode ver os detalhes da rede com:

docker network inspect minha-rede

Além disso, é possível conectar um container a uma rede no momento da execução com a flag --network. Por exemplo:

docker run -d --name meu-nginx --network minha-rede nginx

Comunicação entre containers

Quando os containers estão na mesma rede bridge, eles podem se comunicar usando o nome do container como hostname. O Docker possui um DNS interno que resolve os nomes dos containers para seus endereços IP na rede. Isso facilita a comunicação entre serviços, pois você não precisa se preocupar com IPs dinâmicos.

Por exemplo, se você tiver um container chamado db e outro chamado app na mesma rede, o app pode acessar o db usando o hostname db. Isso é possível porque o Docker cria automaticamente um link de rede quando você conecta containers à mesma rede.

Vamos criar dois containers na mesma rede e testar a comunicação:

docker run -d --name db --network minha-rede -e POSTGRES_PASSWORD=secret postgres
 docker run -d --name app --network minha-rede -p 8080:80 nginx
 docker exec app ping db

Se o ping funcionar, a comunicação está OK. Note que usamos o nome do container db como hostname. Isso é muito mais prático do que usar IPs. Além disso, podemos usar o docker network connect para conectar um container a uma rede depois que ele já foi criado.

Portas

Para que um container seja acessível a partir do host ou do mundo externo, é necessário publicar suas portas. Isso é feito com a flag -p (ou --publish) no comando docker run. A sintaxe é -p <porta-do-host>:<porta-do-container>. Por exemplo, para mapear a porta 80 do container nginx para a porta 8080 do host, usamos:

docker run -d --name meu-nginx -p 8080:80 nginx

Você pode publicar múltiplas portas adicionando mais flags -p. Além disso, é possível usar a flag -P (maiúscula) para publicar todas as portas expostas no Dockerfile, mas isso não é recomendado em produção, pois pode expor portas desnecessárias.

Para verificar as portas publicadas de um container, use docker port <container> ou docker ps. Também é possível especificar o endereço IP do host para vincular a porta apenas a uma interface específica, por exemplo -p 127.0.0.1:8080:80 para acessar apenas localmente.

É importante lembrar que o mapeamento de portas é uma forma de expor o container, mas também aumenta a superfície de ataque. Portanto, publique apenas as portas necessárias e use firewalls para restringir o acesso.

Isolamento

O isolamento de rede é um aspecto crucial da segurança. O Docker oferece várias maneiras de isolar containers, desde o driver none (sem rede) até a criação de redes privadas que não são acessíveis externamente. Cada rede bridge criada por você é isolada das outras redes bridge por padrão, a menos que você conecte explicitamente um container a múltiplas redes.

Para isolar completamente um container, você pode usar a rede none:

docker run -d --name isolado --network none alpine sleep 3600

Esse container não terá interface de rede, exceto a loopback, e não poderá se comunicar com o host ou com outros containers. Isso é útil para processamento de dados sensíveis que não precisam de rede.

Outra prática de isolamento é usar redes separadas para diferentes camadas da aplicação. Por exemplo, você pode ter uma rede para o banco de dados e outra para a aplicação, e conectar apenas os containers necessários. Isso reduz o risco de um container comprometido acessar outros serviços.

Para verificar o isolamento, você pode inspecionar as redes e ver quais containers estão conectados. Lembre-se de que a comunicação entre redes só é possível se um container estiver conectado a ambas as redes, o que pode ser desejado em alguns casos, como um gateway.

Boas práticas e observações finais

Ao trabalhar com redes Docker, siga estas boas práticas: sempre crie redes personalizadas para suas aplicações, em vez de usar a rede padrão bridge; use nomes de containers como hostnames para facilitar a comunicação; evite publicar portas desnecessárias; utilize redes separadas para isolar ambientes; e, em produção, considere o uso de redes overlay com Docker Swarm ou Kubernetes para escalar horizontalmente.

Além disso, lembre-se de que a configuração de rede pode ser feita de forma declarativa com Docker Compose, o que facilita a gestão de múltiplos containers. No Compose, você define redes no arquivo YAML e conecta os serviços a elas.

Referências

Exercícios

  1. Qual é a diferença entre os drivers de rede bridge e host? Dê um exemplo de uso para cada um.

    ✓ Resposta: O driver bridge cria uma rede privada interna no host, isolando os containers do mundo externo, mas permitindo comunicação entre eles e com o host. O driver host remove o isolamento, compartilhando a pilha de rede do host, o que pode ser útil para desempenho máximo, como em aplicações que exigem baixa latência.
  2. Como você faria para dois containers se comunicarem usando seus nomes em vez de IPs?

    ✓ Resposta: Basta conectá-los à mesma rede bridge personalizada. O Docker fornece um DNS interno que resolve os nomes dos containers para seus IPs na rede. Por exemplo: docker network create minha-rede e depois docker run --network minha-rede --name app1 ... e docker run --network minha-rede --name app2 .... O container app1 pode acessar app2 usando o hostname app2.
  3. Explique como publicar uma porta de um container para o host. Dê um exemplo com a flag -p.

    ✓ Resposta: Para publicar uma porta, use a flag -p com a sintaxe -p porta_do_host:porta_do_container. Exemplo: docker run -d -p 8080:80 nginx mapeia a porta 80 do container para a porta 8080 do host, permitindo acessar o nginx em http://localhost:8080.
  4. Como você criaria um container sem nenhuma interface de rede? Qual driver usar?

    ✓ Resposta: Use o driver none. Exemplo: docker run -d --network none --name isolado alpine sleep 3600. Esse container terá apenas a interface loopback e não poderá se comunicar com o host ou outros containers.
  5. Qual é a importância de usar redes personalizadas em vez da rede bridge padrão? Cite dois benefícios.

    ✓ Resposta: Usar redes personalizadas oferece melhor isolamento e controle. Benefícios: 1) DNS embutido para resolução de nomes entre containers, facilitando a comunicação. 2) Isolamento entre redes, permitindo segmentar a aplicação em camadas e reduzir riscos de segurança.