Compartir comentarios
Las respuestas se generan en base a la documentación.

Redes en Compose

Compose gestiona la red por ti de forma predeterminada, pero te ofrece un control detallado cuando lo necesitas. Esta página explica cómo funciona la red por defecto y cómo los contenedores se descubren entre sí por su nombre. También cubre cuándo y cómo definir redes personalizadas, conectar servicios en proyectos de Compose independientes, mapear nombres de host personalizados y depurar problemas de conectividad.

Red por defecto y descubrimiento de servicios

Por defecto, Compose configura una única red para tu aplicación. Cada contenedor de un servicio se une a la red por defecto y es tanto accesible por otros contenedores en esa red como detectable por su nombre de servicio. Esta red utiliza el controlador bridge. Para comprender cuándo utilizarías un controlador diferente, consulta Controladores de red: bridge frente a host.

Para la mayoría de las configuraciones de desarrollo, la red por defecto es suficiente. Al ejecutar docker compose up, Compose crea una red llamada <project-name>_default y conecta todos los servicios a ella. Cada servicio registra su nombre con un servidor DNS interno, de modo que los contenedores pueden comunicarse entre sí utilizando el nombre del servicio directamente. No se necesitan direcciones IP ni configuración manual.

Por ejemplo, supón que tu aplicación está en un directorio llamado myapp y tu archivo compose.yaml se ve así:

services:
  web:
    build: .
    ports:
      - "8000:8000"
  db:
    image: postgres:latest
    ports:
      - "8001:5432"

Compose conecta automáticamente todos los servicios a la red por defecto, por lo que no es necesario definir networks de forma explícita en el archivo de Compose.

Al ejecutar docker compose up, sucede lo siguiente:

  1. Se crea una red llamada myapp_default.
  2. Se crea un contenedor utilizando la configuración de web. Se une a myapp_default bajo el nombre web.
  3. Se crea un contenedor utilizando la configuración de db. Se une a myapp_default bajo el nombre db.

Ahora, cada contenedor puede buscar el nombre de servicio web o db y obtener la dirección IP del contenedor correspondiente. El servicio web puede conectarse a la base de datos en postgres://db:5432. Desde la máquina host, se puede acceder a la misma base de datos en postgres://localhost:8001 si tu contenedor se ejecuta localmente.

Tip

Docker asigna direcciones IP de contenedores de forma dinámica desde la subred de la red cada vez que se inicia un contenedor, por lo que no se persisten tras reinicios o recreaciones. Esto significa que siempre debes hacer referencia a los servicios por su nombre, no por la dirección IP. Cuando se recrean los contenedores, por ejemplo tras un cambio de configuración, reciben una nueva dirección IP. El nombre del servicio permanece estable.

A la red de tu aplicación se le asigna un nombre basado en el "nombre del proyecto", el cual se toma del nombre del directorio en el que reside. Puedes sobrescribir el nombre del proyecto con la bandera --project-name o con la variable de entorno COMPOSE_PROJECT_NAME.

El HOST_PORT y el CONTAINER_PORT sirven para propósitos diferentes. En el ejemplo anterior, para db, el HOST_PORT es 8001 y el puerto del contenedor es 5432 (el valor por defecto de Postgres). La comunicación entre servicios en red utiliza el CONTAINER_PORT. El puerto del host solo se utiliza cuando se accede al servicio desde fuera de la red.

Actualización de contenedores en la red

Si realizas un cambio de configuración en un servicio y ejecutas docker compose up para actualizarlo, el contenedor antiguo se elimina y el nuevo se une a la red bajo una dirección IP diferente pero con el mismo nombre. Los contenedores en ejecución pueden buscar ese nombre y conectarse a la nueva dirección, pero la dirección antigua deja de funcionar.

Si algún contenedor tiene conexiones abiertas con el contenedor antiguo, estas se cierran. Es responsabilidad de cada contenedor detectar esta condición, volver a buscar el nombre y reconectarse.

Cambiar el modo de red

Por defecto, cada servicio se une a la red bridge del proyecto. Es el modo de red más seguro. Si no especificas network_mode, este es el tipo de red que estás creando.

Puedes sobrescribir el modo de red por servicio. La opción network_mode admite los siguientes valores:

  • host: El contenedor comparte la pila de red del host. No se necesita ni se admite el mapeo de puertos, y la resolución DNS por nombre de servicio no funciona. Úsalo para herramientas a nivel de sistema, como monitores de red, que requieren acceso directo a las interfaces del host. Un contenedor que utiliza network_mode: host puede acceder a todos los puertos del host y observar todo el tráfico de red en él. Úsalo únicamente cuando sea realmente necesario.
  • none: Desactiva toda la red del contenedor.
  • service:{name}: Otorga al contenedor acceso al contenedor especificado haciendo referencia a su nombre de servicio.
  • container:{name}: Otorga al contenedor acceso al contenedor especificado haciendo referencia a su ID de contenedor.

