Bem-vindo à aula sobre Redes no Docker! Nesta aula, vamos explorar um dos pilares da orquestração de containers: a rede. Entender como os containers se comunicam entre si e com o mundo externo é essencial para construir aplicações distribuídas, escaláveis e seguras. Vamos começar com uma visão geral dos tipos de rede que o Docker oferece e depois mergulhar em detalhes práticos, como comunicação entre containers, exposição de portas e estratégias de isolamento.

O Docker fornece uma camada de abstração de rede que permite aos containers se conectarem de várias maneiras, dependendo das necessidades da aplicação. Seja em um único host ou em um cluster multi-host (como no Docker Swarm), o Docker simplifica a configuração de rede, mas exige que o desenvolvedor entenda os conceitos para tomar decisões corretas. Vamos abordar cada aspecto com exemplos práticos e boas práticas.

Tipos de rede

O Docker oferece vários drivers de rede, cada um com características específicas. Os principais são: bridge, host, none e overlay. O driver padrão é o bridge, que cria uma rede privada interna no host, permitindo que os containers se comuniquem entre si e com o host, mas com isolamento em relação ao mundo externo.

O driver host remove o isolamento de rede entre o container e o host, usando diretamente a interface de rede do host. Isso pode melhorar o desempenho, mas reduz o isolamento. O driver none desativa completamente a rede, útil para containers que não precisam de conectividade. Já o driver overlay é usado em clusters (Swarm) para permitir comunicação entre containers em diferentes hosts, criando uma rede virtual sobreposta.

Para listar as redes existentes, use o comando docker network ls. Para inspecionar uma rede, use docker network inspect. Vamos ver um exemplo:

docker network ls
# NETWORK ID     NAME      DRIVER    SCOPE
# 1a2b3c4d5e6f   bridge    bridge    local
# 7f8e9d0c1b2a   host      host      local
# 3c4d5e6f7a8b   none      null      local

Para criar uma rede personalizada do tipo bridge, usamos:

docker network create --driver bridge minha-rede

E para conectar um container a essa rede, no momento da criação ou depois:

docker run -d --name meu-container --network minha-rede nginx
# ou
docker network connect minha-rede meu-container

Comunicação entre containers

A comunicação entre containers é um dos principais motivos para usar redes personalizadas. Na rede bridge padrão, os containers podem se comunicar por IP, mas o IP pode mudar quando o container é recriado. Para uma comunicação estável, é melhor usar o nome do container como hostname, o que o Docker resolve automaticamente para o IP correto.

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

docker network create minha-rede
docker run -d --name web --network minha-rede nginx
docker run -it --name teste --network minha-rede alpine sh
# dentro do container teste:
ping web

O comando ping web deve funcionar, pois o Docker fornece um servidor DNS embutido para redes personalizadas. Isso facilita a comunicação entre serviços, como um backend e um banco de dados.

Além disso, é possível expor portas entre containers sem publicá-las no host. Por exemplo, um container backend pode escutar na porta 8080 e o frontend pode acessá-lo via http://backend:8080.

Para verificar a comunicação, você pode usar ferramentas como curl ou ping dentro dos containers, como mostrado acima.

Portas

Para permitir que o mundo externo acesse um container, é necessário publicar 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 publicar a porta 80 do container nginx na porta 8080 do host:

docker run -d -p 8080:80 nginx

Agora, você pode acessar o container via http://localhost:8080. É possível também publicar portas em um intervalo, usar UDP, ou deixar o Docker escolher uma porta aleatória do host com -P (publica todas as portas expostas).

Para ver as portas publicadas de um container, use docker port <container>:

docker port web
# 80/tcp -> 0.0.0.0:8080

É importante notar que a publicação de portas é uma ponte entre o host e o container. Se você não publicar uma porta, o container só será acessível de dentro da rede Docker, o que é útil para serviços internos.

Isolamento

O isolamento de rede é um recurso de segurança crucial. No Docker, ele é alcançado por meio de namespaces e iptables. Cada container tem seu próprio namespace de rede, o que significa que ele tem suas próprias interfaces, rotas e regras de firewall, isolando-o do host e de outros containers.

