Como estabelecer a comunicação entre contêineres e a máquina host

Docker é excelente. Todos sabemos que os contêineres ajudam a simplificar e automatizar o desenvolvimento e a configuração do ambiente de uma aplicação. Vamos explorar como estabelecer a comunicação entre contêineres e de seu interior para a máquina host.
Primeiro, vamos oferecer um pouco de contexto. Para entender como o processo de comunicação funciona, precisamos esclarecer alguns conceitos.
Rede do Docker
Quando você lista as redes depois de uma instalação tradicional do Docker e executa o comando docker network ls...
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
dbb52b9c258f bridge bridge local
0c35ae96fe77 host host local
cb85dfadb376 local_default bridge local
94431f37b012 none null local
...pode obter as redes integradas padrão:
- Bridge
- Host
- None
Nota: como você provavelmente percebeu, há outra chamada local_default, que neste caso é uma rede bridge definida pelo usuário.
Um breve resumo extraído da documentação atual do Docker explica:
Rede bridge: o driver de rede padrão. Se você não especificar um driver, esse é o tipo de rede que está criando. As redes bridge costumam ser usadas quando suas aplicações são executadas em contêineres independentes que precisam se comunicar
Rede host: adiciona um contêiner à pilha de rede do host. Do ponto de vista da rede, não há isolamento entre a máquina host e o contêiner. Por exemplo, se você executar um contêiner com um servidor web na porta 80 usando a rede host, o servidor estará disponível na porta 80 da máquina host.
Rede none: geralmente é conhecida como uma rede específica do contêiner. Um contêiner pode ser conectado a uma rede none. Isso permite a comunicação interna entre contêineres isolados de redes externas. 1
Portanto, a rede responsável pela comunicação entre contêineres é, por padrão, a rede bridge ou as redes bridge definidas pelo usuário. Vamos nos concentrar nesse tipo de rede.
Redes bridge
$ ifconfig bridge0
bridge0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
options=63<RXCSUM,TXCSUM,TSO4,TSO6>
ether 82:17:02:be:ed:00
Configuration:
id 0:0:0:0:0:0 priority 0 hellotime 0 fwddelay 0
maxage 0 holdcnt 0 proto stp maxaddr 100 timeout 1200
root id 0:0:0:0:0:0 priority 0 ifcost 0 port 0
ipfilter disabled flags 0x2
member: en1 flags=3<LEARNING,DISCOVER>
ifmaxaddr 0 port 5 priority 0 path cost 0
member: en2 flags=3<LEARNING,DISCOVER>
ifmaxaddr 0 port 6 priority 0 path cost 0
Address cache:
nd6 options=201<PERFORMNUD,DAD>
media: <unknown type>
status: inactive
Quando você inicia o Docker, uma rede bridge padrão, também chamada bridge0 ou docker0, é criada automaticamente, e os contêineres recém-iniciados se conectam a ela, a menos que seja especificado o contrário. Você também pode criar redes bridge personalizadas definidas pelo usuário. Elas são superiores à rede bridge padrão.
No Docker, uma rede bridge usa uma ponte de software que permite a comunicação entre contêineres conectados à mesma rede bridge, isolando-os dos contêineres que não estão conectados a ela. O driver bridge do Docker instala automaticamente regras na máquina host para que contêineres em diferentes redes bridge não possam se comunicar diretamente.
A comunicação só seria estabelecida se a rede bridge fosse fornecida e as permissões adequadas fossem concedidas nas regras do iptables.
Ping entre contêineres
Para entender a comunicação entre contêineres, vamos criar dois contêineres na mesma rede e verificar se conseguem se enxergar usando um comando ping.
- Crie dois contêineres.
$ docker run -d --name nginx1 -p 8001:80 nginx:latest
$ docker run -d --name nginx2 -p 8002:80 nginx:latest
- Crie uma rede personalizada.
$ docker network create my-custom-net
$ docker network inspect my-custom-net
- Liste as redes. Você deve ver uma nova rede do tipo bridge.
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
dbb52b9c258f bridge bridge local
0c35ae96fe77 host host local
cb85dfadb376 my-custom-net bridge local
94431f37b012 none null local
- Conecte a rede aos contêineres.
docker network connect my-custom-net nginx1
docker network connect my-custom-net nginx2
- Inspecione a rede. Você deve ver os contêineres conectados a ela.
$ docker network inspect my-custom-net
[
{
"Name": "my-custom-net",
"Id": "cb85dfadb376cc7aa020410ee6c839137b6d5dedff7972b9eed0e37ec573b002",
"Created": "2020-04-09T20:26:42.897203Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.19.0.0/16",
"Gateway": "172.19.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
- Antes de tentar fazer ping, instale
iputils-pingno contêiner para que funcione. Para isso, entramos no contêiner chamadonginx1.
$ docker exec -ti nginx1 bash
root@39484307e6ee:/# apt update
root@39484307e6ee:/# apt install iputils-ping
- Você já pode executar um comando ping.
docker exec -ti nginx1 ping nginx2
PING nginx2 (172.18.0.3) 56(84) bytes of data.
64 bytes from nginx2.my-custom-net (172.18.0.3): icmp_seq=1 ttl=64 time=0.224 ms
64 bytes from nginx2.my-custom-net (172.18.0.3): icmp_seq=2 ttl=64 time=0.093 ms
64 bytes from nginx2.my-custom-net (172.18.0.3): icmp_seq=3 ttl=64 time=0.082 ms
64 bytes from nginx2.my-custom-net (172.18.0.3): icmp_seq=4 ttl=64 time=0.081 ms
Como conectar o contêiner a um serviço dentro da máquina host
O Docker permite acessar o host a partir dos contêineres por meio de um DNS especial chamado host.docker.internal.
Você pode executar ping dentro do contêiner nginx1. Receberá uma resposta com o IP atual da sua máquina host
root@39484307e6ee:/# ping host.docker.internal
Em resumo, entendemos melhor como as redes bridge do Docker facilitam a comunicação entre contêineres da mesma rede. Se necessário, podemos conectar os contêineres a qualquer serviço do host usando o nome especial host.docker.internal.
-
"Visão geral das redes," Documentação do Docker. ↩