Puedes mezclar modos en un solo proyecto:

services:
  app:
    image: myapp
    networks:
      - isolated
    ports:
      - "3000:3000"

  monitoring:
    image: netdata/netdata
    network_mode: host # Puede monitorear el sistema host y todos los puertos del host

networks:
  isolated:
    driver: bridge

Especificar redes personalizadas

En lugar de limitarte a utilizar la red por defecto de la aplicación, puedes especificar tus propias redes con la clave de nivel superior networks. Esto te permite crear topologías más complejas y especificar controladores de red personalizados y opciones. También puedes utilizarlo para conectar servicios a redes creadas externamente que no son gestionadas por Compose.

Cada servicio puede especificar a qué redes conectarse mediante la clave networks a nivel de servicio, que consiste en una lista de nombres que hacen referencia a las entradas bajo la clave de nivel superior networks.

El siguiente ejemplo muestra un archivo de Compose que define dos redes personalizadas. El servicio proxy está aislado del servicio db, porque no comparten una red en común. Solo app puede comunicarse con ambos.

services:
  proxy:
    build: ./proxy
    networks:
      - frontend
  app:
    build: ./app
    networks:
      - frontend
      - backend
  db:
    image: postgres:latest
    networks:
      - backend

networks:
  frontend:
    driver: bridge # Especificar opciones del controlador
    driver_opts:
      com.docker.network.bridge.host_binding_ipv4: "127.0.0.1"
  backend:
    driver: custom-driver # Usar un controlador personalizado

Las redes se pueden configurar con direcciones IP estáticas estableciendo ipv4_address y/o ipv6_address para cada red conectada.

A las redes también se les puede asignar un nombre personalizado:

services:
  # ...
networks:
  frontend:
    name: custom_frontend
    driver: custom-driver-1

Redes internas

Establecer internal: true en una red la crea sin conexión a las interfaces de red del host. No tiene una puerta de enlace (gateway) por defecto para la conectividad externa. Esto es útil para servicios como bases de datos que deben ser completamente inaccesibles desde fuera de la red del contenedor:

services:
  cache:
    image: redis
    networks:
      - isolated

  worker:
    image: myworker
    networks:
      - isolated
      - public

networks:
  isolated:
    internal: true # Sin conectividad externa
  public: # Red bridge estándar, creada por Compose al ejecutar docker compose up

Ten en cuenta que un servicio conectado tanto a una red interna como a una no interna (como worker en el ejemplo anterior) aún puede acceder a internet a través de la red no interna public.

Configurar la red por defecto

En lugar de, o además de especificar tus propias redes, también puedes cambiar la configuración de la red por defecto de toda la aplicación definiendo una entrada bajo networks llamada default:

services:
  web:
    build: .
    ports:
      - "8000:8000"
  db:
    image: postgres:latest

networks:
  default:
    driver: custom-driver-1 # Usar un controlador personalizado

Usar una red externa existente

Si has creado manualmente una red bridge utilizando docker network create, puedes conectar tus servicios de Compose a ella marcando la red como external:

services:
  # ...
networks:
  network1:
    name: my-pre-existing-network
    external: true

En lugar de crear <project-name>_default, Compose busca a red llamada my-pre-existing-network y conecta tus contenedores a ella.

Conectar múltiples proyectos de Compose

Las redes externas son especialmente útiles cuando los servicios de diferentes proyectos de Compose necesitan comunicarse. Crea una red compartida una vez y luego haz referencia a ella como externa en cada proyecto:

docker network create inter-project

backend-compose.yaml:

services:
  api:
    image: myapi:latest
    networks:
      - shared
      - default # Conservar también la red interna del proyecto

networks:
  shared:
    external: true
    name: inter-project

frontend-compose.yaml:

services:
  web:
    image: myfrontend:latest
    environment:
      API_URL: http://api:8080 # Referencia por nombre de servicio
    networks:
      - shared

networks:
  shared:
    external: true
    name: inter-project

Los servicios en la misma red externa pueden comunicarse entre sí por el nombre del servicio, al igual que los servicios dentro de un solo proyecto.

Important

La red externa debe existir antes de ejecutar docker compose up. Si no existe, Compose falla con el error Network not found. Créala siempre primero con docker network create.

Red híbrida

Un servicio puede pertenecer tanto a una red compartida externa como a su propia red interna de proyecto. Esto te permite exponer únicamente los servicios que deben ser accesibles desde otros proyectos, manteniendo todo lo demás, como las bases de datos, completamente aislado:

