Solución de problemas de redes en Docker: Resolviendo problemas de conectividad de manera efectiva

Soluciona problemas de redes en Docker con DNS de contenedores, redes definidas por el usuario, publicación de puertos, acceso al host, DNS y cortafuegos.

Solución de problemas de redes en Docker: Resolviendo problemas de conectividad de manera efectiva

Los problemas de redes en Docker son mucho más fáciles de resolver cuando nombras la dirección de la conexión fallida. "El contenedor no puede conectarse" es demasiado vago. ¿El host intenta alcanzar un contenedor? ¿Un contenedor intenta alcanzar a otro? ¿Un contenedor intenta llegar a internet? ¿El tráfico entra desde otra máquina? Cada ruta utiliza un comportamiento diferente de Docker.

Empieza por escribir la ruta en lenguaje sencillo:

navegador en host -> localhost:8080 -> puerto del contenedor 80
contenedor api -> contenedor db -> puerto 5432
contenedor worker -> internet público -> api.example.com:443
portátil remoto -> IP pública del servidor -> puerto publicado del contenedor

Una vez que conozcas la ruta, puedes probar cada salto en lugar de cambiar redes al azar.

Conoce los modos básicos de red de Docker

La mayoría de las configuraciones de Docker en un solo host utilizan redes puente. Docker crea una red virtual en el host, asigna direcciones IP privadas a los contenedores y puede publicar puertos seleccionados de los contenedores en el host.

La red bridge predeterminada funciona, pero las redes puente definidas por el usuario son mejores para las aplicaciones porque proporcionan DNS integrado por nombre de contenedor. Eso significa que un contenedor api puede alcanzar un contenedor db en db:5432 si ambos están conectados a la misma red definida por el usuario.

Crea una así:

docker network create appnet
docker run -d --name db --network appnet postgres:16
docker run -d --name api --network appnet my-api

Existen otros modos. La red host comparte el espacio de nombres de red del host y elimina el comportamiento normal de publicación de puertos; es útil en algunos casos de Linux pero reduce el aislamiento. none le da al contenedor ninguna red. overlay es para redes Docker Swarm de múltiples hosts. Compose crea redes definidas por el usuario para los proyectos automáticamente a menos que configures lo contrario.

"Red no encontrada"

Este error generalmente significa que el nombre de la red es incorrecto o que la red existe en un contexto diferente al que espera el comando.

Verifica las redes disponibles:

docker network ls

Inspecciona la que pretendes usar:

docker network inspect appnet

Si no existe, créala:

docker network create appnet

Con Compose, el nombre real de la red puede tener un prefijo del nombre del proyecto. Una red llamada backend en compose.yml puede aparecer como myproject_backend. Usa:

docker compose ps
docker network ls

Si declaraste una red externa en Compose, Compose no la creará por ti:

networks:
  appnet:
    external: true

En ese caso, créala manualmente o elimina external: true si Compose debe gestionarla.

La comunicación entre contenedores falla

Para que dos contenedores se comuniquen por nombre, generalmente necesitan estar en la misma red definida por el usuario. Confirma eso primero:

docker network inspect appnet

Busca ambos contenedores en la sección Containers.

Luego prueba desde un contenedor que tenga herramientas básicas. Tu imagen de aplicación puede no incluir curl, dig o ping, y eso está bien. Usa un contenedor de depuración temporal en la misma red:

docker run --rm -it --network appnet nicolaka/netshoot

Desde dentro:

dig db
curl -v http://api:8080/health
nc -vz db 5432

Si el DNS falla, los contenedores probablemente no están en la misma red definida por el usuario o estás usando la red puente predeterminada esperando una resolución de nombres que no proporciona de la misma manera. Si el DNS funciona pero la conexión falla, verifica si el servicio de destino está escuchando en el puerto esperado.

Dentro del contenedor de destino:

docker exec -it api sh
ss -ltnp || netstat -ltnp

Un error común es vincular la aplicación a 127.0.0.1 dentro del contenedor. Eso solo escucha en el bucle local dentro de ese contenedor. Otros contenedores no pueden alcanzarlo. Configura la aplicación para que escuche en 0.0.0.0.

También asegúrate de usar el puerto del contenedor, no el puerto publicado en el host, para el tráfico entre contenedores. Si la base de datos escucha en el puerto 5432 en el contenedor, otros contenedores deben usar db:5432, no localhost:15432 o el puerto publicado del host.

El host no puede alcanzar un contenedor

Para que el host alcance un servicio en un contenedor con red puente, normalmente necesitas un puerto publicado:

docker run -d --name web -p 8080:80 nginx

Esto mapea el puerto 8080 del host al puerto 80 del contenedor. Prueba desde el host:

curl -v http://localhost:8080

Verifica lo que Docker publicó:

docker port web
docker ps --format 'table {{.Names}}\t{{.Ports}}'

Si no hay mapeo de puertos, EXPOSE en el Dockerfile no publica el puerto. EXPOSE es documentación y metadatos. Aún necesitas -p o ports: en Compose.

Si el puerto está publicado pero la conexión falla, verifica cuatro cosas:

  1. La aplicación está escuchando dentro del contenedor en el puerto del contenedor.
  2. La aplicación está escuchando en 0.0.0.0, no solo en 127.0.0.1.
  3. Ningún cortafuegos del host bloquea el puerto del host.
  4. Ningún otro proceso ya posee el puerto del host.

Encuentra conflictos de puertos del host:

sudo lsof -i :8080
# o
sudo ss -ltnp 'sport = :8080'

