Cómo comunicarse entre contenedores Docker a través de "nombre de host"

91

Planeo dividir mi servidor monolítico en muchos contenedores acoplables pequeños, pero todavía no he encontrado una buena solución para la "comunicación entre contenedores". Este es mi escenario objetivo:

Escenario objetivo

Sé cómo vincular contenedores y cómo exponer puertos, pero ninguna de estas soluciones me satisface.

¿Existe alguna solución para comunicarse a través de nombres de host (nombres de contenedor) entre los contenedores como en una red de servidor tradicional?

Patrick Gotthard
fuente
He escrito un documento recientemente haciendo exactamente lo que estás buscando. Básicamente, documenta cómo instalar varios contenedores (uno por proceso) y cómo integrarlos. La "comunicación entre contenedores" es parte del juego .
xuhdev
Acabo de encontrar el Blog de Tumtum y encontré este párrafo en la documentación oficial de Docker . No sé si me había perdido este párrafo todo el tiempo o si se había agregado recientemente, pero eso debería ser exactamente lo que necesito :)
Patrick Gotthard
Docker 1.10 está disponible y Docker Connect es increíble ( github.com/docker/docker/blob/… ). Vea mi respuesta editada a continuación
VonC
2
Creo que deberías probar docker-compose . Funciona muy bien.
Suhas Chikkanna

Respuestas:

27

Editar: después de Docker 1.9, el docker networkcomando (ver más abajo https://stackoverflow.com/a/35184695/977939 ) es la forma recomendada de lograr esto.


Mi solución es configurar un dnsmasq en el host para que el registro DNS se actualice automáticamente: los registros "A" tienen los nombres de los contenedores y apuntan a las direcciones IP de los contenedores automáticamente (cada 10 segundos). El script de actualización automática se pega aquí:

#!/bin/bash

# 10 seconds interval time by default
INTERVAL=${INTERVAL:-10}

# dnsmasq config directory
DNSMASQ_CONFIG=${DNSMASQ_CONFIG:-.}

# commands used in this script
DOCKER=${DOCKER:-docker}
SLEEP=${SLEEP:-sleep}
TAIL=${TAIL:-tail}

declare -A service_map

while true
do
    changed=false
    while read line
    do
        name=${line##* }
        ip=$(${DOCKER} inspect --format '{{.NetworkSettings.IPAddress}}' $name)
        if [ -z ${service_map[$name]} ] || [ ${service_map[$name]} != $ip ] # IP addr changed
        then
            service_map[$name]=$ip
            # write to file
            echo $name has a new IP Address $ip >&2
            echo "host-record=$name,$ip"  > "${DNSMASQ_CONFIG}/docker-$name"
            changed=true
        fi
    done < <(${DOCKER} ps | ${TAIL} -n +2)

    # a change of IP address occured, restart dnsmasq
    if [ $changed = true ]
    then
        systemctl restart dnsmasq
    fi

    ${SLEEP} $INTERVAL
done

Asegúrese de que su servicio dnsmasq esté disponible el docker0. Luego, comience su contenedor con --dns HOST_ADDRESSpara usar este servicio mini dns.

Referencia: http://docs.blowb.org/setup-host/dnsmasq.html

xuhdev
fuente
Eso parece interesante, mucho más resistente que mi respuesta --link. +1
VonC
@VonC parece que la nueva red de bibliotecas puede reemplazar esta solución. Veamos sin embargo.
xuhdev
@xuhdev Configuré dnsmasq como en docs.blowb.org/setup-host/dnsmasq.html . Pero tengo problemas cuando uso excavar desde el contenedor de la ventana acoplable, se agota el tiempo. Pero el ping a la ip de la interfaz del host docker0 funciona. También excava con la misma ip de docker0 desde el host de docker. ¿Tiene alguna sugerencia?
Sábado
1
@Satheesh ¿Quizás es la configuración de su firewall lo que evita que su contenedor consulte DNS desde el host?
xuhdev
@xuhdev gracias, es el firewalld en mi máquina host, lo que causó el problema. Una vez que me agaché en firewalld, mi contenedor se comunica con dnsmasq en el host
sábado
206

La nueva función de red le permite conectarse a contenedores por su nombre, por lo que si crea una nueva red, cualquier contenedor conectado a esa red puede llegar a otros contenedores por su nombre. Ejemplo:

1) Crear nueva red

$ docker network create <network-name>       

2) Conectar contenedores a la red

