Kommunikation zwischen Docker-Containern über "Hostname"

89

Ich habe vor, meinen monolthischen Server in viele kleine Docker-Container aufzuteilen, habe aber noch keine gute Lösung für die "Kommunikation zwischen Containern" gefunden. Dies ist mein Zielszenario:

Zielszenario

Ich weiß, wie man Container miteinander verbindet und wie man Ports freigibt, aber keine dieser Lösungen ist für mich zufriedenstellend.

Gibt es eine Lösung für die Kommunikation über Hostnamen (Containernamen) zwischen den Containern wie in einem herkömmlichen Servernetzwerk?

Patrick Gotthard
quelle
Ich habe kürzlich ein Dokument geschrieben , das genau das tut, wonach Sie suchen. Grundsätzlich wird dokumentiert, wie mehrere Container (einer pro Prozess) installiert und integriert werden. "Kommunikation zwischen Containern" ist Teil des Spiels .
Xuhdev
Ich habe gerade den Tumtum-Blog gefunden und bin auf diesen Absatz in der offiziellen Docker-Dokumentation gestoßen . Ich weiß nicht, ob ich diesen Absatz die ganze Zeit verpasst habe oder ob er neu hinzugefügt wurde, aber das sollte genau das sein, was ich brauche :)
Patrick Gotthard
Docker 1.10 ist out und Docker Connect ist fantastisch ( github.com/docker/docker/blob/… ). Siehe meine bearbeitete Antwort unten
VonC
2
Ich denke, Sie sollten Docker-Compose versuchen . Funktioniert sehr gut.
Suhas Chikkanna

Antworten:

27

Bearbeiten: Nach Docker 1.9 wird der docker networkBefehl (siehe unten https://stackoverflow.com/a/35184695/977939 ) empfohlen, um dies zu erreichen.


Meine Lösung besteht darin, auf dem Host ein dnsmasq einzurichten, damit der DNS-Eintrag automatisch aktualisiert wird: "A" -Datensätze haben die Namen von Containern und verweisen automatisch (alle 10 Sekunden) auf die IP-Adressen der Container. Das Skript zur automatischen Aktualisierung wird hier eingefügt:

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

Stellen Sie sicher, dass Ihr dnsmasq-Dienst auf verfügbar ist docker0. Starten Sie dann Ihren Container mit --dns HOST_ADDRESS, um diesen Mini-DNS-Dienst zu nutzen.

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

xuhdev
quelle
Das sieht interessant aus, viel belastbarer als meine --link Antwort. +1
VonC
@VonC sieht so aus, als ob das neue libnetwork diese Problemumgehung ersetzen könnte. Mal sehen.
Xuhdev
@xuhdev Ich richte dnsmasq wie in docs.blowb.org/setup-host/dnsmasq.html ein . Aber ich habe Probleme bei der Verwendung von Dig aus Docker-Container, es ist eine Zeitüberschreitung. Aber Ping an die Host Docker0-Schnittstelle IP funktioniert. Auch graben mit der gleichen docker0 IP vom Docker Host funktioniert. Hast du irgendwelche Vorschläge?
Satheesh
1
@Satheesh Vielleicht sind es Ihre Firewall-Einstellungen, die verhindern, dass Ihr Container DNS vom Host abfragt?
Xuhdev
@xuhdev danke, es ist die Firewall in meinem Host-Computer, die das Problem verursacht hat. Sobald ich mich an die Firewall gebeugt habe, kommuniziert mein Container mit dnsmasq auf dem Host
Satheesh
202

Mit der neuen Netzwerkfunktion können Sie über ihren Namen eine Verbindung zu Containern herstellen. Wenn Sie also ein neues Netzwerk erstellen, kann jeder mit diesem Netzwerk verbundene Container andere Container über ihren Namen erreichen. Beispiel:

1) Erstellen Sie ein neues Netzwerk

$ docker network create <network-name>       

2) Verbinden Sie die Container mit dem Netzwerk

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

oder

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

3) Ping-Container mit Namen

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

Siehe diesen Abschnitt der Dokumentation.

Hinweis: Im Gegensatz zu Legacy erstellt linksdas neue Netzwerk weder Umgebungsvariablen noch gibt es Umgebungsvariablen für andere Container frei.

Diese Funktion unterstützt derzeit keine Aliase

Hemerson Varela
quelle
4
Funktioniert super. Warum aktiviert das Standardnetzwerk dies nicht standardmäßig?
Stéphane
Der weniger offensichtliche Teil ist, dass Sie eine App neu starten müssen, die in einem anderen Container ausgeführt wird. Wie kann Container A eine App in Container B zum Neustart bringen? Es besteht eindeutig die Notwendigkeit eines Kommunikationsbusses. Mein Hauptanliegen ist es, Redis für die Signalisierung und die Kommunikation zwischen Containern zu verwenden. Also abonnieren alle Container den Redis-Kanal und dort werden sie sprechen ... Wie wäre es mit Änderungen an veröffentlichten Ports in Docker-Compose? .yml Datei benötigt eine vollständige docker-compose down,up,restart?
Truthadjustr
Genau das habe ich den ganzen Tag gesucht! Ich wusste nicht, dass Sie einen Netzwerkknoten anhand seines Containernamens / seiner ID referenzieren können. Danke!
Elliotwesoff
1
@ Stéphane Es ist im Standardnetzwerk bridgeaus Gründen der Abwärtskompatibilität deaktiviert, aber ja, ich stimme zu, es sollte definitiv standardmäßig aktiviert sein!
Helmesjo
15

