Wie richte ich die Verknüpfung zwischen Docker-Containern ein, damit ein Neustart sie nicht unterbricht?

80

Ich habe ein paar Docker-Container, die wie folgt laufen:

  • Nginx
  • Web App 1
  • Web App 2
  • PostgreSQL

Da Nginx eine Verbindung zu den Webanwendungsservern in Webanwendung 1 und 2 herstellen muss und die Webanwendungen mit PostgreSQL kommunizieren müssen, habe ich folgende Verknüpfungen:

  • Nginx --- Link ---> Web App 1
  • Nginx --- Link ---> Web App 2
  • Web App 1 --- Link ---> PostgreSQL
  • Web App 2 --- Link ---> PostgreSQL

Das funktioniert zunächst ziemlich gut. Wenn ich jedoch eine neue Version von Web App 1 und Web App 2 entwickle, muss ich sie ersetzen. Ich entferne die Web-App-Container, richte neue Container ein und starte sie.

Für die Web-App-Container wären ihre IP-Adressen zunächst ungefähr so:

  • 172.17.0.2
  • 172.17.0.3

Und nachdem ich sie ersetzt habe, haben sie neue IP-Adressen:

  • 172.17.0.5
  • 172.17.0.6

Diese exponierten Umgebungsvariablen im Nginx-Container verweisen weiterhin auf die alten IP-Adressen. Hier kommt das Problem. Wie ersetze ich einen Container, ohne die Verbindung zwischen Containern zu unterbrechen? Das gleiche Problem tritt auch bei PostgreSQL auf. Wenn ich die PostgreSQL-Image-Version aktualisieren möchte, muss ich sie auf jeden Fall entfernen und die neue ausführen, aber dann muss ich das gesamte Container-Diagramm neu erstellen, sodass dies nicht ideal für den realen Serverbetrieb ist.

Fang-Pen Lin
quelle

Antworten:

53

Der Effekt von --linkist statisch, sodass er für Ihr Szenario nicht funktioniert (derzeit erfolgt keine erneute Verknüpfung, obwohl Sie Verknüpfungen entfernen können ).

Wir haben bei dockerize.it zwei verschiedene Ansätze verwendet, um dies zu lösen, ohne Links oder Botschafter (obwohl Sie auch Botschafter hinzufügen könnten).

1) Verwenden Sie dynamisches DNS

Die allgemeine Idee ist, dass Sie einen einzelnen Namen für Ihre Datenbank (oder einen anderen Dienst) angeben und einen kurzlebigen DNS-Server mit der tatsächlichen IP aktualisieren, wenn Sie Container starten und stoppen.

Wir haben mit SkyDock angefangen . Es funktioniert mit zwei Docker-Containern, dem DNS-Server und einem Monitor, der ihn automatisch aktualisiert. Später sind wir mit Consul zu etwas Benutzerdefinierterem übergegangen (auch mit einer Docker-Version: Docker-Consul ).

Eine Weiterentwicklung davon (die wir nicht ausprobiert haben) wäre, etcd oder ähnliches einzurichten und seine benutzerdefinierte API zu verwenden, um die IPs und Ports zu lernen. Die Software sollte auch die dynamische Rekonfiguration unterstützen.

2) Verwenden Sie die Docker Bridge IP

Wenn Sie die Container-Ports verfügbar machen, können Sie sie einfach an die docker0Bridge binden , die eine bekannte Adresse hat (oder haben kann).

Wenn Sie einen Container durch eine neue Version ersetzen, lassen Sie den neuen Container einfach denselben Port auf derselben IP veröffentlichen.

Dies ist einfacher, aber auch eingeschränkter. Es kann zu Portkonflikten kommen, wenn Sie eine ähnliche Software ausführen (z. B. können zwei Container den 3306-Port auf der docker0Bridge nicht überwachen) usw. usw. Unser aktueller Favorit ist also Option 1.

Abel Muiño
quelle
20

Links gelten für einen bestimmten Container und basieren nicht auf dem Namen eines Containers. Sobald Sie einen Container entfernen, wird die Verbindung getrennt und der neue Container (auch mit demselben Namen) wird nicht automatisch an seine Stelle gesetzt.

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
Ich möchte darauf hinweisen, dass dies nur in Version 1.9 oder höher funktioniert. Einige Distributionen müssen noch mit der neuesten Version veröffentlicht werden.
John Giotta
Eine andere Option ist die Verwendung eines Alias ​​mit Netzwerkbereich anstelle des Containernamens (der global eindeutig sein muss, ist nicht immer hilfreich). Aber die Antwort ist trotzdem absolut richtig.
Ivan Anishchuk
10