services:
  api:
    image: myapp-api
    networks:
      - shared # Accesible desde otros proyectos
      - internal # También puede acceder a la base de datos

  database:
    image: postgres:latest
    networks:
      - internal # No expuesto en la red compartida

networks:
  shared:
    name: inter-project
    external: true
  internal: {} # Específica del proyecto, aislada

DNS personalizado con extra_hosts

Puedes añadir asignaciones personalizadas de nombre de host a IP en el archivo /etc/hosts de un contenedor utilizando extra_hosts. Esto resulta útil cuando un servicio necesita resolver un nombre de host que no está registrado en el DNS interno de Docker. Por ejemplo, una dependencia con IP fija o un endpoint de pruebas (staging):

services:
  app:
    image: myapp
    extra_hosts:
      - "api.staging:192.168.1.100"
      - "cache.internal:192.168.1.101"

Para mapear un nombre de host dinámicamente a la IP de la máquina host, utiliza el valor especial host-gateway:

services:
  app:
    image: myapp
    extra_hosts:
      - "host.docker.internal:host-gateway"

En Linux, host-gateway se resuelve como la IP del host en la red bridge por defecto. En Mac y Windows, Docker proporciona esto de forma automática, por lo que host-gateway se resuelve en la misma dirección IP interna que host.docker.internal.

También puedes controlar extra_hosts desde variables de entorno, lo que facilita apuntar los servicios a diferentes destinos por entorno:

services:
  app:
    image: myapp
    extra_hosts:
      - "api.service:${API_HOST:-127.0.0.1}"
      - "auth.service:${AUTH_HOST:-127.0.0.1}"

Donde .env.development podría establecer API_HOST=localhost y un archivo de entorno de producción podría configurar API_HOST=10.0.1.50.

Para verificar qué se ha inyectado, inspecciona el archivo hosts dentro del contenedor:

$ docker compose exec app cat /etc/hosts

Redes multi-host

Al desplegar una aplicación de Compose en un Docker Engine con el modo Swarm habilitado, puedes utilizar el controlador incorporado overlay para permitir la comunicación multi-host. Las redes overlay siempre se crean como conectables (attachable). De forma opcional, puedes establecer la propiedad attachable en false.

Para obtener más información, consulta la documentación del controlador de red overlay.

Los enlaces (links) permiten definir alias adicionales mediante los cuales un servicio es accesible desde otro servicio. No son necesarios para la comunicación básica de servicio a servicio. Por defecto, cualquier servicio puede comunicarse con cualquier otro utilizando el nombre de ese servicio. En el siguiente ejemplo, db es accesible desde web tanto a través del nombre de host db como de database:

services:
  web:
    build: .
    links:
      - "db:database"
  db:
    image: postgres:latest

Consulta la referencia de links para obtener más información.

Depuración (Debugging)

Cuando un servicio no puede comunicarse con otro, realiza los siguientes pasos en orden: primero confirma que la configuración de red sea correcta, luego confirma que los contenedores estén realmente conectados y finalmente prueba la conectividad en vivo.

Inspeccionar mapeo de puertos

Para averiguar qué puerto del host se mapea a un puerto del contenedor, utiliza docker compose port:

# ¿Qué puerto del host se mapea al puerto 5432 del contenedor en db?
$ docker compose port db 5432
# Output: 0.0.0.0:8001

Esto resulta especialmente útil al utilizar el mapeo de puertos dinámico, donde el puerto del host cambia en cada docker compose up:

services:
  web:
    image: nginx
    ports:
      - "80" # Docker asigna el puerto del host dinámicamente
$ docker compose port web 80
# Output: 0.0.0.0:55432

Al escalar un servicio, cada réplica obtiene su propio puerto dinámico. Utiliza --index para consultar una réplica específica:

$ docker compose up -d --scale web=3

$ docker compose port --index=1 web 80   # Output: 0.0.0.0:55001
$ docker compose port --index=2 web 80   # Output: 0.0.0.0:55002
$ docker compose port --index=3 web 80   # Output: 0.0.0.0:55003

Por defecto, docker compose port busca mapeos TCP. Si un servicio expone tanto TCP como UDP en el mismo puerto, utiliza --protocol:

$ docker compose port --protocol=udp myservice 53

Verificar pertenencia a la red

Para comprobar qué contenedores están conectados a una red (útil al solucionar problemas de conectividad a través de redes externas o personalizadas):

$ docker network inspect <network-name>

Comprobar conectividad

Si la pertenencia a la red parece correcta pero los servicios aún no pueden comunicarse entre sí, prueba la conectividad desde el interior de un contenedor en ejecución utilizando docker compose exec.

Información de referencia adicional

Para obtener detalles completos de las opciones de configuración de red disponibles, consulta las siguientes referencias: