Comunicación entre múltiples proyectos docker-compose


253

Tengo dos docker-compose.ymlarchivos separados en dos carpetas diferentes:

  • ~/front/docker-compose.yml
  • ~/api/docker-compose.yml

¿Cómo puedo asegurarme de que un contenedor frontpuede enviar solicitudes a un contenedor api?

Sé que esa --default-gatewayopción se puede configurar usando docker runun contenedor individual, de modo que se puede asignar una dirección IP específica a este contenedor, pero parece que esta opción no está disponible cuando se usa docker-compose.

Actualmente termino haciendo una docker inspect my_api_container_idy miro la puerta de enlace en la salida. Funciona, pero el problema es que esta IP se atribuye aleatoriamente, por lo que no puedo confiar en ella.

Otra forma de esta pregunta podría ser:

  • ¿Puedo atribuir una dirección IP fija a un contenedor particular usando docker-compose?

Pero al final lo que estoy cuidando es:

  • ¿Cómo se pueden comunicar entre sí dos proyectos diferentes de compilación acoplable?

44
Acabo de ver esto hoy de nuevo. Los desarrolladores finalmente cedieron y permitieron nombrar redes arbitrarias. Usando compose file version 3.5 puede especificar un nombre para la red predeterminada bajo la tecla 'redes'. Esto creará una red con nombre sin el prefijo de nombre de proyecto habitual si no existe ..
cstrutton

Respuestas:


325

Solo necesita asegurarse de que los contenedores con los que desea comunicarse estén en la misma red. Las redes son una construcción de acoplador de primera clase, y no son específicas para componer.

# front/docker-compose.yml
version: '2'
services:
  front:
    ...
    networks:
      - some-net
networks:
  some-net:
    driver: bridge

...

# api/docker-compose.yml
version: '2'
services:
  api:
    ...
    networks:
      - front_some-net
networks:
  front_some-net:
    external: true

Nota: La red de su aplicación recibe un nombre basado en el "nombre del proyecto", que se basa en el nombre del directorio en el que vive, en este caso front_se agregó un prefijo

Luego pueden hablar entre ellos utilizando el nombre del servicio. De frontusted puede hacer ping apiy viceversa.


1
Jivan es una no solución. Sus contenedores no deberían necesitar saber nada sobre el host o ser manipulados de esa manera. Sin embargo, mi respuesta fue bastante corta, la actualicé con más detalle.
johnharris85

3
Robert Moskal solo si hackeas para obtener la ip de tu host Docker en los contenedores. Es mejor que se comuniquen en una red común definida por docker.
johnharris85

2
Tenga en cuenta que el prefijo "front_" para la red se crea automáticamente desde la carpeta en la que se ejecuta. Entonces, si su primer archivo docker-compose estaría ubicado en "example / docker-compose.yml", en su lugar, se llamaría "example_default".
AngryUbuntuNerd

77
También puede proporcionar un nombre a una red utilizando la namepropiedad, lo que deshabilitará el pre-pendiente automático con el nombre del proyecto. Luego, cualquier proyecto puede usar esa red y crearla automáticamente si aún no existe.
SteveB

2
@SteveB: tenga en cuenta que la propiedad de nombre solo funciona a partir de archivos
Docker

78

Solo una pequeña adición a la excelente respuesta de @ johnharris85, cuando está ejecutando un archivo de compilación de docker, defaultse crea una red " " para que pueda agregarlo al otro archivo de componer como una red externa:

# front/docker-compose.yml 
version: '2' 
  services:   
    front_service:
    ...

...

# api/docker-compose.yml
version: '2'
services:
  api_service:
    ...
    networks:
      - front_default
networks:
  front_default:
    external: true

Para mí, este enfoque era más adecuado porque no poseía el primer archivo docker-compose y quería comunicarme con él.


