Comment communiquer entre les conteneurs Docker via "nom d'hôte"

90

Je prévois de diviser mon serveur monolthique en plusieurs petits conteneurs docker mais je n'ai pas encore trouvé de bonne solution pour la "communication inter-conteneurs". Voici mon scénario cible:

Scénario cible

Je sais comment lier des conteneurs entre eux et comment exposer des ports, mais aucune de ces solutions ne me satisfait.

Existe-t-il une solution pour communiquer via des noms d'hôte (noms de conteneurs) entre les conteneurs comme dans un réseau de serveurs traditionnel?

Patrick Gotthard
la source
J'ai récemment écrit un document faisant exactement ce que vous recherchez. Il décrit essentiellement comment installer plusieurs conteneurs (un par processus) et les intégrer. La "communication inter-conteneurs" fait partie du jeu .
xuhdev
Je viens de trouver le blog Tumtum et je suis tombé sur ce paragraphe dans la documentation officielle de Docker . Je ne sais pas si j'ai manqué ce paragraphe tout le temps ou s'il a été nouvellement ajouté, mais cela devrait être exactement ce dont j'ai besoin :)
Patrick Gotthard
docker 1.10 est sorti, et docker connect est génial ( github.com/docker/docker/blob/… ). Voir ma réponse modifiée ci
VonC
2
Je pense que vous devriez essayer docker-compose . Fonctionne très bien.
Suhas Chikkanna

Réponses:

27

Edit: Après Docker 1.9, la docker networkcommande (voir ci-dessous https://stackoverflow.com/a/35184695/977939 ) est le moyen recommandé pour y parvenir.


Ma solution est de configurer un dnsmasq sur l'hôte pour que l'enregistrement DNS soit automatiquement mis à jour: les enregistrements «A» ont les noms des conteneurs et pointent automatiquement vers les adresses IP des conteneurs (toutes les 10 secondes). Le script de mise à jour automatique est collé ici:

#!/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

Assurez-vous que votre service dnsmasq est disponible sur docker0. Ensuite, démarrez votre conteneur avec --dns HOST_ADDRESSpour utiliser ce service mini DNS.

Référence: http://docs.blowb.org/setup-host/dnsmasq.html

xuhdev
la source
Cela semble intéressant, beaucoup plus résistant que ma réponse --link. +1
VonC
@VonC ressemble à la nouvelle libnetwork peut remplacer cette solution de contournement. Voyons voir cependant.
xuhdev
@xuhdev J'ai configuré dnsmasq comme dans docs.blowb.org/setup-host/dnsmasq.html . Mais je rencontre des problèmes lors de l'utilisation de dig from docker container, il expire. Mais ping à l'IP de l'interface hôte docker0 fonctionne. Creusez également avec le même ip docker0 à partir de l'hôte docker fonctionne. Avez-vous des suggestions?
Satheesh le
1
@Satheesh Ce sont peut-être vos paramètres de pare-feu qui empêchent votre conteneur d'interroger le DNS de l'hôte?
xuhdev
@xuhdev merci, c'est le firewalld de ma machine hôte qui a causé le problème. Une fois que j'ai baissé le pare-feu, mon conteneur communique avec dnsmasq sur l'hôte
Satheesh
205

La nouvelle fonctionnalité de mise en réseau vous permet de vous connecter aux conteneurs par leur nom, donc si vous créez un nouveau réseau, tout conteneur connecté à ce réseau peut atteindre d'autres conteneurs par leur nom. Exemple:

1) Créer un nouveau réseau

$ docker network create <network-name>       

2) Connectez les conteneurs au réseau

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

ou

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

3) Ping conteneur par nom

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

Voir cette section de la documentation;

Remarque: contrairement à l'héritage, linksle nouveau réseau ne créera pas de variables d'environnement, ni ne partagera les variables d'environnement avec d'autres conteneurs.

Cette fonctionnalité ne prend actuellement pas en charge les alias

Hemerson Varela
la source
4
Fonctionne très bien. Pourquoi le réseau par défaut ne permet pas cela par défaut?
Stéphane
La partie la moins évidente est que vous devez redémarrer une application exécutée dans un autre conteneur. Alors, comment le conteneur A peut-il faire redémarrer une application dans le conteneur B? De toute évidence, il semble nécessaire d'avoir une sorte de bus de communication. En premier lieu, je pense utiliser Redis pour la signalisation et la communication inter-conteneurs. Donc tous les conteneurs souscrivent au canal redis et c'est là qu'ils parleront ... Que diriez-vous des changements dans les ports publiés dans docker-compose Fichier .yml nécessitant un fichier complet docker-compose down,up,restart?
eigenfield le
c'est exactement ce que je cherchais toute la journée! Je ne savais pas que vous pouviez faire référence à un nœud de réseau par son nom / identifiant de conteneur. Merci!
elliotwesoff
1
@ Stéphane Il est désactivé dans le bridgeréseau par défaut à cause de la rétrocompatibilité mais oui, je suis d'accord, il devrait certainement être activé par défaut!
helmesjo
15

