Redes en Docker
En Docker 0 conectamos el navegador del host con un contenedor mediante un puerto publicado:
host :8080 ── publicación ──→ contenedor :80
Ahora TasteMatch necesita hablar con otro servicio. El recorrido es distinto:
contenedor A ── red Docker ──→ contenedor B
Antes de escribir comandos, separa tres lugares:
- host: nuestro equipo;
- contenedor actual: donde se ejecuta el proceso que hace la conexión;
- otro contenedor: el servicio al que queremos llegar.
localhost siempre empieza por «aquí»
Dentro de un contenedor, localhost se refiere a ese mismo contenedor.
TasteMatch
┌──────────────────────────┐
│ localhost → TasteMatch │
└──────────────────────────┘
Base de datos
┌──────────────────────────┐
│ otro contenedor │
└──────────────────────────┘
Por tanto, TasteMatch no llega a la base de datos usando localhost si la base está en otro contenedor. Tampoco significa automáticamente «el host». Primero debemos identificar desde qué entorno se realiza la conexión.
Qué aporta una red Docker
Una red Docker permite conectar contenedores y controlar qué servicios pueden comunicarse. En una red bridge definida por el usuario, los contenedores pueden localizarse por nombre.
Consulta las redes existentes:
docker network ls
Docker suele disponer de una red llamada bridge. Para nuestras aplicaciones crearemos una red bridge propia: expresa la relación entre sus contenedores y proporciona resolución de nombres adecuada para la práctica.
docker network create tastematch-net
docker network ls
docker network inspect tastematch-net
inspect muestra configuración y, cuando existan, los contenedores conectados. No necesitamos estudiar interfaces Linux ni reglas internas de red.
Primer experimento: servidor y cliente
Inicia Nginx dentro de la red, sin publicar su puerto al host:
docker run -d --name web-interna \
--network tastematch-net \
nginx:alpine
Utiliza una imagen cliente que ya contiene curl:
docker run --rm --name cliente \
--network tastematch-net \
curlimages/curl:8.12.1 \
http://web-interna
La respuesta HTML de Nginx demuestra la comunicación:
cliente
│ http://web-interna:80
▼
tastematch-net
▼
web-interna
No hemos instalado herramientas dentro de Nginx ni publicado -p. El cliente alcanza el puerto interno porque ambos contenedores comparten la red.
Comunicación por nombre y DNS interno
En la red creada por el usuario, Docker proporciona resolución de nombres para los contenedores conectados. web-interna se resuelve a la dirección actual del contenedor correspondiente.
nombre estable para la aplicación
↓
resolución de Docker
↓
dirección del contenedor en esa red
No conviene escribir una IP interna obtenida durante una prueba. Puede cambiar cuando se recrea el contenedor. El nombre expresa mejor qué servicio buscamos.
Puedes comprobar la red desde ambos lados:
docker network inspect tastematch-net
docker inspect web-interna
Busca la sección de redes y confirma que web-interna está conectado a tastematch-net.
Red Docker y publicación resuelven problemas distintos
| Necesidad | Mecanismo |
|---|---|
| Un contenedor llega a otro | Red Docker compartida y puerto del servicio. |
| El host llega a un contenedor | Publicación con -p. |
Publicar un puerto no es requisito para la comunicación interna. Solo publica aquello que realmente necesite una entrada desde el host.
navegador host ── -p 8080:3000 ──→ TasteMatch
│
red Docker interna
│
▼
servicio de datos
El segundo servicio puede permanecer sin puertos publicados si solo lo utiliza TasteMatch.
Aplicación y servicio
Imagina estos contenedores:
tastematch-app
necesita DB_HOST=tastematch-db
│
▼
tastematch-net
│
▼
tastematch-db
escucha en su puerto interno
El hostname correcto es el nombre del servicio en la red, no localhost y no una IP copiada de inspect. La forma de proporcionar DB_HOST se estudiará en la siguiente unidad; aquí importa el recorrido de red.
Conectar un contenedor existente
Crea un segundo servidor inicialmente fuera de nuestra red:
docker run -d --name web-conectable nginx:alpine
Al no indicar --network, el contenedor queda inicialmente en la red bridge predeterminada de Docker, no en tastematch-net.
Conéctalo:
docker network connect tastematch-net web-conectable
docker network inspect tastematch-net
Comprueba desde un cliente efímero:
docker run --rm --network tastematch-net \
curlimages/curl:8.12.1 \
http://web-conectable
Desconéctalo:
docker network disconnect tastematch-net web-conectable
Una nueva petición desde esa red ya no debería localizarlo por ese nombre. Desconectar no elimina el contenedor.
Un contenedor puede participar en varias redes
Crea otra red y conecta web-interna:
docker network create observacion-net
docker network connect observacion-net web-interna
docker inspect web-interna
El contenedor pertenece ahora a dos redes. Eso puede permitirle comunicarse con grupos diferentes, pero no conecta automáticamente entre sí a todos los demás miembros de ambas redes.
Las redes también expresan aislamiento: un contenedor que solo está en observacion-net no obtiene por ello acceso por nombre a servicios exclusivos de tastematch-net.
Retira la conexión adicional:
docker network disconnect observacion-net web-interna
docker network rm observacion-net
Limpieza de la práctica
Primero elimina los contenedores creados:
docker stop web-interna web-conectable
docker rm web-interna web-conectable
Después elimina la red cuando ya no tenga contenedores conectados:
docker network rm tastematch-net
docker network ls
Docker rechazará normalmente eliminar una red que continúa en uso. Inspecciona qué está conectado antes de actuar.
Errores habituales
- Usar
localhostpara otro contenedor: apunta al contenedor actual. - Publicar un puerto para comunicación interna:
-psirve para la entrada desde el host, no sustituye una red compartida. - Copiar una IP interna: usa el nombre del contenedor en una red definida por el usuario.
- Separar los contenedores en redes distintas: comprueba ambos extremos con
inspect. - Confundir el puerto del host y el del servicio: entre contenedores se usa el puerto interno del servicio.
- Eliminar una red sin revisar miembros: inspecciona y desconecta o retira conscientemente los contenedores correspondientes.
Mini reto: servidor y cliente por nombre
Consigue esta arquitectura sin publicar el servidor al host:
cliente-reto ── reto-net ──→ servidor-reto:80
- Crea
reto-net. - Inicia
servidor-retocon Nginx en esa red. - Usa un contenedor efímero
curlimages/curlcomo cliente. - Realiza una petición a
http://servidor-retoy conserva la evidencia. - Explica por qué
http://localhostdesde el cliente no representa al servidor. - Demuestra con
docker network inspectqué contenedores participan. - Crea un segundo servidor Nginx llamado
servidor-extrasin indicar--network: quedará enbridge, fuera dereto-net. - Conecta
servidor-extraareto-netcondocker network connect. - Desde un nuevo cliente efímero en
reto-net, realiza una petición ahttp://servidor-extray comprueba que responde utilizando ese hostname. - Desconecta
servidor-extradereto-netcondocker network disconnect. - Predice qué ocurrirá al repetir la petición desde otro cliente efímero en
reto-net. Después ejecútala y compara el resultado con tu predicción. - Detén y elimina únicamente
servidor-retoyservidor-extra, y eliminareto-net. Crea los clientes efímeros con--rmpara que se eliminen al terminar.
Explica finalmente por qué ningún puerto necesitó publicarse al host. En la siguiente unidad proporcionaremos a una aplicación valores como el hostname del servicio sin reconstruir su imagen.