$ docker run --net=<network-name> ...

o

$ docker network connect <network-name> <container-name>

3) Contenedor de ping por nombre

docker exec -ti <container-name-A> ping <container-name-B> 

64 bytes from c1 (172.18.0.4): icmp_seq=1 ttl=64 time=0.137 ms
64 bytes from c1 (172.18.0.4): icmp_seq=2 ttl=64 time=0.073 ms
64 bytes from c1 (172.18.0.4): icmp_seq=3 ttl=64 time=0.074 ms
64 bytes from c1 (172.18.0.4): icmp_seq=4 ttl=64 time=0.074 ms

Consulte esta sección de la documentación;

Nota: A diferencia de la herencia, linksla nueva red no creará variables de entorno ni las compartirá con otros contenedores.

Actualmente, esta función no admite alias

Hemerson Varela
fuente
4
Funciona genial. ¿Por qué la red predeterminada no habilita esto de forma predeterminada?
Stéphane
La parte menos obvia es que debe reiniciar una aplicación que se ejecuta en un contenedor diferente. Entonces, ¿cómo puede el contenedor A hacer que una aplicación que se ejecute en el contenedor B se reinicie? Claramente, parece que se necesita algún tipo de bus de comunicación. En lo más alto de mi mente es usar Redis para la señalización y la comunicación entre contenedores. Entonces, todos los contenedores se suscriben al canal de redis y será allí donde hablarán ... ¿Qué tal de los cambios en los puertos publicados en docker-compose? .yml archivo que requiere un completo docker-compose down,up,restart?
eigenfield
¡esto es exactamente lo que he estado buscando todo el día! no sabía que podía hacer referencia a un nodo de red por su nombre / id de contenedor. ¡gracias!
elliotwesoff
1
@ Stéphane Está deshabilitado en la bridgered predeterminada debido a la compatibilidad con versiones anteriores, pero sí, estoy de acuerdo, ¡ definitivamente debería estar habilitado de manera predeterminada!
helmesjo
15

Eso debería ser lo que --linkes de , al menos para la parte de nombre de host.
Con Docker 1.10 y PR 19242 , eso sería:

docker network create --net-alias=[]: Add network-scoped alias for the container

(ver la última sección a continuación)

Eso es lo que actualiza los/etc/hosts detalles del archivo.

Además de las variables de entorno, Docker agrega una entrada de host para el contenedor de origen al /etc/hostsarchivo.

Por ejemplo, inicie un servidor LDAP:

docker run -t  --name openldap -d -p 389:389 larrycai/openldap

Y defina una imagen para probar ese servidor LDAP:

FROM ubuntu
RUN apt-get -y install ldap-utils
RUN touch /root/.bash_aliases
RUN echo "alias lds='ldapsearch -H ldap://internalopenldap -LL -b
ou=Users,dc=openstack,dc=org -D cn=admin,dc=openstack,dc=org -w
password'" > /root/.bash_aliases
ENTRYPOINT bash

Puede exponer el " openldapcontenedor como internalopenldap" dentro de la imagen de prueba con --link:

 docker run -it --rm --name ldp --link openldap:internalopenldap ldaptest

Entonces, si escribe 'lds', ese alias funcionará:

ldapsearch -H ldap://internalopenldap ...

Eso devolvería a la gente. El significado internalopenldapse alcanza correctamente a partir de la ldaptestimagen.


Por supuesto, se agregará Docker 1.7 libnetwork, que proporciona una implementación nativa de Go para conectar contenedores. Vea la publicación del blog .
Introdujo una arquitectura más completa, con el Container Network Model (CNM)

https://blog.docker.com/media/2015/04/cnm-model.jpg

Eso actualizará la CLI de Docker con nuevos comandos de "red" y documentará cómo -netse usa el indicador " " para asignar contenedores a las redes.


docker 1.10 tiene una nueva sección alias de ámbito de red , ahora oficialmente documentada ennetwork connect :