simplemente vagando por la forma correcta de asignar IP estática para esta red externa. Manged a hacerlo dentro de services:la etiqueta, sería la sintaxis networks:anidado entonces front_default:(quitar el "-") y luego anidar una IP estática:ipv4_address: '172.20.0.44'
junior MAYHE

76

ACTUALIZACIÓN: A partir de componer archivo versión 3.5:

Esto ahora funciona:

version: "3.5"
services:
  proxy:
    image: hello-world
    ports:
      - "80:80"
    networks:
      - proxynet

networks:
  proxynet:
    name: custom_network

docker-compose up -dse unirá a una red llamada 'custom_network'. Si no existe, ¡se creará!

root@ubuntu-s-1vcpu-1gb-tor1-01:~# docker-compose up -d
Creating network "custom_network" with the default driver
Creating root_proxy_1 ... done

Ahora puedes hacer esto:

version: "2"
services:
  web:
    image: hello-world
    networks:
      - my-proxy-net
networks:
  my-proxy-net:
    external:
      name: custom_network

Esto creará un contenedor que estará en la red externa.

¡Todavía no puedo encontrar ninguna referencia en los documentos pero funciona!


¿Tiene que iniciar los dos servicios en un orden específico? ¿Puede iniciar cualquiera de los dos, y el primero creará la red y el segundo se unirá?
slashdottir

44
El primer servicio (proxy arriba) crea la red. La sintaxis en el segundo ejemplo se une a ella.
cstrutton

2
@slashdottir Puede no marcar la red externa como en el segundo servicio y se creará si no existe todavía.
SteveB

2
Funciona Acabo de hacer girar una gotita de OD con la última composición de Docker. He editado el ejemplo a un ejemplo de trabajo real.
cstrutton

1
En mi caso, resultó ser una solución más adecuada que la respuesta aceptada. El problema con la red externa era que requería iniciar contenedores en un orden predefinido. Para mi cliente, esto no era aceptable. Una red con nombre (desde 3.5) resultó ser la solución perfecta. Gracias.
ygor

25

Todos los contenedores de apipueden unirse a la red front predeterminada con la siguiente configuración:

# api/docker-compose.yml

...

networks:
  default:
    external:
      name: front_default

Consulte la guía de redacción de Docker: uso de una red preexistente (consulte al final)


12

La información de las publicaciones anteriores es correcta, pero no tiene detalles sobre cómo vincular contenedores, que deben conectarse como "enlaces externos".

Espero que este ejemplo te sea más claro:

  • Suponga que tiene app1 / docker-compose.yml, con dos servicios (svc11 y svc12), y app2 / docker-compose.yml con dos servicios más (svc21 y svc22) y suponga que necesita conectarse de forma cruzada:

  • svc11 necesita conectarse al contenedor de svc22

  • svc21 necesita conectarse al contenedor de svc11.

Entonces la configuración debería ser así:

esto es app1 / docker-compose.yml:


version: '2'
services:
    svc11:
        container_name: container11
        [..]
        networks:
            - default # this network
            - app2_default # external network
        external_links:
            - container22:container22
        [..]
    svc12:
       container_name: container12
       [..]

networks:
    default: # this network (app1)
        driver: bridge
    app2_default: # external network (app2)
        external: true

esto es app2 / docker-compose.yml:


version: '2'
services:
    svc21:
        container_name: container21
        [..]
        networks:
            - default # this network (app2)
            - app1_default # external network (app1)
        external_links:
            - container11:container11
        [..]
    svc22:
       container_name: container22
       [..]

networks:
    default: # this network (app2)
        driver: bridge
    app1_default: # external network (app1)
        external: true

6

Desde Compose 1.18 (espec. 3.5), puede anular la red predeterminada utilizando su propio nombre personalizado para todos los archivos Compose YAML que necesite. Es tan simple como agregarles lo siguiente:

networks:
  default:
    name: my-app

Lo anterior supone que se ha versionconfigurado en 3.5(o superior si no lo desaprobarán en 4+).

