Modos de Rede Docker e Comunicação entre Containers

Introdução

Quando projetos em grande escala adotam Docker, surge a necessidade de resolver a comunicação entre containers. Para abordar esse problema, é fundamental compreender diversos conceitos de rede. O Docker, como uma das tecnologias de containers mais populares, oferece funcionalidades notáveis como o gerenciamento de imagens. Contudo, o Docker apresenta algumas limitações, especialmente na área de rede. Por isso, é essencial aprofundar o conhecimento sobre as capacidades de rede do Docker para atender exigences mais complexas.

  1. Redes Padrão

Após a instalação do Docker, três redes são criadas automaticamente. É possível visualizá-las utilizando o comando docker network ls.

Antes de explorar os modos de rede, vamos compreeender o significado de cada um deles.

1.1 Rede Bridge

Neste modo, o daemon Docker cria uma ponte virtual ethernet chamada docker0. Os containers recém-criados são automaticamente conectados a essa interface, permitindo a comunicação entre todas as interfaces conectadas à ponte.

Por padrão, o daemon cria um par de interfaces virtuais denominadas veth pair. Uma dessas interfaces é configurada como a interface eth0 do container, enquanto a outra permanece no namespace do sistema host, recebendo nomes como vethxxx. Isso conecta todos os containers à rede interna.

Por exemplo, ao executar um container baseado na imagem busybox denominado container_a:

docker run -di --name container_a busybox
docker exec container_a ip addr

No sistema host, a visualização através de ip addr revelará:

A comparação confirma que o daemon cria o par de interfaces veth pair, atribuindo uma ao container como eth0 e mantendo a outra no namespace do host com nome similar a vethxxx.

Além disso, o daemon atribui um endereço IP e sub-rede do espaço privado da ponte docker0 ao container, definindo o IP do docker0 como gateway padrão do container. É possível instalar o pacote bridge-utils com yum install -y bridge-utils e utilizar o comando brctl show para visualizar informações da ponte.

Para verificar o IP e Gateway de cada container, utiliza-se docker inspect nome_do_container ou docker inspect ID_do_container, onde na seção NetworkSettings encontram-se os detalhes.

Para visualizar todos os containers utilizando a rede bridge, executa-se docker network inspect bridge, onde a seção Containers exibe os nomes dos containers.

Para utilizar o modo bridge, basta especificar o parâmetro --net bridge ou --network bridge ao criar o container. Este é o modo padrão, tornando o parâmetro opcional.

A implementação do modo bridge segue estes passos principais:

  • O Docker Daemon utiliza a tecnologia veth pair para criar um par de interfaces de rede virtuais no host, como vethA e vethB. A característica do veth pair garante que qualquer pacote recebido por uma interface seja transmitido para a outra.
  • O Docker Daemon conecta vethA à ponte docker0 criada pelo daemon, possibilitando que pacotes da rede do host sejam direcionados a vethA.
  • O Docker Daemon adiciona vethB ao namespace do container e a renomeia para eth0. Assim, pacotes enviados para vethA são recebidos imediatamente pela eth0 do container, estabelecendo comunicação entre host e container. Simultaneamente, o container utiliza apenas eth0, garantindo isolamento do ambiente de rede.

1.2 Rede Host

  • O modo host é especificado através dos parâmetros --net host ou --network host ao criar o container.
  • Um container utilizando o modo host pode utilizar diretamente o endereço IP do sistema host para comunicação externa. Se o host possui um IP público, o container também terá acesso a esse IP. As portas de serviços dentro do container podem utilizar as portas do host sem necessidade de NAT adicional.
  • O modo host permite que o container compartilhe a pilha de rede do host, facilitando a comunicação direta entre hosts externos e o container. Porém, há uma desvantagem: a rede do container lacks isolamento.

Por exemplo, ao criar um container no modo host baseado na imagem busybox denominado container_b:

docker run -di --name container_b --net host busybox
docker exec container_b ip addr

A execução de ip addr no sistema host retorna informações praticamente idênticas.

Para visualizar todos os containers no modo host, utiliza-se docker network inspect host, onde a seção Containers exibe os nomes dos containers.

1.3 Rede None

  • O modo none desabilita as funcionalidades de rede, mantendo apanas a interface lo (loopback local, representando 127.0.0.1). Especifica-se através dos parâmetros --net none ou --network none ao criar o container.
  • O modo none não cria nenhum ambiente de rede para o container, que utilizará apenas dispositivos de rede loopback, sem outros recursos de rede. Embora o modo none forneça configuração mínima de rede, a filosofia "menos é mais" permite que desenvolvedores personalizem a rede de diversas maneiras. Isso reflete o design aberto do Docker.

Por exemplo, ao criar um container no modo none baseado na imagem busybox denominado container_c:

docker run -di --name container_c --net none busybox
docker exec container_c ip addr

Para visualizar todos os containers no modo none, utiliza-se docker network inspect none.

1.4 Rede Container

O modo container é um modo especial no Docker. Ao criar um container, especifica-se --net container:nome_do_container ou --network container:ID_do_container para indicar outro container já em execução.

Containers neste modo compartilham uma pilha de rede, permitindo comunicação eficiente através de localhost.