Por padrão, os containers em uma rede bridge podem se comunicar entre si, mas não com o host (a menos que as portas sejam publicadas). Para um isolamento mais rígido, você pode usar redes none ou configurar políticas de rede, como usar redes separadas para diferentes camadas da aplicação.

Uma boa prática é criar redes separadas para cada componente da aplicação, limitando a comunicação apenas ao necessário. Por exemplo, um banco de dados pode estar em uma rede privada, enquanto o backend está em outra rede que também se conecta à rede do banco, mas o frontend só se conecta ao backend.

Para conectar um container a múltiplas redes, use docker network connect. Para desconectar, use docker network disconnect. Isso permite um controle fino sobre o tráfego.

docker network create backend-net
docker network create frontend-net
docker run -d --name banco --network backend-net mysql
docker run -d --name backend --network backend-net --network frontend-net meu-backend
docker run -d --name frontend --network frontend-net meu-frontend

Nesse cenário, o backend pode acessar o banco e o frontend, mas o frontend não pode acessar o banco diretamente.

Além disso, você pode usar o driver none para desativar completamente a rede, se o container não precisar de conectividade externa, como em tarefas de processamento em lote.

Boas práticas e observações finais

Ao trabalhar com redes no Docker, lembre-se de:

  • Usar redes personalizadas em vez da bridge padrão para obter resolução de DNS por nome.
  • Expor apenas as portas necessárias para o mundo externo, minimizando a superfície de ataque.
  • Isolar componentes críticos em redes separadas para reduzir o risco de acesso não autorizado.
  • Nomear os containers de forma clara para facilitar a comunicação.
  • Usar o comando docker network inspect para diagnosticar problemas de conectividade.

Com essas práticas, você terá uma infraestrutura de rede robusta e segura para suas aplicações Docker.

Referências

Exercícios

  1. Explique a diferença entre os drivers de rede bridge, host e none do Docker, e dê um exemplo de caso de uso para cada um.

    ✓ Resposta: O driver bridge cria uma rede privada interna no host, permitindo comunicação entre containers e com o host, mas com isolamento do mundo externo. É o padrão e é adequado para aplicações que precisam de comunicação entre containers em um único host. O driver host remove o isolamento de rede, usando diretamente a interface do host; é útil para aplicações que exigem alto desempenho de rede ou que precisam usar portas específicas do host. O driver none desativa a rede, ideal para containers que não precisam de conectividade, como tarefas de processamento offline.
  2. Como você cria uma rede personalizada chamada app-net e inicia um container nginx conectado a ela? Escreva os comandos.

    ✓ Resposta:
    docker network create app-net
    docker run -d --name meu-nginx --network app-net nginx
  3. Qual comando você usaria para publicar a porta 8080 do host para a porta 80 de um container? Dê um exemplo completo com docker run.

    ✓ Resposta:
    docker run -d -p 8080:80 nginx
  4. Explique como você pode verificar se um container consegue se comunicar com outro na mesma rede personalizada, usando o nome do container.

    ✓ Resposta: Você pode executar um comando dentro do container, como ping <nome-do-container> ou curl <nome-do-container>:<porta>, desde que ambos estejam na mesma rede. Por exemplo, se os containers web e app estão na rede app-net, dentro de app você pode rodar ping web ou curl http://web:80.
  5. Descreva uma estratégia de isolamento de rede para uma aplicação de três camadas (frontend, backend, banco de dados) usando redes separadas. Inclua os comandos para criar as redes e conectar os containers.

    ✓ Resposta: Crie duas redes: backend-net e frontend-net. O banco de dados fica apenas em backend-net. O backend fica em ambas as redes, para acessar o banco e ser acessado pelo frontend. O frontend fica apenas em frontend-net. Comandos:
    docker network create backend-net
    docker network create frontend-net
    docker run -d --name banco --network backend-net mysql
    docker run -d --name backend --network backend-net --network frontend-net meu-backend
    docker run -d --name frontend --network frontend-net meu-frontend
    Assim, o frontend não consegue acessar o banco diretamente, aumentando a segurança.