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.
- 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 pairpara criar um par de interfaces de rede virtuais no host, comovethAevethB. A característica doveth pairgarante que qualquer pacote recebido por uma interface seja transmitido para a outra. - O Docker Daemon conecta
vethAà pontedocker0criada pelo daemon, possibilitando que pacotes da rede do host sejam direcionados avethA. - O Docker Daemon adiciona
vethBao namespace do container e a renomeia paraeth0. Assim, pacotes enviados paravethAsão recebidos imediatamente pelaeth0do container, estabelecendo comunicação entre host e container. Simultaneamente, o container utiliza apenaseth0, garantindo isolamento do ambiente de rede.
1.2 Rede Host
- O modo host é especificado através dos parâmetros
--net hostou--network hostao 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, representando127.0.0.1). Especifica-se através dos parâmetros--net noneou--network noneao 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.
- 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.
- 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.