Sie können einen Botschaftercontainer verwenden . Verknüpfen Sie den Ambassador-Container jedoch nicht mit Ihrem Client, da dies das gleiche Problem wie oben verursacht. Verwenden Sie stattdessen den exponierten Port des Ambassador-Containers auf dem Docker-Host (normalerweise 172.17.42.1). Beispiel:

Postgres-Band:

$ docker run --name PGDATA -v /data/pgdata/data:/data -v /data/pgdata/log:/var/log/postgresql phusion/baseimage:0.9.10 true

Postgres-Container:

$ docker run -d --name postgres --volumes-from PGDATA -e USER=postgres -e PASS='postgres' paintedfox/postgresql

Botschafter-Container für Postgres:

$ docker run -d --name pg_ambassador --link postgres:postgres -p 5432:5432 ctlc/ambassador

Jetzt können Sie einen Postgresql-Client-Container starten, ohne den Ambassador-Container zu verknüpfen, und auf Postgresql auf dem Gateway-Host zugreifen (normalerweise 172.17.42.1):

$ docker run --rm -t -i paintedfox/postgresql /bin/bash
root@b94251eac8be:/# PGHOST=$(netstat -nr | grep '^0\.0\.0\.0 ' | awk '{print $2}')
root@b94251eac8be:/# echo $PGHOST
172.17.42.1
root@b94251eac8be:/#
root@b94251eac8be:/# psql -h $PGHOST --user postgres
Password for user postgres: 
psql (9.3.4)
SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256)
Type "help" for help.

postgres=#
postgres=# select 6*7 as answer;
 answer 
--------
     42
(1 row)

bpostgres=# 

Jetzt können Sie den Ambassador-Container neu starten, ohne den Client neu starten zu müssen.

Swen Thümmler
quelle
Wird "-p 5432: 5432" das PostgreSQL nicht der Außenwelt aussetzen?
Fang-Pen Lin
2
Ja, es wird. Wenn Sie dies nicht möchten, können Sie "-p 172.17.42.1:5432:5432" verwenden.
Swen Thümmler
1
Übrigens, warum müssen Sie diesen "PGDATA" -Container erstellen und mit einem Postgresql-Container verknüpfen? Ich verstehe nicht, warum nicht einfach einen Postgresql-Container erstellen und dessen Volume direkt einem Host-Verzeichnis zuordnen?
Fang-Pen Lin
Der PGDATA-Container wird nicht benötigt, ich benutze ihn nur zur Trennung von Bedenken. Beim Starten des posgres-Containers muss ich mich nicht daran erinnern, wie die Volumes im PGDATA-Container zugeordnet sind. Ich habe es hinzugefügt, weil ich es gerade so mache. Es ist im Grunde eine Frage des Geschmacks - ich selbst bin mir noch nicht sicher, ob es eine gute Idee ist oder nicht ...
Swen Thümmler
Es ist in der Tat die beste Vorgehensweise, einen Datenvolumencontainer wie Swen zu verwenden.
derFunk
2

Wenn noch jemand neugierig ist, müssen Sie die Hosteinträge in der Datei / etc / hosts jedes Docker-Containers verwenden und sollten nicht von ENV-Variablen abhängen, da diese nicht automatisch aktualisiert werden.

Für jeden verknüpften Container wird ein Hostdateieintrag im Format LINKEDCONTAINERNAME_PORT_PORTNUMBER_TCP usw. erstellt.

Das Folgende stammt aus Docker- Dokumenten

Wichtige Hinweise zu Docker-Umgebungsvariablen

Im Gegensatz zu Hosteinträgen in der Datei / etc / hosts werden in den Umgebungsvariablen gespeicherte IP-Adressen nicht automatisch aktualisiert, wenn der Quellcontainer neu gestartet wird. Wir empfehlen, die Hosteinträge in / etc / hosts zu verwenden, um die IP-Adresse verknüpfter Container aufzulösen.

Diese Umgebungsvariablen werden nur für den ersten Prozess im Container festgelegt. Einige Dämonen, wie z. B. sshd, schrubben sie, wenn Muscheln für die Verbindung erzeugt werden.