Mientras que los enlaces proporcionan una resolución de nombres privados que se localiza dentro de un contenedor, el alias de ámbito de red proporciona una forma de que un contenedor sea descubierto por un nombre alternativo por cualquier otro contenedor dentro del alcance de una red en particular.
A diferencia del alias de enlace, que lo define el consumidor de un servicio, el alias de ámbito de red lo define el contenedor que ofrece el servicio a la red.

Continuando con el ejemplo anterior, cree otro contenedor isolated_nwcon un alias de red.

$ docker run --net=isolated_nw -itd --name=container6 -alias app busybox
8ebe6767c1e0361f27433090060b33200aac054a68476c3be87ef4005eb1df17

--alias=[]         

Agregar un alias de ámbito de red para el contenedor

Puede usar la --linkopción para vincular otro contenedor con un alias preferido

Puede pausar, reiniciar y detener contenedores que están conectados a una red. Los contenedores en pausa permanecen conectados y pueden ser revelados por una inspección de red. Cuando el contenedor se detiene, no aparece en la red hasta que lo reinicia.

Si se especifica, las direcciones IP del contenedor se vuelven a aplicar cuando se reinicia un contenedor detenido. Si la dirección IP ya no está disponible, el contenedor no se inicia.

Una forma de garantizar que la dirección IP esté disponible es especificar una --ip-rangeal crear la red y elegir las direcciones IP estáticas fuera de ese rango. Esto asegura que la dirección IP no se proporcione a otro contenedor mientras este contenedor no esté en la red.

$ docker network create --subnet 172.20.0.0/16 --ip-range 172.20.240.0/20 multi-host-network

$ docker network connect --ip 172.20.128.2 multi-host-network container2
$ docker network connect --link container1:c1 multi-host-network container2
VonC
fuente
3
El problema de --link es que no puede reiniciar un contenedor sin reiniciar también los contenedores vinculados. Cuando miras mi gráfico, reiniciar el contenedor MySQL resultará en una cascada de reinicios de otros contenedores.
Patrick Gotthard
3

EDITAR : Ya no es de vanguardia: http://blog.docker.com/2016/02/docker-1-10/

Respuesta original
Luché con él toda la noche. Si no le teme a la vanguardia, la última versión del motor Docker y Docker componen implementan libnetwork.

Con el archivo de configuración correcto (que debe colocarse en la versión 2), creará servicios que se verán entre sí. Y, además, también puede escalarlos con docker-compose (puede escalar cualquier servicio que desee que no vincule el puerto en el host)

Aquí hay un archivo de ejemplo

version: "2"
services:
  router:
    build: services/router/
    ports:
      - "8080:8080"
  auth:
    build: services/auth/
  todo:
    build: services/todo/
  data:
    build: services/data/

Y la referencia para esta nueva versión del archivo de redacción: https://github.com/docker/compose/blob/1.6.0-rc1/docs/networking.md

Dolanor
fuente
1

Hasta donde yo sé, usando solo Docker esto no es posible. Necesita algo de DNS para mapear las direcciones IP del contenedor a los nombres de host.

Si quieres una solución lista para usar. Una solución es utilizar, por ejemplo, Kontena . Viene con tecnología de superposición de red de Weave y esta tecnología se utiliza para crear redes LAN privadas virtuales para cada servicio y se puede acceder a cada servicio service_name.kontena.local-address.

Aquí hay un ejemplo simple del archivo YAML de la aplicación Wordpress donde el servicio Wordpress se conecta al servidor MySQL con la dirección wordpress-mysql.kontena.local:

wordpress:                                                                         
  image: wordpress:4.1                                                             
  stateful: true                                                                   
  ports:                                                                           
    - 80:80                                                                      
  links:                                                                           
    - mysql:wordpress-mysql                                                        
  environment:                                                                     
    - WORDPRESS_DB_HOST=wordpress-mysql.kontena.local                              
    - WORDPRESS_DB_PASSWORD=secret                                                 
mysql:                                                                             
  image: mariadb:5.5                                                               
  stateful: true                                                                   
  environment:                                                                     
    - MYSQL_ROOT_PASSWORD=secret
Lauri
fuente