Otras respuestas han apuntado lo mismo; Este es un resumen simplificado.


2

Me aseguraría de que todos los contenedores estén docker-composeen la misma red componiéndolos juntos al mismo tiempo, usando:

docker compose --file ~/front/docker-compose.yml --file ~/api/docker-compose.yml up -d

¿Eso me permitirá, por ejemplo, hacer un linko depends_onde un contenedor de frente a un contenedor de API?
Jivan

En realidad, cuando hago lo que usted sugiere, respuestas compuestas por el acoplador, build path ~/front/api either does not exist or is not accessibleo al revés,build path ~/api/front either does not exist or is not accessible
Jivan

1
Si los está componiendo al mismo tiempo, no debería necesitarlo. Se creará una red con todos sus contenedores, todos podrán comunicarse a través del nombre del servicio desde el archivo de redacción ( no el nombre del contenedor).
Nauraushaun

Puede ser más fácil si los dos archivos de composición están en la misma carpeta. Pero no creo que sea necesario, creo que debería funcionar de cualquier manera.
Nauraushaun

2
Esta solución no funciona, vea mi comentario en este hilo: github.com/docker/compose/issues/3530#issuecomment-222490501
johnharris85

2

ACTUALIZACIÓN: A partir de componer archivo versión 3.5:

Encontré un problema similar y lo resolví agregando un pequeño cambio en uno de mi proyecto docker-compose.yml.

Por ejemplo tenemos dos api scoringy ner. Scoringapi necesita enviar una solicitud a la nerapi para procesar la solicitud de entrada. Para hacerlo, se supone que ambos comparten la misma red.

Nota: Cada contenedor tiene su propia red que se crea automáticamente al momento de ejecutar la aplicación dentro de la ventana acoplable. Por ejemplo, la red ner api se creará como ner_defaulty la red api de puntuación se denominará como scoring default. Esta solución funcionará para la versión: '3'.

Como en el escenario anterior, mi API de puntuación quiere comunicarse con una API ner, luego agregaré las siguientes líneas. Lo que significa que siempre que creo el contenedor para ner api, se agrega automáticamente a la red scoring_default.

networks:
  default:
      external:
        name: scoring_default

ner / docker-compose.yml

version: '3'
services:
  ner:
    build: .
    ...

networks:
  default:
      external:
        name: scoring_default

puntuación / docker-compose.yml

version: '3'
services:
  api:
    build: .
    ...

Podemos ver cómo los contenedores anteriores ahora son parte de la misma red llamada scoring_defaultusando el comando:

docker inspeccionar scoring_default