Frameworksnow
quelle
2

Dies ist im experimentellen Build von Docker vor 3 Wochen mit der Einführung von Diensten enthalten: https://github.com/docker/docker/blob/master/experimental/networking.md

Sie sollten in der Lage sein, eine dynamische Verknüpfung herzustellen, indem Sie einen Docker-Container mit den --publish-service <name>Argumenten ausführen . Auf diesen Namen kann über das DNS zugegriffen werden. Dies bleibt beim Neustart des Containers bestehen (solange Sie den Container mit demselben Servicenamen wie natürlich neu starten).

Laurens Rietveld
quelle
Wie installierst du diese Version? github.com/docker/docker/releases/tag/v1.8.0-rc1
m59
1
Weitere Informationen finden Sie auf dieser Seite: github.com/docker/docker/tree/master/experimental . Kurzversion: Ausführen wget -qO- https://experimental.docker.com/ | sh, um die experimentelle Version zu installieren
Laurens Rietveld
1
Diese Antwort war gültig, ist aber jetzt veraltet, da der Docker die experimentelle publish-serviceOption entfernt hat. Jetzt haben sie stattdessen Aliase mit Netzwerkbereich. Im Wesentlichen das Gleiche.
Ivan Anishchuk
1

Sie können Dockerlinks mit Namen verwenden, um dies zu lösen.

Die grundlegendste Einrichtung besteht darin, zuerst einen benannten Datenbankcontainer zu erstellen :

$ sudo docker run -d --name db training/postgres

Erstellen Sie dann einen Webcontainer, der eine Verbindung zu db herstellt:

$ sudo docker run -d -P --name web --link db:db training/webapp python app.py

Damit müssen Sie Container nicht manuell mit ihren IP-Adressen verbinden.

Pak
quelle
2
Hm ... es sieht so aus, als würde Docker den verknüpften Hostnamen irgendwie für Sie generieren, aber so wie es funktioniert, generiert es den Namen in / etc / hosts, es ist statisch, wenn ich den verknüpften Container neu starte, ändert sich die IP, aber / etc. / hosts bleiben gleich, daher funktioniert es nicht.
Fang-Pen Lin
2
Seit Docker Version 1.0 ist die Zuweisung von IP-Adressen aggressiver. Wenn Sie einen Container neu starten ( dbin diesem Fall), erhält er eine neue IP-Adresse. Ihr anderer Container (neu gestartet oder nicht) behält die ENV-Werte ab dem Moment bei, zu dem Sie ihn gestartet haben, und er ist unbrauchbar.
GermanDZ
1
fyi sieht aus wie ein Fix kommt, / etc / hosts wird aktualisiert, wenn ein verknüpfter Container neu gestartet wird: github.com/docker/docker/issues/6350
jamshid
1
Dieses Problem scheint behoben zu sein und die vorgeschlagene Methode funktioniert bei mir.
Jens Piegsa
1
Dies ist hier die richtigste Antwort. Das einzige Problem ist, dass die Verknüpfung unidirektional ist und eine Abhängigkeit zwischen Containern hinzufügt: Sie können zwei Container nicht vernetzen und den verknüpften Container nicht stoppen und dann erneut starten (mit neuen Optionen oder Ähnlichem). Verwenden Sie in jedem Fall Netzwerke (und entweder net-aliasoder den Containernamen).
Ivan Anishchuk
1

Mit dem OpenSVC-Ansatz können Sie Folgendes umgehen:

  • Verwenden Sie einen Dienst mit einer eigenen IP-Adresse / einem eigenen DNS-Namen (mit dem Ihre Endbenutzer eine Verbindung herstellen).
  • Sagen Sie Docker, dass Ports für diese bestimmte IP-Adresse verfügbar gemacht werden sollen (Docker-Option "--ip").
  • Konfigurieren Sie Ihre Apps so, dass sie eine Verbindung zur IP-Adresse des Dienstes herstellen

Jedes Mal, wenn Sie einen Container ersetzen, stellen Sie sicher, dass er eine Verbindung zur richtigen IP-Adresse herstellt.

Tutorial hier => Docker Multi Containers mit OpenSVC

Verpassen Sie nicht den Teil "Komplexe Orchestrierung" am Ende des Tutorials, mit dem Sie Container in der richtigen Reihenfolge starten / stoppen können (1 Postgresql-Teilmenge + 1 Webapp-Teilmenge + 1 Nginx-Teilmenge).

