Cómo establecer la comunicación entre contenedores y la máquina anfitriona

Docker es excelente. Todos sabemos que los contenedores ayudan a simplificar y automatizar el desarrollo y la configuración del entorno de una aplicación. Exploremos cómo establecer la comunicación entre contenedores y desde su interior hacia la máquina anfitriona.
Primero, daremos un poco de contexto. Para comprender cómo funciona el proceso de comunicación, necesitamos aclarar algunos conceptos.
Red de Docker
Cuando enumeras las redes después de una instalación tradicional de Docker y ejecutas el 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
...puedes obtener las redes integradas predeterminadas:
- Bridge
- Host
- None
Nota: como probablemente notaste, hay otra llamada local_default, que en este caso es una red bridge definida por el usuario.
Un breve resumen extraído de la documentación actual de Docker explica:
Red bridge: el controlador de red predeterminado. Si no especificas un controlador, este es el tipo de red que estás creando. Las redes bridge suelen usarse cuando tus aplicaciones se ejecutan en contenedores independientes que necesitan comunicarse
Red host: agrega un contenedor a la pila de red de la máquina anfitriona. Desde la perspectiva de la red, no existe aislamiento entre la máquina anfitriona y el contenedor. Por ejemplo, si ejecutas un contenedor con un servidor web en el puerto 80 usando la red host, el servidor web estará disponible en el puerto 80 de la máquina anfitriona.
Red none: suele conocerse como una red específica del contenedor. Un contenedor puede conectarse a una red none. Esto permite la comunicación interna entre contenedores aislados de redes externas. 1
Por lo tanto, la red encargada de la comunicación entre contenedores es, de manera predeterminada, la red bridge o las redes bridge definidas por el usuario. Concentrémonos en este tipo de red.
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
Cuando inicias Docker, se crea automáticamente una red bridge predeterminada, también llamada bridge0 o docker0, y los contenedores recién iniciados se conectan a ella, salvo que se especifique lo contrario. También puedes crear redes bridge personalizadas definidas por el usuario. Estas son superiores a la red bridge predeterminada.
En Docker, una red bridge usa un puente de software que permite comunicarse a los contenedores conectados a la misma red bridge, mientras los aísla de los contenedores que no están conectados a ella. El controlador bridge de Docker instala automáticamente reglas en la máquina anfitriona para que los contenedores de distintas redes bridge no puedan comunicarse directamente entre sí.
La comunicación solo se establecería si se proporciona la red bridge y se otorgan los permisos adecuados en las reglas de iptables.
Ping entre contenedores
Para comprender la comunicación entre contenedores, creemos dos contenedores en la misma red y comprobemos que puedan verse mediante un comando ping.
- Crea dos contenedores.
$ docker run -d --name nginx1 -p 8001:80 nginx:latest
$ docker run -d --name nginx2 -p 8002:80 nginx:latest
- Crea una red personalizada.
$ docker network create my-custom-net
$ docker network inspect my-custom-net
- Enumera las redes. Deberías ver una nueva red de 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
- Conecta la red con los contenedores.
docker network connect my-custom-net nginx1
docker network connect my-custom-net nginx2
- Inspecciona la red. Deberías ver los contenedores conectados a ella.
$ 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 intentar hacer ping, debes instalar
iputils-pingen el contenedor para que funcione. Para eso, entramos en el contenedor llamadonginx1.
$ docker exec -ti nginx1 bash
root@39484307e6ee:/# apt update
root@39484307e6ee:/# apt install iputils-ping
- Ya puedes ejecutar un 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
Cómo conectar el contenedor a un servicio dentro de la máquina anfitriona
Docker permite acceder a la máquina anfitriona desde los contenedores mediante un DNS especial llamado host.docker.internal.
Puedes ejecutar ping dentro del contenedor nginx1. Recibirás una respuesta con la IP actual de tu máquina anfitriona
root@39484307e6ee:/# ping host.docker.internal
En resumen, logramos comprender mejor cómo las redes bridge de Docker facilitan la comunicación entre contenedores de la misma red. Si es necesario, podemos conectar nuestros contenedores a cualquier servicio de la máquina anfitriona mediante el nombre especial host.docker.internal.
-
"Descripción general de las redes," Documentación de Docker. ↩