Para Compose, recuerda la sintaxis:

ports:
  - "8080:80"

El lado izquierdo es el puerto del host. El lado derecho es el puerto del contenedor.

El contenedor no puede llegar a internet

Prueba la conectividad IP y el DNS por separado:

docker exec -it app sh
ping -c 2 1.1.1.1
ping -c 2 example.com

Algunas imágenes no incluyen ping. Usa curl si está disponible:

curl -I https://example.com

Si la IP funciona pero los nombres fallan, es un problema de DNS. Verifica:

cat /etc/resolv.conf

Docker normalmente inyecta configuraciones de resolución. Las VPN corporativas, el DNS personalizado y las redes de Docker Desktop pueden complicar esto. Puedes configurar el DNS a nivel del daemon en la configuración del daemon de Docker, o pasar DNS para un contenedor específico:

docker run --dns 1.1.1.1 ...

No uses DNS público a ciegas en entornos corporativos donde los nombres internos deben resolverse. Usa los servidores DNS apropiados para la red.

Si ni la IP ni el DNS funcionan, verifica si el contenedor está en --network none, si las reglas del cortafuegos/NAT del host están rotas, si el daemon de Docker tiene configuraciones de red personalizadas y si el host mismo tiene acceso a internet.

Un contenedor necesita alcanzar un servicio en el host

Desde un contenedor, localhost significa el contenedor mismo, no el host. Esta es una de las sorpresas más comunes de redes en Docker.

En Docker Desktop, host.docker.internal generalmente se resuelve al host. En el motor Docker moderno para Linux, puedes agregar una entrada de puerta de enlace del host:

docker run --add-host=host.docker.internal:host-gateway ...

Luego el contenedor puede llamar:

curl http://host.docker.internal:3000

Asegúrate de que el servicio del host escuche en una dirección accesible desde Docker, no solo en un enlace de bucle local al que Docker no pueda acceder en tu entorno. Si un servidor de desarrollo local solo se vincula a 127.0.0.1, es posible que necesites vincularlo a 0.0.0.0 o a la interfaz del host, dependiendo del sistema operativo y los requisitos de seguridad.

Máquinas remotas no pueden alcanzar el contenedor

Si el host puede alcanzar localhost:8080 pero otra máquina no puede alcanzar server-ip:8080, Docker puede estar bien. Verifica el cortafuegos del host, el grupo de seguridad en la nube, el enrutador/NAT y si Docker publicó solo en el bucle local.

Esto publica en todas las interfaces del host:

docker run -p 8080:80 nginx

Esto publica solo en localhost:

docker run -p 127.0.0.1:8080:80 nginx

La publicación solo en bucle local a menudo es deseable para el desarrollo local o configuraciones de proxy inverso, pero bloqueará el acceso remoto por diseño.

En servidores en la nube, también verifica los cortafuegos del proveedor. Abrir ufw en la VM no ayuda si el grupo de seguridad en la nube aún bloquea el puerto.

Errores comunes de redes en Compose

Compose le da a cada servicio un nombre DNS basado en el nombre del servicio. Si tu servicio se llama db, otros servicios generalmente deben conectarse a db, no a localhost.

Ejemplo:

services:
  api:
    build: .
    environment:
      DATABASE_URL: postgres://postgres:postgres@db:5432/app
    depends_on:
      - db
  db:
    image: postgres:16

depends_on controla el orden de inicio, no la preparación. El contenedor de la base de datos puede iniciarse antes de que Postgres esté listo para aceptar conexiones. Tu aplicación debe reintentar las conexiones o usar un patrón de inicio consciente de healthchecks.

También distingue ports de expose. ports publica en el host. expose documenta o expone puertos a servicios vinculados, pero no los hace accesibles desde el host de la misma manera.

Depuración a nivel de paquetes

Cuando las comprobaciones normales no explican el problema, usa una imagen de depuración de red:

docker run --rm -it --network container:<target-container> nicolaka/netshoot

Eso se une al espacio de nombres de red del contenedor de destino, lo que te permite inspeccionar la red desde el mismo punto de vista sin instalar herramientas en la imagen de la aplicación.

Los comandos útiles incluyen:

ip addr
ip route
cat /etc/resolv.conf
dig service-name
curl -v http://service-name:port
tcpdump -nn -i any port 8080

Usa tcpdump cuando necesites saber si los paquetes llegan en absoluto. Si los paquetes nunca llegan, mira antes del contenedor: publicación, cortafuegos, enrutamiento, balanceador de carga. Si los paquetes llegan y no sale ninguna respuesta, mira dentro del contenedor o la aplicación.

Un flujo breve de solución de problemas

Usa este orden para la mayoría de los problemas de redes en Docker:

  1. Define la ruta exacta: host a contenedor, contenedor a contenedor, contenedor a internet, o remoto a host a contenedor.
  2. Verifica la conexión de red con docker network inspect.
  3. Verifica la resolución de nombres desde el lado de origen.
  4. Verifica si el proceso de destino está escuchando en la interfaz y puerto correctos.
  5. Verifica la publicación de puertos solo para el tráfico que cruza del host al contenedor.
  6. Verifica el cortafuegos, VPN, proxy, DNS y reglas de seguridad en la nube fuera de Docker.

La mayoría de los problemas de redes en Docker no son errores profundos de Docker. Generalmente son nombres incorrectos, puertos incorrectos, enlace de bucle local, puertos publicados faltantes, suposiciones de DNS o tráfico probado desde el lado equivocado del límite.