Der Hauptnachteil besteht darin, dass Sie Webapp- und PostgreSQL-Ports einer öffentlichen Adresse aussetzen und tatsächlich nur der Nginx-TCP-Port öffentlich zugänglich gemacht werden muss.

user3757286
quelle
1

Sie können auch die Ambassador-Methode ausprobieren, einen Zwischencontainer zu haben, um den Link intakt zu halten ... (siehe https://docs.docker.com/articles/ambassador_pattern_linking/ ) für weitere Informationen

Gekkie
quelle
Ambassador ist ein schönes Muster, hat jedoch das gleiche Problem: Die IP-Adresse wird beim Neustart nicht unbedingt aktualisiert. Sie eignen sich jedoch hervorragend für die Konnektivität zwischen Hosts. Nun, vielleicht mit einer neuen Docker-Version, die auch nicht benötigt wird.
Ivan Anishchuk
@ IvanAnishchuk wahr, aber zu dem Zeitpunkt, als der Kommentar abgegeben wurde, war dies der richtige Weg ... (vor +2 Jahren;))
Gekkie
0

Sie können die Verbindungsports Ihrer Images an feste Ports auf dem Host binden und die Dienste so konfigurieren, dass sie stattdessen verwendet werden.

Dies hat auch seine Nachteile, könnte aber in Ihrem Fall funktionieren.

ivant
quelle
Das Binden von localhost-Ports hat in der Tat seine Nachteile. Neue Docker-Netzwerke machen es veraltet.
Ivan Anishchuk
0

Eine andere Alternative ist die Verwendung der --net container:$CONTAINER_IDOption.

Schritt 1: Erstellen Sie "Netzwerk" -Container

docker run --name db_net ubuntu:14.04 sleep infinity
docker run --name app1_net --link db_net:db ubuntu:14.04 sleep infinity
docker run --name app2_net --link db_net:db ubuntu:14.04 sleep infinity
docker run -p 80 -p 443 --name nginx_net --link app1_net:app1 --link app2_net:app2 ubuntu:14.04 sleep infinity

Schritt 2: Injizieren von Diensten in "Netzwerk" -Container

docker run --name db --net container:db_net pgsql
docker run --name app1 --net container:app1_net app1
docker run --name app2 --net container:app1_net app2
docker run --name nginx --net container:app1_net nginx

Solange Sie die "Netzwerk" -Container nicht berühren, sollten sich die IP-Adressen Ihrer Links nicht ändern.

user1202136
quelle
Ein vom Benutzer erstelltes Brückennetzwerk mit einem aussagekräftigen Namen ist wahrscheinlich die bessere Option. Es ist nicht erforderlich, Container zu erstellen, um ihre Netzwerke zu nutzen.
Ivan Anishchuk
0

In diesem Fall benötigen Sie einen Alias ​​mit Netzwerkbereich . Es ist eine ziemlich neue Funktion, mit der ein Container "veröffentlicht" werden kann, der einen Dienst für das gesamte Netzwerk bereitstellt, im Gegensatz zu Link-Aliasen, auf die nur von einem Container aus zugegriffen werden kann.

Es wird keine Abhängigkeit zwischen Containern hinzugefügt - sie können kommunizieren, solange beide ausgeführt werden, unabhängig von Neustarts und Austausch- und Startreihenfolge. Ich glaube, es verwendet intern DNS anstelle von / etc / hosts

Verwenden Sie es folgendermaßen: docker run --net=some_user_definied_nw --net-alias postgres ...Sie können mit diesem Alias ​​von jedem Container im selben Netzwerk aus eine Verbindung herstellen.

Funktioniert nicht im Standardnetzwerk, leider müssen Sie eines mit erstellen docker network create <network>und es dann --net=<network>für jeden Container verwenden ( compose unterstützt es ebenfalls ).

Zusätzlich zum Ausfall des Containers und damit für Alias ​​nicht erreichbar, können mehrere Container auch einen Alias ​​gemeinsam nutzen. In diesem Fall kann nicht garantiert werden, dass der richtige Alias ​​aufgelöst wird. Aber in einigen Fällen kann dies wahrscheinlich bei einem nahtlosen Upgrade helfen.

Es ist noch nicht alles sehr gut dokumentiert, schwer herauszufinden, nur durch Lesen der Manpage.

Ivan Anishchuk
quelle