No modo container, o novo container não cria sua própria interface de rede ou configura seu próprio IP. Em vez disso, compartilha IP e faixa de portas com um container especificado. É importante destacar que, embora a rede seja compartilhada, outros recursos como sistema de arquivos e lista de processos permanecem isolados.

Por exemplo, ao criar um container container_d baseado no container container_a:

docker run -di --name container_d --net container:container_a busybox
docker exec container_d ip addr

O resultado de ip addr do container container_a é:

E no sistema host:

Os testes confirmam que o Docker daemon cria apenas um par de interfaces veth pair para conectar o container container_a ao host, enquanto o container container_d utiliza diretamente as informações de rede do container container_a.

Ao parar o container container_a, o container container_d остается apenas com a interface loopback.

Após reiniciar o container container_a e o container container_d, as informações de rede são restauradas.

  1. Redes Personalizadas

Embora as redes padrão do Docker sejam simples de utilizar, para garantir a segurança das aplicações nos containers, é recomendado utilizar redes personalizadas no desenvolvimento. Isso também permite a resolução DNS automática entre nomes de containers e endereços IP.

A partir da versão 1.10 do Docker, o daemon implementa um servidor DNS interno, permitindo que containers se comuniquem diretamente pelo nome. Para isso, basta utilizar o parâmetro --name ao criar o container.

Porém, o Docker DNS possui uma limitação: funciona apenas em redes user-defined. Ou seja, a rede bridge padrão não suporta DNS, tornando necessário criar redes personalizadas.

2.1 Criar uma Rede

O comando docker network create cria redes personalizadas:

docker network create --help

O comando docker network create suporta o parâmetro --driver para especificar o tipo de rede, sendo bridge o padrão.

Para criar uma rede personalizada baseada no modo bridge denominada minha_rede:

docker network create minha_rede

Para visualizar as redes disponíveis:

docker network ls

Para criar um container utilizando a rede personalizada:

docker run -di --name container_e --net minha_rede busybox

Para verificar as informações de rede do container, utiliza-se docker inspect na seção NetworkSettings.

2.2 Conectar uma Rede

O comando docker network connect nome_da_rede nome_do_container conecta um container a uma nova rede:

docker network connect bridge container_e

Ao inspecionar novamente o container, observa-se a adição da rede bridge padrão.

2.3 Desconectar uma Rede

O comando docker network disconnect nome_da_rede nome_do_container remove a conexão de rede:

docker network disconnect minha_rede container_e

Ao inspecionar novamente o container, resta apenas a rede bridge padrão.

2.4 Remover uma Rede

O comando docker network rm nome_da_rede remove uma rede personalizada, retornando o nome da rede em caso de sucesso:

docker network rm minha_rede

Importante: Se houver containers criados utilizando uma rede personalizada, não será possível removê-la.

  1. Comunicação entre Containers

Agora, aplicando os conceitos aprendidos, vamos implementar a comunicação entre containers. É fundamental entender que, para a comunicação entre containers, ambos devem possuir interfaces de rede na mesma rede.

Inicialmente, criamos dois containers utilizando a rede bridge padrão:

docker run -di --name container_f busybox
docker run -di --name container_g busybox

Para visualizar os IPs dos dois containers:

docker network inspect bridge

Em seguida, testamos a comunicação entre os dois containers:

docker exec container_f ping -c 3 IP_DO_CONTAINER_G

O teste confirma que containers na mesma rede podem se comunicar. No entanto, os endereços IP podem não ser固定的, podendo mudar. Isso exigiria atualizações nos endereços IP em todas as comunicações. É possível utilizar nomes de containers para comunicação?

docker exec container_f ping -c 3 container_g

O teste revela que a comunicação por nome não funciona na rede bridge padrão.

A partir da versão 1.10 do Docker, o daemon implementa um servidor DNS interno que permite comunicação direta pelos nomes dos containers. Isso é alcançado simplesmente utilizando o parâmetro --name ao criar o container.

Porém, o Docker DNS possui uma limitação: funciona apenas em redes user-defined. A rede bridge padrão não suporta DNS, tornando necessário criar redes personalizadas.

Criamos uma rede personalizada baseada em bridge denominada rede_personalizada e dois containers utilizando essa rede:

docker network create rede_personalizada
docker run -di --name container_h --net rede_personalizada busybox
docker run -di --name container_i --net rede_personalizada busybox

Para verificar os IPs dos dois containers:

docker network inspect rede_personalizada

Testamos a comunicação entre os containers utilizando IP e nome:

docker exec container_h ping -c 3 IP_DO_CONTAINER_I
docker exec container_h ping -c 3 container_i

Os testes confirmam que containers na mesma rede personalizada podem se comunicar, sendo possível utilizar nomes de containers.

E se to necessário que um container na rede bridge se comunique com um container na rede_personalizada? A solução é simples: conectar o container da rede bridge à nova rede.

docker network connect rede_personalizada container_f

Com o conhecimento de comunicação entre containers, é possível praticar a implementação de clusters de aplicações utilizando múltiplos containers. O próximo passo é aprender sobre Docker Compose e Docker Swarm.

Publicado em 7-23 01:24