Das sollte sein , was --linkfür ist , zumindest für den Hostnamen Teil.
Mit Docker 1.10 und PR 19242 wäre das:

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

(siehe letzten Abschnitt unten)

Das ist , was Aktualisierung der /etc/hostsDatei Details

Zusätzlich zu den Umgebungsvariablen fügt Docker der /etc/hostsDatei einen Hosteintrag für den Quellcontainer hinzu .

Starten Sie beispielsweise einen LDAP-Server:

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

Und definieren Sie ein Image, um diesen LDAP-Server zu testen:

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

Sie können den openldapContainer als ' internalopenldap' im Testbild mit --link verfügbar machen:

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

Wenn Sie dann 'lds' eingeben, funktioniert dieser Alias:

ldapsearch -H ldap://internalopenldap ...

Das würde die Leute zurückbringen. Die Bedeutung internalopenldapwird vom ldaptestBild aus korrekt erreicht .


Natürlich wird Docker 1.7 hinzugefügt libnetwork, das eine native Go-Implementierung zum Verbinden von Containern bietet. Siehe den Blog-Beitrag .
Mit dem Container Network Model (CNM) wurde eine vollständigere Architektur eingeführt.

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

Dadurch wird die Docker-CLI mit neuen "Netzwerk" -Befehlen aktualisiert und dokumentiert, wie das " -net" -Flag verwendet wird, um Containern Netzwerke zuzuweisen.


Docker 1.10 hat einen neuen Abschnitt Alias ​​mit Netzwerkbereich , der jetzt offiziell dokumentiert ist innetwork connect :

Während Links eine Auflösung von privaten Namen bieten, die in einem Container lokalisiert ist, bietet der Alias ​​mit Netzwerkbereich eine Möglichkeit, einen Container durch einen alternativen Namen von einem anderen Container im Bereich eines bestimmten Netzwerks zu erkennen.
Im Gegensatz zum Link-Alias, der vom Verbraucher eines Dienstes definiert wird, wird der Alias ​​mit Netzwerkbereich durch den Container definiert, der den Dienst dem Netzwerk anbietet.

Fahren Sie mit dem obigen Beispiel fort und erstellen Sie einen weiteren Container isolated_nwmit einem Netzwerkalias.

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

--alias=[]         

Fügen Sie einen Alias ​​mit Netzwerkbereich für den Container hinzu

Mit der --linkOption können Sie einen anderen Container mit einem bevorzugten Alias ​​verknüpfen

Sie können Container anhalten, neu starten und stoppen, die mit einem Netzwerk verbunden sind. Angehaltene Container bleiben verbunden und können durch eine Netzwerkinspektion aufgedeckt werden. Wenn der Container gestoppt wird, wird er erst im Netzwerk angezeigt, wenn Sie ihn neu starten.

Wenn angegeben, werden die IP-Adresse (n) des Containers erneut angewendet, wenn ein gestoppter Container neu gestartet wird. Wenn die IP-Adresse nicht mehr verfügbar ist, kann der Container nicht gestartet werden.

Eine Möglichkeit, um sicherzustellen, dass die IP-Adresse verfügbar ist, besteht darin, --ip-rangebeim Erstellen des Netzwerks eine anzugeben und die statische (n) IP-Adresse (n) außerhalb dieses Bereichs auszuwählen. Dadurch wird sichergestellt, dass die IP-Adresse keinem anderen Container zugewiesen wird, während sich dieser Container nicht im Netzwerk befindet.

$ 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
quelle
3
Das Problem von --link ist, dass Sie einen Container nicht neu starten können, ohne auch die verknüpften Container neu zu starten. Wenn Sie sich meine Grafik ansehen, führt ein Neustart des MySQL-Containers zu einer Kaskade anderer Container-Neustarts.
Patrick Gotthard
3

EDIT : Es ist nicht mehr auf dem neuesten Stand : http://blog.docker.com/2016/02/docker-1-10/

Ursprüngliche Antwort
Ich habe die ganze Nacht damit gekämpft. Wenn Sie keine Angst vor dem neuesten Stand haben , implementieren die neueste Version der Docker-Engine und Docker Compose beide libnetwork.

Mit der richtigen Konfigurationsdatei (die in Version 2 eingefügt werden muss) erstellen Sie Dienste, die sich alle sehen. Und als Bonus können Sie sie auch mit Docker-Compose skalieren (Sie können jeden gewünschten Dienst skalieren, der den Port auf dem Host nicht bindet).

Hier ist ein Beispiel - Datei

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

Und die Referenz für diese neue Version der Compose-Datei: https://github.com/docker/compose/blob/1.6.0-rc1/docs/networking.md

Dolanor
quelle
1

Soweit ich weiß, ist dies nicht möglich, wenn nur Docker verwendet wird. Sie benötigen DNS, um Container-IP: s Hostnamen zuzuordnen.

Wenn Sie eine sofort einsatzbereite Lösung wünschen. Eine Lösung besteht darin, beispielsweise Kontena zu verwenden . Es wird mit der Netzwerk-Overlay-Technologie von Weave geliefert. Diese Technologie wird verwendet, um virtuelle private LAN-Netzwerke für jeden Dienst zu erstellen, und jeder Dienst kann von erreicht werden service_name.kontena.local-address.

Hier ist ein einfaches Beispiel für die YAML-Datei der Wordpress-Anwendung, in der der Wordpress-Dienst eine Verbindung zum MySQL-Server mit der Adresse wordpress-mysql.kontena.local herstellt:

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
quelle