Cela devrait être à quoi--link ça sert , au moins pour la partie hostname.
Avec docker 1.10 et PR 19242 , ce serait:

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

(voir la dernière section ci-dessous)

C'est ce que la mise à jour des/etc/hosts détails du fichier

En plus des variables d'environnement, Docker ajoute une entrée d'hôte pour le conteneur source au /etc/hostsfichier.

Par exemple, lancez un serveur LDAP:

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

Et définissez une image pour tester ce serveur 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

Vous pouvez exposer le ' openldap' conteneur en tant que ' internalopenldap' dans l'image de test avec --link:

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

Ensuite, si vous tapez 'lds', cet alias fonctionnera:

ldapsearch -H ldap://internalopenldap ...

Cela rendrait les gens. La signification internalopenldapest correctement atteinte à partir de l' ldaptestimage.


Bien sûr, docker 1.7 ajoutera libnetwork, qui fournit une implémentation Go native pour connecter des conteneurs. Voir le billet de blog .
Il a introduit une architecture plus complète, avec le Container Network Model (CNM)

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

Cela mettra à jour la CLI Docker avec de nouvelles commandes «réseau» et documentera comment l' -netindicateur « » est utilisé pour attribuer des conteneurs aux réseaux.


docker 1.10 a une nouvelle section Alias ​​à l'échelle du réseau , maintenant officiellement documentée dansnetwork connect :

Alors que les liens fournissent une résolution de nom privé localisée dans un conteneur, l'alias de portée réseau permet à un conteneur d'être découvert par un autre nom par tout autre conteneur dans le cadre d'un réseau particulier.
Contrairement à l'alias de lien, qui est défini par le consommateur d'un service, l'alias de portée réseau est défini par le conteneur qui offre le service au réseau.

En continuant avec l'exemple ci-dessus, créez un autre conteneur isolated_nwavec un alias réseau.

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

--alias=[]         

Ajouter un alias de portée réseau pour le conteneur

Vous pouvez utiliser l' --linkoption pour lier un autre conteneur avec un alias préféré

Vous pouvez suspendre, redémarrer et arrêter les conteneurs connectés à un réseau. Les conteneurs en pause restent connectés et peuvent être révélés par une inspection du réseau. Lorsque le conteneur est arrêté, il n'apparaît pas sur le réseau tant que vous ne le redémarrez pas.

Si spécifié, la ou les adresses IP du conteneur sont réappliquées lorsqu'un conteneur arrêté est redémarré. Si l'adresse IP n'est plus disponible, le conteneur ne démarre pas.

Une façon de garantir que l'adresse IP est disponible est de spécifier un --ip-rangelors de la création du réseau et de choisir la ou les adresses IP statiques hors de cette plage. Cela garantit que l'adresse IP n'est pas donnée à un autre conteneur tant que ce conteneur n'est pas sur le réseau.

$ 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
la source
3
Le problème de --link est que vous ne pouvez pas redémarrer un conteneur sans redémarrer également les conteneurs liés. Lorsque vous regardez mon graphique, le redémarrage du conteneur MySQL entraînera une cascade d'autres redémarrages de conteneur.
Patrick Gotthard
3

EDIT : Ce n'est plus à la fine pointe: http://blog.docker.com/2016/02/docker-1-10/

Réponse originale
Je me suis battu avec lui toute la nuit. Si vous n'avez pas peur de la pointe de la technologie , la dernière version du moteur Docker et de Docker composent les deux implémentent libnetwork.

Avec le bon fichier de configuration (qui doit être mis en version 2), vous allez créer des services qui se verront tous. Et, bonus, vous pouvez également les mettre à l'échelle avec docker-compose (vous pouvez mettre à l'échelle n'importe quel service de votre choix qui ne lie pas le port sur l'hôte)

Voici un fichier d' exemple

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

Et la référence pour cette nouvelle version de compose file: https://github.com/docker/compose/blob/1.6.0-rc1/docs/networking.md

Dolanor
la source
1

Autant que je sache, en utilisant uniquement Docker, ce n'est pas possible. Vous avez besoin de DNS pour mapper les adresses IP du conteneur aux noms d'hôte.

Si vous voulez une solution prête à l'emploi. Une solution consiste à utiliser par exemple Kontena . Il est livré avec la technologie de superposition de réseau de Weave et cette technologie est utilisée pour créer des réseaux LAN privés virtuels pour chaque service et chaque service peut être atteint par service_name.kontena.local-address.

Voici un exemple simple de fichier YAML de l'application Wordpress où le service Wordpress se connecte au serveur MySQL avec l'adresse 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
la source