¡Hola developer 👋🏻!
Si estás aprendiendo Docker 🐳, probablemente ya te hayas dado cuenta de que el networking es una de esas partes que cuesta encajar al principio:
¿cómo se hablan los contenedores?, ¿qué pasa con los puertos?, ¿qué redes crea Docker por defecto?
En este artículo te dejo un resumen claro y directo para entender cómo funcionan las redes en Docker y cuándo usar cada una.
🎥 Además, si prefieres verlo con ejemplos reales, tienes un vídeo en mi canal de YouTube donde lo explico paso a paso y sin teoría innecesaria. He incrustado el vídeo justo aquí para que puedas seguirlo mientras lees o volver a él cuando quieras.
Port mapping
Normalmente, cuando comienzas a jugar con Docker, lo primero que usas es lo que se conoce como port mapping. El objetivo de esta característica es poder acceder a la aplicación que está hospedada en un contenedor de Docker desde tu máquina local. Para ello, lo que hacemos es mapear un puerto de nuestra máquina con el puerto por el que está a la escucha el contenedor. Esto se consigue de la siguiente manera:
docker run -d --publish 9090:80 nginxEl comando –publish (normalmente acortado a -p) es el que nos permite crear esa asociación entre mi puerto local (9090) y el puerto que nos interese del contenedor (80). En este caso, para acceder simplemente necesitaríamos utilizar un navegador web y acudir a la dirección http://localhost:9090
Por otro lado, si la imagen que estamos utilizando para crear nuestro contenedor ha hecho un buen uso de las instrucciones EXPOSE, podemos usar el comando de esta otra manera, para que Docker se encargue de elegir por nosotros puertos aleatorios en nuestra máquina que mapear con aquellos especificados en el Dockerfile:
docker run -d --publish-all hello-worldEsto está bien cuando yo, Gisela, quiero hablar con uno de los contenedores que se está ejecutando en mi máquina a nivel de desarrollo pero ¿y si quiero que los contenedores hablen entre sí?
Docker y el networking
Si seguiste los artículos anteriores que escribí sobre Docker, ya sabes más que de sobra que las aplicaciones se ejecutan dentro de contenedores, de manera independiente unas de otras. Sin embargo, estas aplicaciones necesitan comunicarse entre ellas, en la mayoría de las ocasiones, como puede ser el front end con el back end, como bases de datos, APIs, etcétera. En este sentido, Docker tiene una forma súper sencilla de lidiar con las redes ¿Y si te dijera que has estado trabajando con redes en Docker desde el minuto uno sin tu saberlo? Pues así de fácil es.
A modo de culturilla, la parte de networking de Docker está basada en una arquitectura llamada Container Network Model (CNM). Esta está implementada en una librería llamada libnetwork, que forma parte de Docker Engine. Por otro lado, cada SO que soporta Docker Engine tiene un conjunto de drivers que permiten a libnetwork crear la configuración de la red que corresponda en cada sistema operativo por nosotros. De manera nativa, estos traen consigo cuatro tipos:
- Bridge/NAT: Se utiliza cuando tienes un solo host de Docker y neesitas que tus contenedores hablen entre sí.
- Overlay: está disponible cuando trabajamos en un formato clúster, es decir que tenemos más de un host de Docker trabajando como uno solo, por lo que necesito que los contenedores puedan hablar entre sí, independientemente de dónde les haya tocado ejecutarse dentro del clúster.
- None: este driver es la forma de decir a Docker que para un contenedor/es en concreto no queremos que estén conectados a ninguna red. Lo que hace en este caso es no asignarle una tarjeta de red y listo 🙂
- Macvlan: esta opción se utiliza en el caso de querer conectar los dos mundos: el dockerizado y el sin dockerizar. Se trataría de un entorno híbrido que hace que mis contenedores tengan asignadas una IP dentro de la red donde está mi host/s.
También existen plugins de terceros que extienden estas opciones, como por ejemplo Weave Net.
Por defecto, todas las instalaciones de Docker vienen con tres configuraciones creadas por defecto. Para comprobarlo puedes utilizar el siguiente comando:
docker network lsEste te devolverá lo siguiente:
PS C:\dev> docker network ls
NETWORK ID NAME DRIVER SCOPE
d4cd8b5ba890 bridge bridge local
b326b2ae67bd host host local
179faac8beb7 none null localVeamos cada una de ellas con detalle.
Bridge
Este tipo es el más simple que ofrece Docker y es el que has estado usando todo este tiempo, sin tú saberlo. Se utiliza cuando sólo existe un host y solo es necesario comunicar los contenedores que viven en él entre ellos. En Linux se crean con el driver llamado bridge mientras que en Windows se utiliza el llamado nat, pero el funcionamiento es el mismo. Esta red es la utilizada por defecto, salvo que utilices el parámetro –network, que veremos después. Para comprobarlo, vamos a crear dos contenedores:
docker run -dit --name container-1 alpine ash
docker run -dit --name container-2 alpine ashSi inspeccionamos la red bridge, veremos que existe un apartado llamado Containers donde aparecen los dos que tenemos ejecutándose:
docker network inspect bridge
Como ves, ahí están tus dos contendores, con las IPs 172.17.0.3 y 172.17.0.2, lo cual nos indica que están dentro del mismo rango y podrían comunicarse entre ellos. Pruebas a hacer un ping desde el container-1 al container-2:
docker attach container-1
ping -c 2 172.17.0.3y verás que el resultado es satisfactorio:
/ # ping -c 2 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.723 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.069 ms
--- 172.17.0.3 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.069/0.396/0.723 msSin embargo, cada vez que cree este contendor esta IP puede ser diferente, ya que de ninguna forma estas IPs se reservan a mis contenedores. Esto puede suponer un problema si varios contenedores se comunican entre sí, y quiero añadir esta información como parte de mi aplicación. Es por ello que lo que molaría es que pudieramos comunicarnos con los contenedores a través de su nombre, en lugar de la IP, y es otra de las características que Docker nos brinda. Sin embargo, en esta red bridge generada por defecto no es posible usar este mecanismo. De hecho, si lo intentas verás que los contenedores en esta red no se reconocen por su nombre:
/ # ping -c 2 container-2
ping: bad address 'container-2'Para ello debemos crearnos nuestras propias redes.
Crear tu propia red
Para crea una red dentro de Docker es súper sencillo, solamente debes utilizar el siguiente comando:
docker network create returngis-netUna vez creada, si vuelves a listar las redes disponibles en tu host verás que tienes una nueva, llamada returngis-net que utiliza el driver bridge.
PS C:\dev> docker network ls
NETWORK ID NAME DRIVER SCOPE
d4cd8b5ba890 bridge bridge local
b326b2ae67bd host host local
179faac8beb7 none null local
0a8a9486bff6 returngis-net bridge localPara probar la comunicación a través del nombre vamos a crear dos contenedores dentro de la misma, utilizando el parámetro –network:
docker container run -dit --name container-a --network returngis-net alpine ash
docker container run -dit --name container-b --network returngis-net alpine ash
Si ahora intentamos hacer ping desde el contenedor b al contenedor a:
docker attach container-b
ping -c 3 container-aVeremos que en esta ocasión el resultado es positivo:
PS C:\dev> docker attach container-b
/ # ping -c 3 container-a
PING container-a (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.131 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.106 ms
64 bytes from 172.18.0.2: seq=2 ttl=64 time=0.118 ms
--- container-a ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.106/0.118/0.131 ms
/ #Esto es así porque en este escenario Docker utiliza un servidor DNS configurado por cada contenedor que les permite resolver el nombre del resto con los que comparta red.
Conectar un contenedor a dos redes
Una duda que puede surgirte a raíz de este ejemplo es ¿puede un contenedor conectarse a más de un red? Pues la respuesta es que sí 🙂 y también es muy fácil de hacerlo. Sin embargo, durante la creación solo es posible conectarse a una única red. Para conectarse a una segunda, tercera, etcétera necesitas hacerlo a posteriori utilizar el comando network connect:
docker network connect returngis-net container-1Si has ido ejecutando todos los ejemplos, ahora el container-1, que originalmente solo estaba en la red por defecto, llamada bridge, ahora también pertenece a la red llamada returngis-net, por lo que podría comunicarse tanto con container-a como con container-b:
docker attach container-1
ping -c 2 container-a
ping -c 2 container-bHost
Otra de las redes que aparece en el listado de tu máquina local era la llamada host. Lo que hace esta red es eliminar el aislamiento entre el host y los contenedores, por lo que un contenedor no recibe su propia IP, como vimos en los ejemplos anteriores, sino que utiliza la del host. Por lo tanto, si ejecutas un contenedor asociado a esta red escuchando por el puerto 80, la aplicación del contenedor estará disponible a través del puerto 80 de la IP del host. Este modo suele ser útil cuando necesitas mejorar el rendimiento de tu aplicación. Sólo funciona en Linux y no está soportado en Docker Desktop, debido a que estamos trabajando con una máquina virtual.
docker run --rm -d --network host --name my_nginx nginx
Para comprobar que funciona deberías de acceder a http://localhost:80.
None
En el caso de que no quisieramos que un contenedor tenga una red asignada, utilizaremos –network none como en el siguiente ejemplo:
#Disable the network for a container
docker run --rm -dit --network none --name no-net-alpine alpine ash
Si ahora revisas las tarjetas de red que tiene asignado este contenedor comprobarás que solo aparece la interfaz loopback, que es para que el contenedor pueda hablar consigo mismo, pero nada más.
PS C:\dev> docker exec no-net-alpine ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN qlen 1000
link/sit 0.0.0.0 brd 0.0.0.0Overlay
Para el tipo overlay, necesitas trabajar en modo clúster. Si intentas crear una red de este tipo, sin estar en este modo, obtendrás el siguiente mensaje:
PS C:\dev> docker network create --driver overlay multihost-net
Error response from daemon: This node is not a swarm manager. Use "docker swarm init" or "docker swarm join" to connect this node to swarm and try again.Sin embargo, la experiencia para ti sería la misma: la comunicación entre los diferentes contenedores podrá hacerse tanto a través de su IP como de su nombre, actuando como una única red, aunque haya diferentes máquinas ejecutando nuestros contenedores.
Macvlan
Como decía al inicio, existe un cuarto modo llamado macvlan que nos permite asignar IPs de una red a cada uno de los contenedores que viven en nuestro host. Sin embargo, para que este tipo de redes funcione necesitamos que la tarjeta de red del host esté en modo promiscuo, lo cual significa que necesita estar a la escucha de todos los paquetes que viajan por dicha red, simplemente para poder reconocer aquellos que potencialmente pueden ser para uno de sus contendores. Este modo muchas veces no está permitido en muchas organizaciones. La forma de configurar una red de este tipo sería así:
docker network create -d macvlan \
--subnet=172.16.86.0/24 \
--gateway=172.16.86.1 \
-o parent=eth0 \
my-macvlan-netCon ello conseguiríamos que los contenedores que estén dentro de esta puedan ser alcanzados por otras aplicaciones que vivan fuera de Docker.
¡Saludos!
Si quieres seguir aprendiendo sobre contenedores, tienes una lista de vídeos en mi canal de YouTube 👇🏻
¿Qué son los contenedores? Docker desde cero para principiantes
Docker CLI: Comandos esenciales que todo developer debe conocer
Domina los Dockerfiles: De lo básico a multi-stage
4 formas de crear un Dockerfile sin empezar desde cero
¿No entiendes las redes en Docker? Mira este vídeo
Cómo NO perder tus datos en Docker: Guía definitiva
Deja de configurar contenedores a mano: Aprende Docker Compose 🐙
Cómo subir tus imágenes a Docker Hub (Guía paso a paso)
Publica y automatiza tus imágenes de Docker con GitHub


Hola,
Una consulta sobre redes en Docker. Tengo montado un reverse proxy con nginx y letscript sobre la red Bridge (entiendo que es lo normal) y quiero montar un servicio Moodle en otro contenedor. El problema surge cuando Moodle o Nextcloud, por ejemplo, intentan conectar contra la base de datos mariadb que está en otro contenedor. Si les paso por variables de entorno la ip de dentro de la red Bridge, van perfect, pero claro, al reiniciar.. las ips que docker asigna a los contenedores cambian y me deja sin servicios. ¿Alguna sugerencia? Que ya he intentado casi de todo: si monto con docker-compose moodle y mariadb (usando la imagen de bitnami) sobre una red distinta, en local me funciona, pero nginx no me deja acceder desde fuera, cambiando en la variable de entorno por las IPs externas… 🙂
Solucionado! El problema es que debía crear primero el proxy sobre una red propia y luego agregarlo a la bridged. Posteriormente todos los demás contenedores a la red propia y .. FUNCIONA! Gracias por los vídeos!