{
    "Name": "scoring_default",
        ....
    "Containers": {
    "14a6...28bf": {
        "Name": "ner_api",
        "EndpointID": "83b7...d6291",
        "MacAddress": "0....",
        "IPv4Address": "0.0....",
        "IPv6Address": ""
    },
    "7b32...90d1": {
        "Name": "scoring_api",
        "EndpointID": "311...280d",
        "MacAddress": "0.....3",
        "IPv4Address": "1...0",
        "IPv6Address": ""
    },
    ...
}

1

Puede agregar un .envarchivo en todos sus proyectos que contengan COMPOSE_PROJECT_NAME=somename.

COMPOSE_PROJECT_NAME anula el prefijo utilizado para nombrar recursos, por lo que todos sus proyectos utilizaránsomename_default como su red, lo que hace posible que los servicios se comuniquen entre sí como lo estaban en el mismo proyecto.

NB: Recibirá advertencias para contenedores "huérfanos" creados a partir de otros proyectos.


0
version: '2'
services:
  bot:
    build: .
    volumes:
      - '.:/home/node'
      - /home/node/node_modules
    networks:
      - my-rede
    mem_limit: 100m
    memswap_limit: 100m
    cpu_quota: 25000
    container_name: 236948199393329152_585042339404185600_bot
    command: node index.js
    environment:
      NODE_ENV: production
networks:
  my-rede:
    external:
      name: name_rede_externa

0

Para usar otra red docker-compose, simplemente haga lo siguiente (para compartir redes entre docker-compose):

  1. Ejecute el primer proyecto docker-compose por up -d
  2. Encuentre el nombre de la red del primer docker-compose por: docker network ls(Contiene el nombre del proyecto del directorio raíz)
  3. Luego use ese nombre con esta estructura a continuación en el segundo archivo docker-compose.

segundo docker-compose.yml

version: '3'
services:
  service-on-second-compose:  # Define any names that you want.
    .
    .
    .
    networks:
      - <put it here(the network name that comes from "docker network ls")>

networks:
  - <put it here(the network name that comes from "docker network ls")>:
    external: true

0

Otra opción es simplemente ejecutar el primer módulo con 'docker-compose', verificar la ip relacionada con el módulo y conectar el segundo módulo con la red anterior como externa, y apuntar la ip interna

ejemplo app1: nueva red creada en las líneas de servicio, marca como externa: verdadera en la parte inferior app2: indica la "nueva red" creada por app1 cuando sube, marca como externa: verdadera en la parte inferior y configura la configuración para conectarse, la ip que app1 tiene en esta red.

Con esto, deberían poder hablar entre ellos

* de esta manera es solo para el enfoque de prueba local, con el fin de no hacer una configuración demasiado compleja ** Sé que es muy 'parche' pero funciona para mí y creo que es tan simple que otros pueden aprovechar esto


0

Si usted es

  • tratando de comunicarse entre dos contenedores de diferentes proyectos de composición acoplable y no quiere usar la misma red (porque digamos que tendrían un contenedor PostgreSQL o Redis en el mismo puerto y preferiría no cambiar estos puertos y no usarlo) en la misma red)
  • desarrollando localmente y quiere imitar la comunicación entre dos proyectos de composición de Docker
  • ejecutando dos proyectos docker-compose en localhost
  • desarrollando especialmente aplicaciones Django o API Django Rest Framework (drf) y ejecutando la aplicación dentro del contenedor en algún puerto expuesto
  • conseguir Connection refusedmientras intenta comunicarse entre dos contenedores

Y tú quieres

  • contenedor api_acomunicarse con api_b(o viceversa) sin la misma "red de acopladores"

(ejemplo a continuación)

puede usar el "host" del segundo contenedor como IP de su computadora y el puerto que se asigna desde el contenedor Docker. Puede obtener la IP de su computadora con este script (desde: Encontrar direcciones IP locales usando stdlib de Python ):

import socket
def get_ip():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        # doesn't even have to be reachable
        s.connect(('10.255.255.255', 1))
        IP = s.getsockname()[0]
    except:
        IP = '127.0.0.1'
    finally:
        s.close()
    return IP

Ejemplo:

project_api_a/docker-compose.yml:

networks:
  app-tier:
    driver: bridge

services:
  api:
    container_name: api_a
    image: api_a:latest
    depends_on:
      - postgresql
    networks:
      - app-tier

dentro del api_acontenedor estás ejecutando la aplicación Django: manage.py runserver 0.0.0.0:8000

y el segundo docker-compose.yml de otro proyecto:

project_api_b/docker-compose-yml :

networks:
  app-tier:
    driver: bridge

services:
  api:
    container_name: api_b
    image: api_b:latest
    depends_on:
      - postgresql
    networks:
      - app-tier

dentro del api_bcontenedor estás ejecutando la aplicación Django: manage.py runserver 0.0.0.0:8001

E intentar conectar desde el contenedor api_aa la api_bURL del api_bcontenedor será: http://<get_ip_from_script_above>:8001/

Puede ser especialmente valioso si está utilizando incluso más de dos (tres o más) proyectos compuestos por docker y es difícil proporcionar una red común para todo esto: es una buena solución y solución

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.