Compose: Gibt es eine Möglichkeit, den Start des Containers zu verzögern, um abhängige Dienste mit einer längeren Startzeit zu unterstützen?

Erstellt am 5. Aug. 2014  ·  314Kommentare  ·  Quelle: docker/compose

Ich habe einen MySQL-Container, dessen Start etwas Zeit in Anspruch nimmt, da Daten importiert werden müssen.

Ich habe einen Alfresco-Container, der vom MySQL-Container abhängt.

Im Moment, wenn ich fig verwende, schlägt der Alfresco-Dienst im Alfresco-Container fehl, wenn versucht wird, eine Verbindung zum MySQL-Container herzustellen ... angeblich, weil der MySQL-Dienst noch nicht lauscht.

Gibt es eine Möglichkeit, diese Art von Problem in Abb. 1 zu behandeln?

Hilfreichster Kommentar

Ja, ich würde mich für so etwas interessieren - ich wollte früher darüber posten.

Das kleinste Aufprallmuster, das ich mir vorstellen kann, um dieses Problem für uns zu beheben, wäre das Folgende:

Fügen Sie "wait" als neuen Schlüssel in fig.yml hinzu, mit einer ähnlichen Wertesemantik wie link. Docker würde dies als Voraussetzung behandeln und warten, bis dieser Container verlassen wurde, bevor er fortfährt.

Meine Docker-Datei würde also ungefähr so ​​aussehen:

db:
  image: tutum/mysql:5.6

initdb:
  build: /path/to/db
  link:
    - db:db
  command: /usr/local/bin/init_db

app:
  link:
    - db:db
  wait:
    - initdb

Beim Ausführen der App werden alle Link-Container gestartet, dann der Wartecontainer ausgeführt und erst dann zum eigentlichen App-Container übergegangen, wenn der Wartecontainer (initdb) beendet wurde. initdb würde ein Skript ausführen, das darauf wartet, dass die Datenbank verfügbar ist, dann alle Initialisierungen / Migrationen / was auch immer ausführt und dann beendet wird.

Das sind sowieso meine Gedanken.

Alle 314 Kommentare

Bei der Arbeit verpacken wir unsere abhängigen Dienste in ein Skript, das prüft, ob der Link noch besteht. Ich weiß, einer meiner Kollegen würde sich auch dafür interessieren! Persönlich halte ich es für ein Problem auf Containerebene, auf die Verfügbarkeit von Diensten zu warten, aber ich kann mich irren :)

Wir machen das gleiche mit dem Verpacken. Ein Beispiel finden Sie hier: https://github.com/dominionenterprises/tol-api-php/blob/master/tests/provisioning/set-env.sh

Es wäre praktisch, ein Einstiegspunktskript zu haben, das alle Links durchläuft und wartet, bis sie funktionieren, bevor der an ihn übergebene Befehl gestartet wird.

Dies sollte in Docker selbst integriert sein, aber die Lösung ist ein Ausweg. Ein Container sollte erst dann als gestartet betrachtet werden, wenn der von ihm bereitgestellte Link geöffnet wurde.

@bfirsh das ist mehr als ich mir vorgestellt habe, wäre aber exzellent.

Ein Container sollte erst dann als gestartet betrachtet werden, wenn der von ihm bereitgestellte Link geöffnet wurde.

Ich denke, genau das brauchen die Leute.

Im Moment werde ich eine Variation von https://github.com/aanand/docker-wait verwenden

Ja, ich würde mich für so etwas interessieren - ich wollte früher darüber posten.

Das kleinste Aufprallmuster, das ich mir vorstellen kann, um dieses Problem für uns zu beheben, wäre das Folgende:

Fügen Sie "wait" als neuen Schlüssel in fig.yml hinzu, mit einer ähnlichen Wertesemantik wie link. Docker würde dies als Voraussetzung behandeln und warten, bis dieser Container verlassen wurde, bevor er fortfährt.

Meine Docker-Datei würde also ungefähr so ​​aussehen:

db:
  image: tutum/mysql:5.6

initdb:
  build: /path/to/db
  link:
    - db:db
  command: /usr/local/bin/init_db

app:
  link:
    - db:db
  wait:
    - initdb

Beim Ausführen der App werden alle Link-Container gestartet, dann der Wartecontainer ausgeführt und erst dann zum eigentlichen App-Container übergegangen, wenn der Wartecontainer (initdb) beendet wurde. initdb würde ein Skript ausführen, das darauf wartet, dass die Datenbank verfügbar ist, dann alle Initialisierungen / Migrationen / was auch immer ausführt und dann beendet wird.

Das sind sowieso meine Gedanken.

(überarbeitet, siehe unten)

+1 auch hier. Es ist nicht sehr ansprechend, dies in den Befehlen selbst tun zu müssen.

+1 auch. Bin gerade auf dieses Problem gestoßen. Tolles Tool übrigens, macht mein Leben so viel einfacher!

+1 wäre toll das zu haben.

+1 auch. Vor kurzem auf die gleichen Probleme gestoßen

+1 auch. irgendeine Aussage von Dockerguys?

Ich schreibe Wrapper-Skripte als Einstiegspunkte für die Synchronisierung. Ich bin mir nicht sicher, ob es sinnvoll ist, einen Mechanismus in Abb. Zu haben, wenn Sie andere Ziele für Ihre Container haben, die die Orchestrierung auf andere Weise durchführen. Scheint sehr anwendungsspezifisch für mich zu sein, als solche die Verantwortung der Container, die die Arbeit erledigen.

Nach einigem Nachdenken und Experimentieren stimme ich dem irgendwie zu.

Als solche Anwendung habe ich im Grunde eine synchrone
waitfor (Host, Port) -Funktion, mit der ich auf Dienste der Anwendung warten kann
ist abhängig von (entweder über die Umgebung oder explizit erkannt
Konfiguration über CLI-Optionen).

Prost
James

James Mills / Prologic

E: [email protected]
W: prologic.shortcircuit.net.au

Am Freitag, 22. August 2014, um 18:34 Uhr, Mark Stuart [email protected]
schrieb:

Ich schreibe Wrapper-Skripte als Einstiegspunkte für die Synchronisierung im Moment.
Ich bin mir nicht sicher, ob es sinnvoll ist, einen Mechanismus in Abb. 1 zu haben, wenn Sie andere Ziele für haben
Ihre Container, die die Orchestrierung auf andere Weise ausführen. Scheint sehr
Anwendung spezifisch für mich als solche die Verantwortung der Container
die Arbeit machen.

- -
Antworte direkt auf diese E-Mail oder sieh sie dir auf GitHub an
https://github.com/docker/fig/issues/374#issuecomment -53036154.

Ja, hier sind einige grundlegende "Abhängigkeiten von" erforderlich ...
Wenn Sie also 20 Container haben, müssen Sie einfach keine Feigen hochfahren und alles beginnt mit der richtigen Reihenfolge ...
Es gibt jedoch auch eine Timeout-Option oder andere Mechanismen zur Fehlererkennung

Noch eine +1 hier. Ich habe Postgres länger als Django, um zu starten, so dass die DB nicht für den Migrationsbefehl ohne Hackery da ist.

@ahknight interessant, warum läuft die Migration während run ?

Möchten Sie die Migration nicht tatsächlich während der build -Phase ausführen? Auf diese Weise können Sie frische Bilder viel schneller starten.

Leider gibt es ein größeres Startskript für die betreffende Anwendung. Im Moment arbeiten wir zuerst außerhalb der Datenbank, indem wir nc -w 1 in einer Schleife verwenden, um auf die Datenbank zu warten, und dann DB-Aktionen ausführen. Es funktioniert, aber ich fühle mich schmutzig.

Ich hatte viel Erfolg bei dieser Arbeit in der fig build -Phase. Ich habe ein Beispiel dafür mit einem Django-Projekt (das noch in Arbeit ist): https://github.com/dnephin/readthedocs.org/blob/fig-demo/dockerfiles/database/Dockerfile#L21

Keine Notwendigkeit für den Start abzufragen. Obwohl ich mit MySQL etwas Ähnliches gemacht habe, musste ich für den Start abfragen, weil das mysqld init-Skript dies noch nicht getan hat. Dieses Postgres-Init-Skript scheint viel besser zu sein.

Folgendes habe ich mir gedacht:

Mit der Idee von Docker / Docker # 7445 könnten wir dieses Attribut "wait_for_helth_check" in Abb. 2 implementieren.
Es wäre also eine Feige, kein Docker-Problem?

Gibt es sowieso eine Möglichkeit, den TCP-Status auf dem verknüpften Container zu überprüfen? Wenn ja, dann denke ich, ist dies der richtige Weg. =)

@dnephin Kannst du etwas mehr erklären, was du in Dockerfiles machst, um dies zu unterstützen?
Kann die Build-Phase die Laufzeit nicht beeinflussen?

@ Docteurklein kann ich. Ich habe den Link von oben behoben (https://github.com/dnephin/readthedocs.org/blob/fig-demo/dockerfiles/database/Dockerfile#L21).

Die Idee ist, dass Sie alle langsameren "Setup" -Operationen während des Builds ausführen, sodass Sie beim Start des Containers nicht auf etwas warten müssen. Im Fall einer Datenbank oder eines Suchindex würden Sie:

  1. Starten Sie den Dienst
  2. Erstellen Sie die Benutzer, Datenbanken, Tabellen und Fixture-Daten
  3. Fahren Sie den Dienst herunter

Alles als ein einziger Build-Schritt. Später, wenn Sie den Datenbankcontainer mit fig up im Grunde sofort gestartet werden, und Sie können auch den Docker-Build-Cache für diese langsameren Vorgänge nutzen.

nett! Vielen Dank :)

@dnephin nett, hatte nicht daran gedacht.

+1 Dies wird definitiv benötigt.
Ein hässlicher Zeitverzögerungs-Hack würde in den meisten Fällen ausreichen, aber eine echte Lösung wäre willkommen.

Können Sie ein Beispiel geben, warum / wann es benötigt wird?

In meinem Anwendungsfall habe ich einen Elasticsearch-Server und dann einen Anwendungsserver, der eine Verbindung zu Elasticsearch herstellt. Das Hochfahren von Elasticsearch dauert einige Sekunden, daher kann ich nicht einfach fig up -d ausführen, da der Anwendungsserver beim Herstellen einer Verbindung zum Elasticsearch-Server sofort ausfällt.

Angenommen, ein Container startet MySQL und der andere startet eine App, die MySQL benötigt, und es stellt sich heraus, dass die andere App schneller startet. Aus diesem Grund haben wir vorübergehende fig up Fehler.

Kran kann dies umgehen, indem Sie Gruppen erstellen, die einzeln gestartet werden können. Sie können also die MySQL-Gruppe starten, 5 Sekunden warten und dann die anderen Dinge starten, die davon abhängen.
Funktioniert im kleinen Maßstab, ist aber keine echte Lösung.

@oskarhane nicht sicher, ob dieses "5 Sekunden warten" hilft, in einigen Fällen müssen Sie möglicherweise länger warten (oder können einfach nicht sicher sein, dass es nicht länger als 5 Sekunden dauert) ... es ist nicht sehr sicher Verlassen Sie sich auf das Warten.
Außerdem müssten Sie das Warten und Laden der anderen Gruppe manuell durchführen, und das ist irgendwie lahm, fig sollte das für Sie tun = /

@oskarhane , @dacort , @ddossot : Denken Sie daran, dass in der realen Welt Dinge abstürzen und neu starten, Netzwerkverbindungen kommen und gehen usw. Unabhängig davon , ob Fig eine bequeme Möglichkeit zum Warten auf einen TCP-Socket bietet oder nicht, sollten Ihre Container sein widerstandsfähig gegen Verbindungsfehler. Auf diese Weise funktionieren sie überall richtig.

Sie haben Recht, aber bis wir alle bereits vorhandenen Apps so repariert haben, dass sie sich beim Start ordnungsgemäß von dem Fehlen ihrer kritischen Ressourcen (wie DB) erholen (was eine großartige Sache ist, aber leider nur selten von Frameworks unterstützt wird), sollten wir sie verwenden fig start , um einzelne Container in einer bestimmten Reihenfolge mit Verzögerungen anstelle von fig up zu starten.

Ich kann sehen, wie ein Shell-Skript kommt, um fig zu steuern, um Docker zu steuern: wink:

Ich bin damit einverstanden, dass dies nicht in Feigen eingebaut wird, aber einige Ratschläge zu bewährten Methoden für das Warten auf die Bereitschaft wären gut

Ich habe in einem Code aus einem früheren Kommentar gesehen, dass dies getan wurde:

while ! exec 6<>/dev/tcp/${MONGO_1_PORT_27017_TCP_ADDR}/${MONGO_1_PORT_27017_TCP_PORT}; do
    echo "$(date) - still trying to connect to mongo at ${TESTING_MONGO_URL}"
    sleep 1
done

In meinem Fall gibt es keinen /dev/tcp Pfad, vielleicht ist es eine andere Linux-Distribution (?) - ich bin auf Ubuntu

Ich fand stattdessen diese Methode, die in Ordnung zu funktionieren scheint:

until nc -z postgres 5432; do
    echo "$(date) - waiting for postgres..."
    sleep 1
done

Dies scheint zu funktionieren, aber ich weiß nicht genug über solche Dinge, um zu wissen, ob es robust ist ... weiß jemand, ob es eine mögliche Race-Bedingung zwischen Port mit bis zu nc und Postgres-Server gibt, der wirklich akzeptieren kann Befehle?

Ich wäre glücklicher, wenn es möglich wäre, die Prüfung umzukehren. Ist es statt einer Abfrage aus den abhängigen Containern möglich, ein Signal vom Zielcontainer (dh dem Postgres-Server) an alle abhängigen Container zu senden?

Vielleicht ist es eine dumme Idee, hat jemand irgendwelche Gedanken?

@ anentropic Docker-Links sind einseitig, daher ist die Abfrage aus dem Downstream-Container derzeit die einzige Möglichkeit, dies zu tun.

Weiß jemand, ob es eine mögliche Race-Bedingung zwischen dem Port bis nc und dem Postgres-Server gibt, der wirklich Befehle akzeptieren kann?

Im allgemeinen Fall gibt es keine Möglichkeit zu wissen - es könnte für Postgres zutreffen, es könnte für andere Dienste falsch sein -, was ein weiteres Argument dafür ist, dies in Abb. 1 nicht zu tun.

@aanand Ich habe versucht, Ihren Docker / Wait-Image-Ansatz zu verwenden, bin mir aber nicht sicher, was passiert. Im Grunde genommen habe ich diesen "Orientdb" -Container, auf den viele andere NodeJS-App-Container verweisen. Dieser orientdb-Container benötigt einige Zeit, um den TCP-Port abzuhören, und dies führt dazu, dass die anderen Container den Fehler "Verbindung abgelehnt" erhalten.

Ich hoffte, dass ich diesen Fehler nicht sehe, wenn ich den Wartecontainer mit Orientdb verknüpfe. Aber leider bekomme ich es immer noch zufällig. Hier ist mein Setup (Docker Version 1.4.1, Abb. 1.0.1 auf einer Ubuntu 14.04 Box):

orientdb:
    build: ./Docker/orientdb
    ports:
        -   "2424:2424"
        -   "2480:2480"
wait:
    build: ./Docker/wait
    links:
        - orientdb:orientdb
....
core:
    build:  ./Docker/core
    ports:
        -   "3000:3000"
    links:
        -   orientdb:orientdb
        -   nsqd:nsqd

Jede Hilfe wird geschätzt. Vielen Dank.

@mindnuts das wait Bild ist eher eine Demonstration; Es ist nicht für die Verwendung in einem fig.yml geeignet. Sie sollten dieselbe Technik (wiederholtes Abrufen) in Ihrem core -Container verwenden, um auf den Start des orientdb -Containers zu warten, bevor Sie den Hauptprozess starten.

+1 hat gerade angefangen, darauf zu stoßen, als ich benutzerdefinierte Bilder ziehe, anstatt sie in der fig.yml zu erstellen. Node App schlägt fehl, weil Mongodb noch nicht fertig ist ...

Ich habe nur Stunden damit verbracht zu debuggen, warum MySQL beim manuellen Starten von WordPress mit Docker erreichbar war und warum es beim Starten mit Abb. Offline war. Erst jetzt wurde mir klar, dass Abb den MySQL-Container immer neu startet, wenn ich die Anwendung starte, sodass die WordPress-Entrypoint.sh stirbt noch keine Verbindung zu MySQL herstellen können.

Ich habe meine eigene überschriebene entrypoint.sh hinzugefügt, die 5 Sekunden wartet, bevor die echte entrypoint.sh ausgeführt wird. Dies ist jedoch eindeutig ein Anwendungsfall, der eine allgemeine Lösung benötigt, wenn es einfach sein soll, eine MySQL + WordPress-Containerkombination mit Docker / Fig zu starten.

Daher stirbt die WordPress-Datei entrypoint.sh, die noch keine Verbindung zu MySQL herstellen kann.

Ich denke, das ist ein Problem mit dem WordPress-Container.

Während ich anfangs ein Fan dieser Idee war, denke ich nach dem Lesen von https://github.com/docker/docker/issues/7445#issuecomment -56391294, dass eine solche Funktion der falsche Ansatz wäre und schlechte Praktiken fördert.

Es scheint zwei Fälle zu geben, mit denen sich dieses Problem befassen soll:

Ein Abhängigkeitsdienst muss verfügbar sein, um eine Initialisierung durchführen zu können.

Jede Containerinitialisierung sollte wirklich während build . Auf diese Weise wird es zwischengespeichert, und die Arbeit muss nicht von jedem Benutzer des Bildes wiederholt werden.

Ein Abhängigkeitsdienst muss verfügbar sein, damit eine Verbindung hergestellt werden kann

Die Anwendung sollte wirklich widerstandsfähig gegen Verbindungsfehler sein und die Verbindung erneut versuchen.

Ich nehme an, die Wurzel des Problems liegt darin, dass es keine Grundregeln dafür gibt, wessen Verantwortung es ist, auf die Bereitschaft der Dienste zu warten. Aber selbst wenn dies der Fall wäre, ist es meiner Meinung nach etwas unrealistisch zu erwarten, dass Entwickler jedem einzelnen Initialisierungsskript eine erneute Datenbankverbindung hinzufügen. Solche Skripte werden häufig benötigt, um leere Datenvolumes vorzubereiten, die gerade bereitgestellt wurden (z. B. die Datenbank erstellen).

Das Problem wäre tatsächlich viel weniger aufdringlich, wenn Fig verknüpfte Container (dh den Datenbankserver) beim Neustart des Anwendungscontainers nicht immer neu starten würde. Ich weiß nicht wirklich, warum das so ist.

Das Problem wäre tatsächlich viel weniger aufdringlich, wenn Fig verknüpfte Container (dh den Datenbankserver) beim Neustart des Anwendungscontainers nicht immer neu starten würde. Ich weiß nicht wirklich, warum das so ist.

Tatsächlich werden Container nicht nur neu gestartet, sondern zerstört und neu erstellt, da dies der einfachste Weg ist, um sicherzustellen, dass Änderungen an fig.yml werden. Wir sollten schließlich eine intelligentere Lösung implementieren, die "aktuelle Konfiguration" mit "gewünschte Konfiguration" vergleichen und nur das neu erstellen kann, was sich geändert hat.

Zurück zum ursprünglichen Problem: Ich halte es nicht für unrealistisch, von Containern eine Verbindungswiederholungslogik zu erwarten - dies ist von grundlegender Bedeutung für den Entwurf eines verteilten Systems, das funktioniert. Wenn verschiedene Skripte es gemeinsam nutzen müssen, sollte es in eine ausführbare Datei (oder ein sprachspezifisches Modul, wenn Sie keine Shell verwenden) zerlegt werden, damit jedes Skript oben nur waitfor db aufrufen kann.

@kennu was ist mit --no-recreate ? / cc @aanand

@aanand Ich meinte den unrealistischen Kommentar aus der Sicht, dass der Docker Hub bereits voll von veröffentlichten Bildern ist, die wahrscheinlich keine Verbindungswiederholungen in ihren Initialisierungsskripten behandeln, und dass es ein ziemliches Unterfangen wäre, alle dazu zu bringen, ihn hinzuzufügen. Aber ich denke, es könnte getan werden, wenn Docker Inc offizielle Richtlinien / Anforderungen veröffentlicht.

Persönlich möchte ich Container / Bilder jedoch lieber einfach halten und das zugrunde liegende System sich um das Auflösen von Abhängigkeiten kümmern lassen. Tatsächlich löst die Neustartrichtlinie von Docker möglicherweise bereits alles (wenn der Anwendungscontainer keine Verbindung zur Datenbank herstellen kann, wird er neu gestartet und erneut versucht, bis die Datenbank verfügbar ist).

Wenn Sie sich jedoch auf die Neustartrichtlinie verlassen, sollte diese standardmäßig aktiviert sein. Andernfalls verbringen die Benutzer Stunden damit, das Problem zu debuggen (wie ich es gerade getan habe). Beispielsweise verwendet Kubernetes standardmäßig RestartPolicyAlways für Pods.

irgendwelche Fortschritte in diesem Bereich? Ich möchte wiederholen, dass es nicht sinnvoll ist, zu erwarten, dass sich alle Docker-Images ändern und die gesamte Community Verbindungswiederholungspraktiken implementiert. Fig ist ein Docker-Orchestrierungswerkzeug und das Problem liegt in der Reihenfolge, in der die Dinge ausgeführt werden, sodass die Änderung in Fig vorgenommen werden muss, nicht in Docker oder der Community.

Es ist nicht sinnvoll zu erwarten, dass sich alle Docker-Images ändern und die gesamte Community Verbindungswiederholungspraktiken implementiert

Es ist nicht so, dass eine Anwendung wegen Docker oder Abb. Erneut versuchen müsste. Anwendungen sollten gegenüber unterbrochenen Verbindungen stabil sein, da das Netzwerk nicht zuverlässig ist . Jede Anwendung sollte bereits auf diese Weise erstellt werden.

Ich persönlich musste in keinem meiner Container Wiederholungsversuche implementieren, und ich brauchte auch keine Verzögerung oder Wartezeit beim Start. Ich glaube, dass die meisten Fälle dieses Problems in diese beiden Kategorien fallen (meine Verwendung von "Wiederholen" ist hier wahrscheinlich nicht großartig, ich meinte eher, dass es eine Verbindung wiederherstellen würde, wenn die Verbindung geschlossen würde, und nicht unbedingt für einen Zeitraum, in dem versucht wird, mehrere zu versuchen mal).

Wenn Sie sicherstellen, dass die gesamte Initialisierung während der "Build" -Phase erfolgt und die Verbindungen bei der nächsten Anforderung wiederhergestellt werden, müssen Sie es nicht erneut versuchen (oder auf den Start anderer Container warten). Wenn Verbindungen träge geöffnet werden (wenn die erste Anfrage gestellt wird), anstatt eifrig (während des Startvorgangs), müssen Sie vermutlich überhaupt nicht erneut versuchen.

Das Problem liegt in der Reihenfolge, in der [fig] die Dinge tut

Ich sehe bisher keine Erwähnung in dieser Diskussion. Fig startet den Start basierend auf den in der Konfiguration angegebenen Links, daher sollten Container immer in der richtigen Reihenfolge gestartet werden. Können Sie einen Testfall angeben, bei dem die Bestellung falsch ist?

Ich muss @dnephin hier zustimmen. Sicher, es wäre praktisch, wenn compose / fig in der Lage wäre, etwas zu zaubern und die Verfügbarkeit von Diensten zu überprüfen. Wie würde sich das Verhalten verhalten, wenn ein Dienst nicht reagiert? Das hängt wirklich von den Anforderungen Ihrer Anwendung / Ihres Stacks ab. In einigen Fällen sollte der gesamte Stapel zerstört und durch einen neuen ersetzt werden, in anderen Fällen sollte ein Failover-Stapel verwendet werden. Viele andere Szenarien können gedacht werden.

Compose / Fig kann diese Entscheidungen nicht treffen, und Überwachungsdienste sollten in der Verantwortung der Anwendungen liegen, die im Container ausgeführt werden.

Ich möchte vorschlagen, dass @dnephin nur Glück

Ich möchte auch das WordPress-Initialisierungsbeispiel wiederholen: Es führt ein Start-Shell-Skript aus, das eine neue Datenbank erstellt, wenn der MySQL-Container diese noch nicht hat (dies ist beim Erstellen des Docker-Images nicht möglich, da es von der abhängig ist extern gemountetes Datenvolumen). Ein solches Skript wird erheblich komplexer, wenn es generische Datenbankfehler von Fehlern "Datenbank ist noch nicht bereit" unterscheiden und eine vernünftige Wiederholungslogik innerhalb des Shell-Skripts implementieren muss. Ich halte es für sehr wahrscheinlich, dass der Autor des Bildes das Startskript niemals gegen die genannten Rennbedingungen testen wird.

Die in Docker integrierte Neustartrichtlinie bietet jedoch eine Problemumgehung, wenn Sie akzeptieren möchten, dass Container sporadisch nicht gestartet werden können, und regelmäßig Fehler in Protokollen drucken. (Und wenn Sie daran denken, es einzuschalten.)

Persönlich würde ich dafür sorgen, dass Dinge einfach funktionieren, indem Fig automatisch erkennt, welche Container-Ports einem verknüpften Container ausgesetzt sind, sie vor dem Starten des verknüpften Containers anpingt (mit einem vernünftigen Zeitlimit) und schließlich eine Konfigurationseinstellung zum Überschreiben / Deaktivieren dieser Funktionalität bereitstellt.

Dies ist beim Erstellen des Docker-Images nicht möglich, da dies vom extern bereitgestellten Datenvolumen abhängt

Wahr. Hier besteht ein Ansatz darin, den Datenbankcontainer nur einmal zu starten (falls erforderlich, mit einem anderen Einstiegspunkt / Befehl), um die Datenbank zu initialisieren, oder einen Nur-Daten-Container für die Datenbank zu verwenden, der aus demselben Image wie der Datenbankcontainer selbst erstellt wurde.

Ein solches Skript wird erheblich komplexer, wenn generische Datenbankfehler von Fehlern "Datenbank ist noch nicht bereit" unterschieden werden müssen

Compose / Fig wird dort auf dasselbe Problem stoßen; Wie überprüfe ich, ob MySQL aktiv ist und akzeptiere Verbindungen? (und PostgreSQL, und (_einfügen Sie Ihren Dienst hier_)). Auch, wo soll der "Ping" ausgeführt werden? In dem Container, den Sie starten, vom Host?

Soweit ich das beurteilen kann, enthält das offizielle WordPress-Image eine Überprüfung, ob MySQL Verbindungen im docker-entrypoint.sh akzeptiert

@thaJeztah "Fügen Sie in PHP eine einfache Wiederholungslogik für MySQL-Verbindungsfehler hinzu", verfasst von tianon vor 2 Tagen - Nice. :-) Wer weiß, vielleicht wird dies doch ein Standardansatz, aber ich habe immer noch meine Zweifel, insbesondere an dieser Art von Wiederholungsimplementierungen, die tatsächlich von allen Bildautoren getestet wurden.

Über das Port-Ping - Ich kann nicht ohne weiteres sagen, wie die optimale Implementierung aussehen würde. Ich denke, vielleicht einfache Verbindungsprüfung von einem temporär verknüpften Container und erneuter Versuch, während ECONNREFUSED erhalten wird. Was auch immer 80% (oder möglicherweise 99%) der Probleme löst, so dass Benutzer sie nicht jedes Mal selbst lösen müssen.

@ kennu Ah! Vielen Dank, ich wusste nicht, dass es erst kürzlich hinzugefügt wurde. Ich habe das Skript jetzt wegen dieser Diskussion überprüft.

Um es klar auszudrücken, ich verstehe die Probleme, die Sie haben, aber ich bin nicht sicher, ob Compose / Fig sie auf eine saubere Art und Weise lösen kann, die für alle (und zuverlässig) funktioniert. Ich verstehe, dass viele Bilder in der Registrierung keine "Schutzmaßnahmen" haben, um diese Probleme zu beheben, aber ich bezweifle, dass es die Verantwortung von Compose / Fig ist, dies zu beheben.

Nachdem ich das oben Gesagte gesagt habe; Ich denke, es wäre eine gute Sache, dies im Abschnitt Best Practices für Dockerfile zu dokumentieren.

Die Menschen sollten darauf aufmerksam gemacht werden, und es sollten einige Beispiele hinzugefügt werden, um zu veranschaulichen, wie mit "Ausfall" von Diensten umgegangen werden soll. Einschließlich eines Links zu dem WikiPedia-Artikel, den @dnephin (und möglicherweise andere Quellen) als Referenz erwähnt hat.

Ich bin auf das gleiche Problem gestoßen und mag diese Idee von

Personally, I would make Things Just Work, by making Fig autodetect which container ports are exposed to a linked container, ping them before starting the linked container (with a sane timeout), and ultimately provide a configuration setting to override/disable this functionality.

Ich denke, dies würde viele typische Anwendungsfälle lösen, wie für mich, wenn dies vom offiziellen Mongodb- Container

Ich stimme @soupdiver zu. Ich habe auch Probleme in Verbindung mit einem Mongo-Container, und obwohl er mit einem start.sh-Skript funktioniert, ist das Skript nicht sehr dynamisch und fügt eine weitere Datei hinzu, die ich in meinem Repo behalten muss (ich möchte nur eine haben Dockerfile und docker-compose.yml in meinem Node Repo). Es wäre schön, wenn es eine Möglichkeit gäbe, es einfach zum Laufen zu bringen, aber ich denke, etwas Einfaches wie ein Warte-Timer wird es in den meisten Fällen nicht schaffen.

IMO-Ping reicht nicht aus, da die grundlegende Netzwerkverbindung möglicherweise verfügbar ist, der Dienst selbst jedoch noch nicht bereit ist.
Dies ist beispielsweise beim MySQL-Image der Fall. Die Verwendung von Curl oder Telnet für die Verbindungsprüfung an den exponierten Ports wäre sicherer, obwohl ich nicht weiß, ob dies ausreichen würde. In den meisten Containern sind diese Tools jedoch nicht standardmäßig installiert.

Könnten Docker oder Feige diese Kontrollen durchführen?

Könnten Docker oder Feige diese Kontrollen durchführen?

Kurzum: _no_. Aus verschiedenen Gründen;

  • Das Ausführen eines "Pings" innerhalb eines Containers würde das Ausführen eines zweiten Prozesses bedeuten. Fig / Compose kann einen solchen Prozess nicht automatisch starten, und ich glaube nicht, dass Fig / Compose Ihren Container durch Installieren von Software (wie Curl oder Telnet) ändern soll.
  • (Wie bereits in einem früheren Kommentar erwähnt) Für jeden Dienst ist eine andere Methode erforderlich, um zu überprüfen, ob er Verbindungen akzeptiert / einsatzbereit ist. Einige Dienste benötigen möglicherweise Anmeldeinformationen oder Zertifikate, um eine Verbindung herzustellen. Fig / Compose kann nicht automatisch erfinden, wie das geht.

und ich glaube nicht, dass Fig / Compose Ihren Container ändern soll, indem Sie Software (wie Curl oder Telnet) darin installieren.

Nein, sicher nicht.

Fig / Compose kann nicht automatisch erfinden, wie das geht.

Nicht erfinden. Ich dachte mehr über eine Anweisung für Feigen oder Hafenarbeiter nach, wie man sie überprüft, z.

web:
    image: nginx
    link: db
db:
   is_available: "curl DB_TCP_ADDR:DB_TCP_PORT"

Der Befehl telnet auf dem Docker-Host ausgeführt, nicht im Container.
Aber ich denke nur laut, ich weiß, dass dies nicht die perfekte Lösung ist. Die derzeitige Verwendung benutzerdefinierter Prüfskripte für die Container könnte jedoch verbessert werden.

Der Telnet-Befehl würde auf dem Docker-Host ausgeführt, nicht im Container.

Dann müssten curl oder <name a tool that's needed> auf dem Host installiert werden. Dies könnte sogar große Sicherheitsprobleme haben (z. B. möchte jemand lustig sein und verwendet is_available: "rm -rf /" ). Abgesehen davon ist der Zugriff auf die Datenbank vom _Host_ aus keine Garantie dafür, dass sie auch aus dem Container heraus zugänglich ist.

Aber ich denke nur laut, ...

Ich weiß, und ich schätze es. Denken Sie nur, es gibt keinen zuverlässigen Weg, dies zu automatisieren, oder es würde den meisten Anwendungsfällen dienen. In vielen Fällen würden Sie mit etwas Komplexem enden (nehmen Sie zum Beispiel das Beispiel curl ; wie lange sollte es versuchen, eine Verbindung herzustellen? Wiederholen?). Eine solche Komplexität ist besser, wenn Sie sich innerhalb des Containers bewegen. Dies wäre auch nützlich, wenn der Container mit Docker und nicht mit Fig / Compose gestartet würde.

@thaJeztah Ich stimme dir vollkommen zu. Und es ist sehr wahrscheinlich, dass es keine 100% ige Lösung geben wird.

Ich werde einen Vorschlag wiederholen, den ich zuvor gemacht habe: Es würde für mich ausreichen, wenn ich in der fig.yml angeben könnte, dass dieser Container beendet werden soll, bevor dieser andere Container ausgeführt wird.

Dies würde es mir ermöglichen, einen Container zu erstellen, der weiß, wie man auf alle seine Abhängigkeiten wartet - Ports überprüfen, Datenbanken initialisieren, was auch immer - und würde Feigenwissen so wenig wie möglich erfordern.

Ich würde es so konfiguriert sehen wie:

"" "
App:
Links:
- db: db
Voraussetzungen:
- runthisfirst

runthisfirst:
Links:
- db: db
"" "

runthisfirst hat einen Link, der bedeutet, dass die Datenbank gestartet wird, um den Zugriff zu überprüfen. Die App wird erst ausgeführt, wenn runthisfirst beendet wurde (Bonuspunkte, wenn runthisfirst erfolgreich beendet werden muss).

Ist das als Antwort machbar?

KJL

Am 10. Februar 2015, um 05:28 Uhr, schrieb Tobias Munk [email protected] :

@thaJeztah https://github.com/thaJeztah Ich stimme Ihnen voll und ganz zu. Und es ist sehr wahrscheinlich, dass es keine 100% ige Lösung geben wird.

- -
Antworten Sie direkt auf diese E-Mail oder sehen Sie sie auf GitHub https://github.com/docker/fig/issues/374#issuecomment -73561930 an.

Ich habe gerade versucht, meine Shell-Skript-Starter zu migrieren, und bin auf dieses Problem gestoßen. Es wäre sogar schön, einen einfachen Sleep / Wait-Schlüssel hinzuzufügen, der nur so viele Sekunden schläft, bevor der nächste Container gestartet wird.

db:
  image: tutum/mysql:5.6
  sleep: 10
app:
  link:
    - db:db

Ich mag das aus mehreren Gründen wirklich nicht.

a) Ich denke, es ist der falsche Ort dafür
b) Wie lange schläfst du?
c) Was ist, wenn das Timeout nicht lange genug ist?

Abgesehen von den offensichtlichen Problemen denke ich wirklich nicht
Infrastruktur sollte sich darum kümmern, was die Anwendung
ist und umgekehrt. IHMO sollte die App geschrieben werden
toleranter und / oder klüger in Bezug auf die eigenen Anforderungen.

Davon abgesehen vorhandene Anwendungen und Legacy-Anwendungen
wird etwas brauchen - aber es sollte wahrscheinlich mehr zusammen sein
die Zeilen von:

a docker-compose.yml :

db:
  image: tutum/mysql:5.6
app:
  wait: db
  link:
    - db:db

Wobei wait wartet, dass "exponierte" Dienste auf db verfügbar werden.

Das Problem ist, wie bestimmen Sie das?

In den einfachsten Fällen warten Sie, bis Sie erfolgreich öffnen können
eine TCP- oder UDP-Verbindung zu den exponierten Diensten.

Dies könnte für dieses Problem übertrieben sein, aber eine gute Lösung wäre, wenn Docker ein Ereignisauslösesystem bereitstellen würde, mit dem Sie einen Auslöser von einem Container aus initiieren könnten, der zu einem Rückruf in einem anderen Container führte. Wenn Sie darauf warten, Daten in eine MySQL-Datenbank zu importieren, bevor Sie einen anderen Dienst starten, reicht es nicht aus, nur zu überwachen, ob der Port verfügbar war.

Wenn ein Einstiegspunktskript eine Warnung für Docker innerhalb des Containers festlegt (z. B. eine vordefinierte Umgebungsvariable festlegen), die ein Ereignis in einem anderen Container auslöst (möglicherweise dieselbe synchronisierte Umgebungsvariable festlegen), können Skripte auf beiden Seiten wissen, wann dies sicher ist Aufgaben sind erledigt.

Natürlich könnten wir unseren eigenen Socket-Server oder andere Mittel einrichten, aber das ist mühsam, um ein Problem mit der Container-Orchestrierung zu lösen.

@aanand Ich habe fast etwas , das mit Ihrem Warteansatz als Ausgangspunkt funktioniert. Es passiert jedoch noch etwas anderes zwischen Docker-Compose-Lauf und Docker-Lauf, bei dem der erstere zu hängen scheint, während der spätere einen Zauber wirkt.

Beispiel docker-compose.yml:

db:
  image: postgres
  ports:
    - "5432"
es:
  image: dockerfile/elasticsearch
  ports:
    - "9200"
wait:
  image: n3llyb0y/wait
  environment:
    PORTS: "5432 9200"
  links:
    - es
    - db

dann mit ...

docker-compose run wait

das soll aber nicht sein. Die verknüpften Dienste werden gestartet und es sieht so aus, als würden wir nur darauf warten, dass sie ersticken (zumindest in meiner Virtualbox-Umgebung. Ich komme zur nc -Schleife und wir bekommen dann einen einzelnen Punkt ... nichts).

Wenn die verknüpften Dienste ausgeführt werden, kann ich diese Methode verwenden (was ich im Wesentlichen für unsere CI-Builds getan habe).

docker run -e PORTS="5432 9200" --links service_db_1:wait1 --links service_es_1:wait2 n3llyb0y/wait

Es fühlt sich so an, als ob docker-compose run auf die gleiche Weise funktionieren sollte. Der Unterschied besteht darin, dass Sie bei Verwendung von docker-compose run mit dem Trennflag -d keinen Wartevorteil erhalten, da der Hintergrund des Wartecontainers und ich denke (zu diesem Zeitpunkt), dass die Nichtverwendung des Flags das Warten verursacht an den anderen Diensten ohne Hintergrund zu ersticken. Ich werde genauer hinsehen

Nach einigem Ausprobieren scheint der obige Ansatz zu funktionieren! Es ist nur so, dass die Busybox-Basis kein Netcat-Util hat, das sehr gut funktioniert. Meine modifizierte Version des Dienstprogramms @aanand wait funktioniert gegen Docker-Compose 1.1.0, wenn docker-compose run <util label> anstelle von docker-compose up . Anwendungsbeispiel im Link.

Ich bin mir nicht sicher, ob es Verkettungssituationen gemäß der ursprünglichen Frage bewältigen kann. Wahrscheinlich nicht.

Lass mich wissen was du denkst.

Dies ist ein sehr interessantes Thema. Ich denke, es wäre wirklich interessant zu sehen, wie ein Container wartet, bis ein anderer fertig ist. Aber wie jeder sagt, was bedeutet bereit? In meinem Fall habe ich einen Container für MySQL, einen anderen, der seine Backups verwaltet und auch für den Import einer ersten Datenbank zuständig ist, und dann die Container für jede App, die die Datenbank benötigt. Es ist offensichtlich, dass es nicht ausreicht, zu warten, bis die Ports verfügbar sind. Zuerst muss der MySQL-Container gestartet werden und dann sollte der Rest warten, bis der MySQL-Dienst einsatzbereit ist, nicht vorher. Um dies zu erreichen, musste ich ein einfaches Skript implementieren, das beim Neustart ausgeführt wird und die Funktionalität docker exec verwendet. Grundsätzlich wäre der Pseudocode wie folgt:

run mysql
waitUntil "docker exec -t mysql mysql -u root -prootpass database -e \"show tables\""
run mysql-backup
waitUntil "docker exec -t mysql mysql -u root -prootpass database -e \"describe my_table\""
run web1
waitUntil "dexec web1 curl localhost:9000 | grep '<h1>Home</h1>'"
run web2
waitUntil "dexec web2 curl localhost:9000 | grep '<h1>Home</h1>'"
run nginx

Wobei die Funktion waitUntil eine Schleife mit einer Zeitüberschreitung hat, die den Befehl docker exec … auswertet und prüft, ob der Beendigungscode 0 ist.

Damit versichere ich, dass jeder Container wartet, bis seine Abhängigkeiten einsatzbereit sind.

Ich denke, es könnte eine Option sein, sich in das Compose-Dienstprogramm zu integrieren. Vielleicht so etwas, bei dem wait_until eine Liste anderer Abhängigkeiten (Container) deklariert und auf jede wartet, bis sie auf den entsprechenden Befehl in Ordnung reagieren (oder mit einem optionalen Muster oder einem regulären Ausdruck, um zu überprüfen, ob das Ergebnis mit übereinstimmt etwas, das Sie erwarten, obwohl die Verwendung des Befehls grep ausreichen könnte).

mysql:
  image: mysql
  ...
mysql-backup:
  links:
   - mysql
  wait_until:
   - mysql: mysql -u root -prootpass database -e "show tables"
  ...
web1:
  links:
   - mysql
  wait_until:
   - mysql: mysql -u root -prootpass database -e "describe my_table"
  ...
web2:
  links:
   - mysql
  wait_until:
   - mysql: mysql -u root -prootpass database -e "describe my_table"
  ...
nginx:
  links:
   - web1
   - web2
  wait_until:
   - web1: curl localhost:9000 | grep '<h1>Home</h1>'
   - web2: curl localhost:9000 | grep '<h1>Home</h1>'
  ...

Was ist nicht einfach für den Hafen wie diesen?
http://docs.azk.io/en/azkfilejs/wait.html#

@robsonpeixoto : Das Warten auf den Port reicht für viele Anwendungsfälle nicht aus. Angenommen, Sie erstellen eine Datenbank mit Daten bei der Erstellung und möchten nicht, dass der Webserver gestartet und eine Verbindung hergestellt wird, bis der Datenvorgang abgeschlossen ist. Der Port ist die ganze Zeit geöffnet, damit der Webserver nicht gestartet wird.

So etwas wie WaitCondition von AWS CloudFormation wäre schön. http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-waitcondition.html

+1 Ich habe das gleiche Problem, wenn ich Docker zum Testen meiner Rails-Apps verwende, die von MySQL abhängen

+1 Ich habe auch dieses Problem. Ich mag die @ adrianhurt- Idee, bei der Sie tatsächlich die zu bewertende Bedingung

+1

Ich habe diesen Tab für eine Weile geöffnet: http://crosbymichael.com/docker-events.html ... scheint relevant zu sein

+1

+1 für einfaches Timeout

+1 für einen Bereitschaftszustand

+1

Ich löse dies seit einiger Zeit sehr zuverlässig auf Anwendungsebene, wie es in diesem Thread empfohlen wurde.

Um Ihnen eine Vorstellung davon zu geben, wie dies für MySQL + PHP implementiert werden kann, hier ist mein Code.

Von igorw / retry :)

Da das Netzwerk zuverlässig ist, sollten die Dinge immer funktionieren. Habe ich recht? In den Fällen, in denen dies nicht der Fall ist, wird erneut versucht.

+1

@ schmunk42 Nettes Zeug - Ich mag es, dass es ein gutes Beispiel ist, um sowohl die Verbindung herzustellen als auch eine idempotente Datenbank-Setup-Operation durchzuführen.

Könnte gut sein, ein (einige) grundlegende Beispiel (e) für die Aufnahme in die Dokumente zu erstellen, für verschiedene Fälle, z. B. NodeJS, Ruby, PHP.

+1 sollte zumindest einige Optionen bieten, um eine Verzögerung hinzuzufügen, bevor der Container erfolgreich gestartet wird.

+1

So lösen Sie Probleme, wenn Sie versuchen, Dienste zu verbinden, die nicht Ihr Code sind.
Zum Beispiel, wenn Sie den Service Service und die Datenbank InfluxDB . Services erfordert InfluxDB und InfluxDB hat einen langsamen Start.

Wie kann Docker-Compose warten, bis InfluxDB fertig ist?

Wenn der Code mein ist, kann ich ihn lösen, indem ich es erneut versuche. Aber für die dritte App kann ich den Code nicht ändern.

@robsonpeixoto Es gibt einige Beispiele in diesem Ticket mit Netcat oder ähnlichen Methoden. Sie können sich mein MySQL-Beispiel in einem anderen Ticket ansehen: https://github.com/docker/docker/issues/7445#issuecomment -101523662

Das ist der Grund, warum ich denke, dass jeder Container die optionale Fähigkeit haben sollte, seine eigene Bereitschaft anzuzeigen. Bei einer Datenbank möchte ich beispielsweise warten, bis der Dienst vollständig bereit ist, und nicht, wenn der Prozess erstellt wird. Ich löse dies mit benutzerdefinierten Überprüfungen mit docker exec und überprüfe, ob es beispielsweise eine einfache Abfrage lösen kann.

Ein optionales Flag für docker run zur Angabe eines internen Überprüfungsbefehls eignet sich hervorragend, um ihn später von einem anderen Container aus mit einem speziellen Flag für die Verknüpfung zu verknüpfen.

Etwas wie:

$ sudo docker run -d --name db training/postgres --readiness-check /bin/sh -c "is_ready.sh"
$ sudo docker run -d -P --name web --link db:db --wait-for-readiness db training/webapp python app.py

Wobei is_ready.sh ein einfacher boolescher Test ist, der für die Entscheidung zuständig ist, wann der Container als bereit betrachtet wird.

+1

@schmunk42 nice quote!

+1

+1

+1

+1

+1

+1

+1

+1

+1

Eigentlich habe ich es mir anders überlegt, also -1

Es ist sinnvoller, wenn Ihr Container prüft, ob der Dienst eines Drittanbieters verfügbar ist. Dies ist problemlos mit einem kleinen Bash-Wrapper-Skript möglich, das beispielsweise nc .

Sich auf eine Verzögerung zu verlassen ist verlockend, aber eine schlechte Lösung, weil:

  • Ihr Container wartet immer X Sekunden, bevor er fertig ist.
  • In einigen Fällen reichen X Sekunden möglicherweise immer noch nicht aus (z. B. schwere E / A oder CPU auf dem Host), sodass Ihr Container immer noch nicht ausfallsicher ist.
  • Keine Fehlerstrategie.

Es ist besser, sich auf das Schreiben eines Wrapper-Bash-Skripts zu verlassen, weil:

  • Ihr Container ist so schnell wie möglich fertig.
  • Sie können jede Fehlerstrategie implementieren, z. B. 10 Mal versuchen, dann fehlschlagen, für immer versuchen usw. Sie können die Verzögerung sogar selbst implementieren, indem Sie vor dem Versuch schlafen!

Beim Lesen dieses Threadnought sehe ich, dass niemand Geheimnisse erwähnt. Ich versuche, einen Nur-Daten-Container zu verwenden, der nach seiner Ausführung Geheimnisse anfordert. Das Problem, das ich habe: Wenn das Übertragen / Entschlüsseln meiner Geheimnisse zu lange dauert, schlägt mein abhängiger Container fehl, weil die erwarteten Daten nicht vorhanden sind. Ich kann die Methode "Alles gut in den Container legen, bevor Sie ihn ausführen" nicht wirklich anwenden, weil sie Geheimnisse sind.

Ich weiß, dass es aufgrund des Kontexts des Rückkehrcodes einige Unklarheiten bei reinen Datencontainern gibt, aber gibt es einen besseren Weg, dies zu tun?

Ähnlich ändere ich meine Meinung dazu, -1. Der Ansatz von

Wenn wir nun Entwickler von Anwendungen / Bibliotheken / Frameworks dazu bringen könnten, diese Verantwortung zu erkennen und zu unterstützen, wäre das fantastisch.

In Anbetracht der Ansätze, die Sie wählen würden, ist es schwierig, andere Dämonen von der Seite zu laden, was nicht empfohlen wird. In meinem Beispiel, in dem eine Rails-App versucht, eine Verbindung zu einer MySQL-Datenbank herzustellen, während eine andere Rails-App gerade die DB beim ersten Start migriert und ausgibt, möchte ich die Rails-App darüber informieren, dass sie nicht versucht, die DB zu verwenden Sie müssen die ActiveRecord-Bibliothek modifizieren (was nicht passieren wird) oder ein Skript ausführen, das ständig überprüft, ob die Datenbank migriert und gesetzt wurde. Aber woher weiß ich sicher, ohne zu wissen, welche Daten dort enthalten sein sollen und / oder ob ein Skript auf dem System ausgeführt wird, das die Datenbank ausgibt, um den Rest über die Verbindung zu informieren.

Vielleicht fehlt mir die offensichtliche Lösung, aber Ihre Antwort "Entwickler sollten in der Lage sein, dies in ihrem eigenen Code zu behandeln" bricht zusammen, wenn Sie Standardbibliotheken verwenden und wenn Sie keine Dämonen von der Seite laden sollen ein Container.

@mattwallington Ich bin mir nicht mal sicher, wie Compose eine Lösung für diese Situation bieten würde ...
Das ist auch furchtbar spezifisch, was es für Compose noch schwieriger machen würde, etwas zu erfinden. Ich würde einige der obigen Tipps von

Ich denke, Sie haben den Punkt meiner letzten Zeile verpasst. Viele Standardbibliotheken funktionieren nicht genau, weil sie nicht belastbar erstellt wurden. Es gibt keine magische Lösung, die all das lösen kann, was Compose nur implementieren kann.

Verstanden. Mein früherer Vorschlag, der für viele dieser Anwendungsfälle funktionieren würde, ist eine gemeinsame Umgebungsvariable zwischen Containern. Eine Seite könnte die Abfrage der Variablen blockieren und die andere Seite könnte die Aktion ausführen und dann die Variable festlegen.

+1 @mattwallington Idee einer gemeinsam genutzten Umgebungsvariablen zwischen Containern

Vielen Dank. Es ist einfach (auf Produktebene. Ich habe keine Ahnung, was es von der Entwicklungsseite her bedeuten würde, da ich mir den Code nicht angesehen habe), aber es würde viele dieser Probleme lösen und wahrscheinlich viele andere, da es nicht spezifisch ist dieses Problem.

Aber woher weiß ich sicher, ohne zu wissen, welche Daten dort enthalten sein sollen und / oder ob ein Skript auf dem System ausgeführt wird, das die Datenbank ausgibt, um den Rest über die Verbindung zu informieren.

@mattwallington : Überprüfen Sie die Migrationsnummer in der Schematabelle. Wenn die Nummer korrekt ist, wissen Sie, dass die Migration ausgeführt wurde.

Es sollte kein Bash-Wrapper-Skript oder eine Logik in Compose oder Docker sein, es liegt in der Verantwortung der Anwendung selbst. Alles, was sich nicht auf Anwendungsebene befindet, funktioniert auch nur bei der Initialisierung. Wenn dieser Dienst ausfällt, wird ein Wrapper-Skript oder etwas anderes nicht ausgeführt.

@ agilgur5 : Ja, ich bin damit einverstanden, dass es von der Anwendung verarbeitet wird, aber ein Bash-Skript ist eine einfache Lösung, um Anwendungen zu verarbeiten, die nicht auf diese Weise codiert sind, z. B. indem die App neu gestartet wird, wenn der Dienst nicht verfügbar ist.

Es kann den ganzen Tag über darüber gestritten werden, was auf App-Ebene getan werden sollte oder könnte, aber anstatt zu erwarten, dass jede App auf dem Markt damit umgeht und bei der Selbstwiederherstellung großartig wird (unwahrscheinlich), warum sind wir so dagegen, einige Funktionen hinzuzufügen, die gelöst werden können Dieses Problem für Apps, die im Docker ausgeführt werden, unabhängig davon, wie die Apps von Drittanbietern geschrieben wurden oder was sie tun SOLLTEN, aber nicht tun. Darüber haben wir die Kontrolle. Lösen wir das Problem, anstatt zu entscheiden, wer es lösen soll, da wir keine Kontrolle darüber haben.

Ich stimme @mattwallington zu. Sie können diesen zusätzlichen Aufwand für die Selbstwiederherstellung auf App-Ebene von jedem einzelnen Entwickler jedes einzelnen Container-Images verlangen, aber ein großer Prozentsatz von ihnen ist mit Sicherheit zu ignorant oder einfach zu beschäftigt, um es sorgfältig zu implementieren und zu testen. Das Endergebnis wird sein, dass einige Container wissen, wie sie sich selbst wiederherstellen können, während viele dies nicht tun. Und als Benutzer haben Sie keine Tools, mit denen Sie diejenigen verwalten können, die dies nicht tun.

Eine Idee, die mir gerade in den Sinn kam: Anstatt das Problem durch Verzögerung des Container-Starts zu lösen, könnte Compose versuchen, den ausgefallenen Container wiederherzustellen.

So etwas wie recover: auto würde den ausgefallenen Container 5 Mal in 2, 4, 8, 16 und 32 Sekunden neu starten und dann vollständig aufgeben.

Hat jemand an den Begriff eines Container-Abhängigkeits-Containers gedacht?

Zum Beispiel:

`` `#! yml
db:
Bild: MySQL

warten auf:
Links:
- db
Bände:
- /var/lib/docker.sock:/docker.sock
- $ {PWD} /docker-compose.yml:/docker-compose.yml
Befehl: Docker-Compose Up -D App

App:
Bild: myuser / myapp
Links:
- db
`` `

Die Grundidee hier ist, dass Sie das Problem für Container ohne Selbstwiederherstellungsmechanismen lösen, indem Sie einen dedizierten wiederverwendbaren Dienst erstellen, der im Docker Hub veröffentlicht werden kann und den jeder dann einfach in seine Komposition einfügen kann.

Ich wäre sogar bereit, einen solchen Service / Container / Image zu prototypisieren und andere damit spreizen zu lassen, um zu sehen, wie es abschneidet ...

@prologic Das Problem mit einer Abhängigkeit ist, wie stellen Sie sicher, dass der Dienst, mit dem Sie sprechen möchten, tatsächlich aktiv ist?

Ihr db -Container reagiert möglicherweise auf ein ping jedoch vor dem Start eine Bereinigung / Initialisierung der Datenbank durch, bevor sie tatsächlich für mysql / psql -Befehle verfügbar ist.

Kann dieser Test auf konfigurierbare Weise definiert und / oder in einem Skript an einen wiederverwendbaren Dienst vom Typ waitfor übergeben werden?

IMHO, dies ist ein sehr häufiges Problem und jeder hat seine eigenen spezifischen Anforderungen. Wie bereits in dieser Ausgabe erwähnt, könnte (und sollte) Docker einem Container die Möglichkeit bieten, einen einfachen Befehl zur Überprüfung seiner eigenen Bereitschaft anzugeben. Natürlich sollten wir als Entwickler speziell angeben müssen, wie die Bereitschaft jedes Containers überprüft werden soll.

Ein optionales Flag für den Docker-Lauf zur Angabe eines internen Überprüfungsbefehls eignet sich hervorragend, um ihn später von einem anderen Container aus mit einem speziellen Flag für die Verknüpfung zu verknüpfen.

Etwas wie:

$ sudo docker run -d --name db training/postgres --readiness-check /bin/sh -c "is_ready.sh"
$ sudo docker run -d -P --name web --link db:db --wait-for-readiness db training/webapp python 

Wobei is_ready.sh ein einfacher boolescher Test ist, der für die Entscheidung zuständig ist, wann der Container als bereit betrachtet wird.

Es könnte auch ein Befehl für einen Container sein, seine Bereitschaft manuell zu überprüfen.

Wobei is_ready.sh ein einfacher boolescher Test ist, der für die Entscheidung zuständig ist, wann der Container als bereit betrachtet wird.

Dies bedeutet, dass jeder Entwickler seine Bilder / Container so vorbereiten sollte, dass sie etwas enthalten, mit dem überprüft werden kann, ob der Container bereit ist.

Das bringt uns zurück zu Platz 1.; Entwickler sind dafür verantwortlich, dass ihre Container unempfindlich gegen Service-Ausfall / Startzeit sind, da sie als einzige erkennen können, was dies für ihre Situation bedeutet.

Oder übersehen Sie hier etwas?

Genau. Die Verantwortung liegt beim Entwickler / Container / Service

Am Donnerstag, den 30. Juli 2015, Sebastiaan van Stijn [email protected]
schrieb:

Wobei is_ready.sh ein einfacher boolescher Test ist, der für die zuständig ist
Entscheidung, wann der Container als fertig gilt.

Das bedeutet, dass jeder Entwickler seine Bilder / Container darauf vorbereiten sollte
Fügen Sie etwas hinzu, mit dem überprüft werden kann, ob der Container vorhanden ist
bereit.

Das bringt uns zurück zu Platz 1.; Entwickler sind dafür verantwortlich
machen ihre Container widerstandsfähig gegen Service-Ausfall / Startzeit, weil
Sie sind die einzigen, die sagen können, was das für ihre Situation bedeutet.

Oder übersehen Sie hier etwas?

- -
Antworte direkt auf diese E-Mail oder sieh sie dir auf GitHub an
https://github.com/docker/compose/issues/374#issuecomment -126278215.

James Mills / Prologic

E: [email protected]
W: prologic.shortcircuit.net.au

Ja natürlich. Für mich ist der einzige, der wirklich weiß, wann ein Container fertig ist, der eigene Container. Docker kann nichts über den Inhalt eines Containers wissen. Es ist eine Black Box. Das einzige, was es tun könnte, ist, den Container zu fragen (mit einer benutzerdefinierten Aktion, die angegeben wird, wenn Sie ausgeführt werden möchten, wie ich vorgeschlagen habe, oder auf eine andere übliche Weise, um ihn zu testen). Und offensichtlich ist der Entwickler der einzige, der weiß, was er braucht und welchen Inhalt diese Black Box hat.

Ja das ist richtig!

Am Donnerstag, 30. Juli 2015, schrieb adrianhurt [email protected] :

Ja natürlich. Für mich der einzige, der wirklich weiß, wann ein Container ist
fertig ist der eigene behälter. Docker kann nichts über den Inhalt von wissen
ein Container. Es ist eine Black Box. Das einzige, was es tun könnte, ist die zu fragen
Container (mit einer benutzerdefinierten Aktion, die angegeben wird, wenn Sie ausgeführt werden möchten, wie ich
vorgeschlagen oder eine andere übliche Methode, um es zu testen). Und natürlich der Entwickler
ist der einzige, der weiß, was er braucht und welchen Inhalt diese Black Box hat.

- -
Antworte direkt auf diese E-Mail oder sieh sie dir auf GitHub an
https://github.com/docker/compose/issues/374#issuecomment -126285056.

James Mills / Prologic

E: [email protected]
W: prologic.shortcircuit.net.au

OK - nehmen wir an, wir wollen dieses Problem schließlich lösen, um die gesamte in der Entwicklung befindliche oder ältere Software zu erhalten, die Netzwerkfehler nicht bewältigen kann. Ich sage nicht, dass wir es tun, ich möchte nur ein Gefühl für Syntax, Semantik und Komplexität bekommen.

Die Mindestanforderungen scheinen zu sein:

  • Ich möchte, dass Compose wartet, um einen Dienst zu starten, bis ein anderer Dienst "bereit" ist.
  • Ich möchte "bereit" definieren als "akzeptiert TCP-Verbindungen auf Port X" oder etwas anderes.

Nehmen wir auch an, dass Health Checks es für eine Weile nicht in Docker schaffen.

Ich frage mich, ob es im allgemeinen Fall gelöst werden könnte, indem es möglich gemacht wird, auf das Beenden der Container eines anderen Dienstes zu warten. Sie könnten dann Ihren Gesundheitscheck als nur einen weiteren Dienst schreiben.

web:
  image: mywebapp
  links: ["db"]
  wait_for: ["db_wait"]

db_wait:
  image: netcat
  links: ["db"]
  command: sh -c "while ! nc -w 1 -z db 5432; do sleep 1; done"

db:
  image: postgres

Wenn Sie eine benutzerdefinierte Integritätsprüfung wünschen, definieren Sie diese im Dienst "Warten". Hier wird db_wait erst beendet, wenn mytable in der mydb -Datenbank vorhanden ist:

db_wait:
  image: postgres
  links: ["db"]
  command: sh -c "while ! psql --host db --dbname mydb -c "\d mytable"; do sleep 1; done"

Wenn Sie ein Datenbankvorbereitungsskript haben, das zuerst ausgeführt werden soll, können Sie das zum Warten machen:

web:
  image: mywebapp
  links: ["db"]
  wait_for: ["prepare_db"]

prepare_db:
  image: prepare_db
  links: ["db"]
  command: ./prepare.sh

db:
  image: postgres

Der erste Fall (warten Sie, bis der Container TCP-Verbindungen akzeptiert) ist möglicherweise häufig genug, um sofort unterstützt zu werden.

web:
  image: mywebapp
  links: ["db"]
  wait_for_tcp: ["db:5432"]

db:
  image: postgres

All dies hat eine versteckte Implikation: docker-compose up -d müsste blockiert werden, während der zwischengeschaltete Integritätsprüfungs- oder Vorbereitungsdienst ausgeführt wird, damit er die Verbraucherdienste starten kann, sobald dies erledigt ist.

Ja. Meiner Meinung nach sollte Docker selbst eine Möglichkeit bieten, um festzustellen, wann ein Container bereit ist, und dann könnte Compose ihn verwalten. Wir könnten dann ein neues Problem direkt im Docker ansprechen. Für mich könnte es so etwas sein wie:

web:
  image: mywebapp
  links: ["db"]
  wait_for: ["db"]

db:
  image: postgres
  ready_when: sh -c "while ! psql --host db --dbname mydb -c "\d mytable"; do sleep 1; done"

Dann müssen keine neuen Services als Problemumgehung erstellt werden

Einverstanden. Geben Sie dem Entwickler die Flexibilität. Das Anhalten des Containers ist möglicherweise auch nicht das, was der Entwickler von diesem Container benötigt, während er wartet. Vielleicht muss ein eigener Init passieren, aber warten Sie, bis die Datenbank für Verbindungen bereit ist. Wenn es sich also um eine einfache gemeinsam genutzte env-Variable handelt, kann der Entwickler sie nach Bedarf verwenden.

Wie würde eine gemeinsam genutzte Umgebungsvariable aktualisiert? Soweit mir bekannt ist, können Sie nach dem Start keine Änderungen an den Umgebungsvariablen eines Prozesses vornehmen.

Sie können sie beispielsweise in einer Bash-Sitzung ändern, sie werden jedoch nicht in allen anderen Ebenen weitergegeben.

Dazu muss die Änderung festgeschrieben und der Container neu gestartet werden. Dies ist in diesem Szenario ziemlich nutzlos.

Selbst wenn sich die Variable ändern könnte, müsste der Prozess im Container wissen, um sie abzufragen. Dies macht dies zu einer Nichtlösung für die Legacy-Software, für die diese Funktion angeblich vorgesehen ist.

Da es sich eigentlich nur um die Lösung dieses Problems für Legacy-Container in Entwicklungsumgebungen handelt, könnte es meines Erachtens als Tool gelöst werden, das auf der Erstellung von docker-compose ps -s (Liste der Dienste in Abhängigkeitsreihenfolge, # 1077) aufbaut.

Mit diesen beiden Befehlen könnte ein Tool geschrieben werden, das ungefähr so ​​funktioniert:

  1. Führen Sie docker-compose ps -s , um die Liste der Dienstnamen in Abhängigkeitsreihenfolge abzurufen
  2. Führen Sie docker-compose up -d --no-recreate <first service from the list>
  3. Führen Sie den Befehl "healthcheck" für diesen Dienst aus, bis er fehlerfrei ist oder das Timeout erreicht. Dies kann eine HTTP-Anfrage oder ein docker exec -Anruf sein
  4. Wiederholen Sie 2 und 3 für jeden Dienst in der Liste
  5. Führen Sie docker-compose logs (oder nicht, wenn -d wird)

Auf diese Weise können die Konfigurationen "Healthcheck" und "Warten auf" extern sein, aber ich denke nicht, dass der Entwickler etwas anderes bereitstellen muss, als erforderlich wäre, wenn es als Teil von Compose selbst implementiert würde.

Gibt es einen Grund, warum dies nicht funktionieren würde?

Ich bin froh, dass wir den Umfang auf ältere Container beschränkt haben. Das ist viel vernünftiger: +1:

@dnephin Ich finde das aus der unmittelbaren Perspektive großartig, dass es flexibel ist und dass Compose keine eingebaute Unterstützung für Legacy-Container haben muss, aber ich sehe ein unmittelbares Problem ähnlich dem, was @mattwallington beschrieben hat, was, wenn die anderen Container dies können Führen Sie einige Dinge aus (z. B. init), bevor Sie eine Verbindung zu diesem Dienst herstellen. Das Blockieren dieses Containers funktioniert, ist aber nicht ideal (ich bin mir jedoch nicht sicher, ob es eine ideale Lösung für einen Legacy-Container gibt). Das würde zumindest mein Problem lösen, jetzt muss ich das Ticket finden!

+1

um Abhängigkeiten in Docker-Compose-Dateien angeben zu können ...

... keine Links verwenden (da diese nicht mit net = host kompatibel sind). Ich hätte gedacht, es ist die Aufgabe oder zumindest das Anliegen eines Multi-Container-Management-Tools, zu wissen, in welcher Reihenfolge die Dinge starten sollen, ähnlich wie Puppet einen eigenen Abhängigkeitsbaum hat, aber manchmal weiß der Benutzer es am besten und kann es überschreiben. Wenn ich all das lese, scheint es mir die einzige Schwierigkeit zu sein, zu entscheiden, wann ein Container "in Betrieb" ist, damit der nächste Container in der Abhängigkeitskette gestartet werden kann. Dies könnte genau der gleiche Mechanismus sein wie der Link-Mechanismus (vorerst) - alles ist besser als nichts. Später könnte die Klarstellung des "Erfüllens einer Abhängigkeit" beispielsweise durch die Docker-Compose-Datei festgelegt werden - für diese Abhängigkeit ist es wichtig, dass der Container ausgeführt wird

depends_on:
  container: foo
  requires: running

Für diese Abhängigkeit ist es wichtig, dass die TCP-Ports der Container empfangsbereit sind.

depends_on:
  container: foo
  requires: listening

Zu sagen, dass es die Aufgabe eines externen Tools oder Skripts auf Docker-Compose ist, ist gleichbedeutend damit, dass Docker-Compose kein wirkliches Interesse oder keine Verantwortung für die Orchestrierung der Ausführung von 2 oder mehr Containern auf demselben Computer hat. Also, was ist sein Zweck?

Ich hätte gedacht, es ist die Aufgabe oder zumindest das Anliegen eines Multi-Container-Management-Tools, zu wissen, in welcher Reihenfolge die Dinge beginnen sollen

Nein, nicht unbedingt. Aus den Gründen, die bereits mehrfach in diesem Thread dargelegt wurden, gibt es meines Erachtens nur zwei Gründe, um diesen Job an ein Container-Management-Tool zu übergeben:

  1. Sie führen Standard-Container-Images aus, die nicht für die Nichtverfügbarkeit der Upstream-Services geeignet sind, von denen sie abhängen, und aus technischen oder geschäftlichen Gründen können Sie sie nicht ändern oder erweitern.
  2. Sie führen Software nur in einer Entwicklungsumgebung aus, nicht in einer Produktionsumgebung, und Sie können sich besser mit Zeit beschäftigen als mit der Implementierung von Resilienz.

Wenn Sie jedoch die Kontrolle über Ihre Software haben und planen, sie für die Produktion bereitzustellen, können Sie nicht darauf hoffen, sich auf ein externes Tool zu verlassen, das die Dinge nur in der richtigen Reihenfolge startet, selbst wenn Sie Ihre Bereitschaftsbedingungen sorgfältig definiert haben. Es ist keine Lösung für das grundlegende Problem, und Ihr System fällt in dem Moment aus, in dem ein Netzwerkproblem auftritt. Im besten Fall müssen Sie jedes Mal alle Ihre Web-Frontend-Container automatisch neu starten, und ich kann mir nicht vorstellen, dass dies für irgendjemanden eine akzeptable Ausfallzeit darstellt.

Stimmen Sie mit Ihnen überein, dass gut geschriebene Software Netzwerkausfälle bewältigt. Ich denke, wir sind uns beide einig, dass nicht jede Software gut geschrieben ist und Entwickler nicht immer über alle möglichen Anwendungsfälle nachdenken.

Vielleicht möchte ich einen Client-Container ausführen, auf dem eine JVM ausgeführt wird, und ein anderes Überwachungstool zum Anhängen, beide im Host-PID-Namespace, damit einer den anderen überwachen kann, und ich werde nur unterstützt und lizenziert, um ein solches Tool mit dem vom Anbieter autorisierten Image auszuführen . Wenn das Überwachungstool vorhandene JVMs überwacht, ist es wichtig, in welcher Reihenfolge sie (offensichtlich) gestartet werden. Es gibt wahrscheinlich Hunderte von Anwendungsfällen, in denen es auf die Reihenfolge ankommt, von denen einige Netzwerke betreffen (MySQL, Elasticsearch, Service Discovery Cluster wurden alle erwähnt), andere jedoch andere Dinge betreffen, die durch effektive Nutzung der verschiedenen Namespaces gemeinsam genutzt werden können.

Daher stimme ich usecase (1) definitiv zu, da Sie unter bestimmten Umständen einen Container einfach nicht wechseln können

Aber auch sobald sich ein Werkzeug mit mehreren Dingen befasst, stößt es sofort auf Bestellung. Wenn die Bestellung wichtig ist und die einzige Möglichkeit, eine Bestellung zu garantieren, darin besteht, ein Bash-Skript um Docker-Compose zu schreiben, um zuerst etwas und dann etwas anderes zu erstellen, ist Docker-Compose möglicherweise nicht einmal in der Kette vorhanden, außer der Tatsache JSON / YAML ist hübscher als cmdline-Argumente.

IMHO Letztendlich ist ein Tool für eine Reihe von Anwendungsfällen entweder nützlich oder nicht. Docker-Compose ist eindeutig nützlich für ungeordnete Starts mehrerer Container auf demselben Host. Wenn es bei genügend Personen und genügend Anwendungsfällen um das Bestellen geht und ein Tool sie nicht anspricht, werden die Benutzer nur für diese Anwendungsfälle woanders hingehen, was eine Schande ist.

Wie auch immer, ich sehe, ich überarbeite nur alte Argumente, also werde ich jetzt aus diesem Thread aussteigen.

Dies ist das gleiche Argument, das in Tausenden von Threads zu so vielen verschiedenen Entwicklungsthemen angegeben wird: "Wenn der gesamte Code von allen richtig geschrieben worden wäre, wäre dies nicht erforderlich, also lasst es uns nicht tun." Das ist gleichbedeutend mit der Aussage: Wenn alle Menschen auf der Welt aufhören würden, fossile Brennstoffe zu verbrennen oder Elektrizität zu verbrauchen, könnten wir die Probleme beheben, die wir auf unserem Planeten verursachen. Du hast Recht. Wenn alle Apps das tun würden, was sie sollten, würden wir hier nicht darüber sprechen. Aber wir leben auf Erden. Ein Ort, der massive Unvollkommenheiten mit einer Spezies von Wesen hatte, die dazu neigen, Dinge auf die harte Tour zu lernen. Dieselben Leute, die Unternehmen nur auf den Weg bringen, weil sie ein "Produkt mit minimaler Lebensfähigkeit" bauen, um zu beten, dass sie es in die nächste Finanzierungsrunde oder zum nächsten Kunden schaffen, der es ihnen eines Tages ermöglichen könnte, die Version zu erstellen, die sie sich wünschen .

Die Welt ist nicht und wird niemals perfekt sein und so können wir nur das tun, was wir unter unserer eigenen Kontrolle haben. Und in diesem Fall habe ich nur die Möglichkeit, Sie alle zu überzeugen (auch bekannt als die Leute, die das Tool entwickeln, das ich absolut liebe und das ich gerne verwenden würde, wenn es nur diese eine Funktion hätte), um es einzubauen Ein Weg, wie es mit der Software umgehen kann, die in der Welt existiert, in der wir leben. Nicht die, in der wir uns gewünscht haben.

Am 31. Juli 2015, um 03:42 Uhr, schrieb Aanand Prasad [email protected] :

Ich hätte gedacht, es ist die Aufgabe oder zumindest das Anliegen eines Multi-Container-Management-Tools, zu wissen, in welcher Reihenfolge die Dinge beginnen sollen

Nein, nicht unbedingt. Aus den Gründen, die bereits mehrfach in diesem Thread dargelegt wurden, gibt es meines Erachtens nur zwei Gründe, um diesen Job an ein Container-Management-Tool zu übergeben:

Sie führen Standard-Container-Images aus, die nicht für die Nichtverfügbarkeit der Upstream-Services geeignet sind, von denen sie abhängen, und aus technischen oder geschäftlichen Gründen können Sie sie nicht ändern oder erweitern.

Sie führen Software nur in einer Entwicklungsumgebung aus, nicht in einer Produktionsumgebung, und Sie können sich besser mit Zeit beschäftigen als mit der Implementierung von Resilienz.

Wenn Sie jedoch die Kontrolle über Ihre Software haben und planen, sie für die Produktion bereitzustellen, können Sie nicht darauf hoffen, sich auf ein externes Tool zu verlassen, das die Dinge nur in der richtigen Reihenfolge startet, selbst wenn Sie Ihre Bereitschaftsbedingungen sorgfältig definiert haben. Es ist keine Lösung für das grundlegende Problem, und Ihr System fällt in dem Moment aus, in dem ein Netzwerkproblem auftritt. Im besten Fall müssen Sie jedes Mal alle Ihre Web-Frontend-Container automatisch neu starten, und ich kann mir nicht vorstellen, dass dies für irgendjemanden eine akzeptable Ausfallzeit darstellt.

- -
Antworte direkt auf diese E-Mail oder sieh sie dir auf GitHub an.

+1 @aanand. Dies ist nicht etwas, das Sie in seinem Umfang einschränken können. Wenn dies erledigt ist, muss die Funktion etwas sein, auf das sich die Leute verlassen können. Sie haben lange Zeit für "dauerhafte" Infrastruktur programmiert und es dauert lange, bis die Massen konvertiert sind. Sie werden dies für eine lange Zeit nutzen.

Ich möchte noch einmal betonen, dass wir nicht ausgeschlossen haben, etwas zu implementieren, um das Problem der Startzeitabhängigkeiten zwischen Containern zu lösen. Ich habe erst gestern in diesem Thread eine mögliche Lösung skizziert.

Aber ich möchte, dass wir uns auf derselben Seite befinden, für wen diese Funktion bestimmt ist, welche Probleme sie lösen wird, welche Probleme sie nicht lösen wird und inwieweit sie sich darauf verlassen kann. Das versuche ich herauszufinden.

Angesichts der zahlreichen vorhandenen Definitionen der Bereitschaft ("Container wurde gestartet" vs. "Container akzeptiert TCP-Verbindungen" vs "Benutzerdefinierte Integritätsprüfungsdurchläufe") und der unterschiedlichen Komplexität der Implementierung der einzelnen Tests möchte ich einen Vergleich erhalten Vorstellung davon, wie viele Personen von der sofort einsatzbereiten Unterstützung für jeden einzelnen profitieren würden.

Eine übliche Technik zum Synchronisieren von Diensten ist so etwas wie eine "Dienstregistrierung" mit etcd oder ähnlichem. Wie wäre es mit einem wait_for_service:

web:
  image: mywebapp
  links: ["db"]
  wait_for_service:
    type: etcd (or consul, or zk)    -- or use swarm type notation
    addr: http://my.etcd.com/
    path: postgres.service

db:
  image: postgres

compose weiß nicht, ob der Dienst wirklich bereit ist, kann jedoch nach den Daten in der Registrierung suchen und den abhängigen Container darauf basierend starten. Es liegt in der Verantwortung der Dienste (wie Postgres), ihre Verfügbarkeit in der Registrierung zu veröffentlichen, sodass für ältere Anwendungen eine Art Wrapping-Skript dies tun würde: Starten Sie die App, achten Sie darauf, dass der Port live geschaltet wird, und veröffentlichen Sie sie dann in der Registrierung.

Hallo @aanand.

Ich habe dies heute mit @bfirsh über die Twitter besprochen, da es etwas ist, auf das ich gestoßen bin.

Insbesondere beim Aufbau eines verteilten Systems aus vielen kleinen Docker-Komponenten stellte ich fest, dass Integrationstests erforderlich waren, bei denen alle Hauptschnittstellen-Apps und ihre Abhängigkeiten hochgefahren und dann verschiedene Tests gegen sie ausgeführt wurden, bevor alles wieder heruntergefahren wurde.

Dies führte dazu, dass es einige Zeit dauerte, bis Riak anfing, verglichen mit fast allem anderen, das es verwendet.

Ich würde mich nicht besonders dagegen aussprechen, dass Sie sagen: "Container starten asynchron, befassen sich damit", aber das Entwerfen um jeden Dienst, der einen konsistenten Integritätscheck enthält, würde zumindest das "Behandeln" für eine Implementierung pro Dienst enthalten, anstatt Code zu haben um das Wiederholen von Verbindungen in jeder App zu behandeln, die auf dem Dienst basiert.

Dienste, die ihren eigenen Gesundheitscheck definieren, sind auch für Überwachungszwecke von Vorteil.

@elliotcm In beiden Punkten einverstanden.

Zum Testen auf unserem CI haben wir ein kleines Dienstprogramm erstellt, das in einem Docker-Container verwendet werden kann, um auf die Bereitschaft verknüpfter Dienste zu warten. Es findet automatisch alle verknüpften TCP-Dienste aus ihren Umgebungsvariablen und versucht wiederholt und gleichzeitig, TCP-Verbindungen herzustellen, bis dies erfolgreich ist oder eine Zeitüberschreitung auftritt.

Wir haben auch einen Blog-Beitrag geschrieben, in dem beschrieben wird, warum wir es erstellt haben und wie wir es verwenden .

@meeee das sieht wirklich schön und nützlich aus! Können Sie uns Beispiele zeigen, wie es sich dreht? insbesondere bei der Verwendung mit einem Docker-Compose?

Ich hatte heute einen schwierigeren Fall, in dem ich zum ersten Mal einen MySQL-Container startete. Der Container bootet sich beim ersten Ausführen selbst und startet den Datenbankdämon neu, wenn er konfiguriert ist. Dies führte dazu, dass meine Portverfügbarkeitsprüfungen vorzeitig ausgelöst wurden und abhängige Container gestartet wurden, aber keine Verbindung hergestellt werden konnten. Dies war in einer CI-Umgebung, in der die Dinge komplett neu eingerichtet werden sollen.

Ich bin sehr daran interessiert zu komponieren, um eine Art Warte- / Überprüfungsverhalten zu unterstützen, aber es gibt einige täuschend knifflige Fälle da draußen. Gerne nehme ich an Diskussionen teil.

@prologic Alles, was Sie tun müssen, ist, den Befehl waitforservices in einem Docker-Container abhängig von anderen Diensten / Containern auszuführen, bevor Sie Ihre Anwendung oder Tests ausführen. Es findet alle verknüpften Dienste und wird ausgeführt, bis eine Verbindung hergestellt werden kann oder eine bestimmte Zeit verstrichen ist (standardmäßig 60 Sekunden). Führen Sie nach dem Beenden der Binärdatei einfach den gesamten Code aus, der von anderen Diensten abhängt (möglicherweise möchten Sie jedoch den Beendigungsstatus überprüfen).

@pugnascotia Ihr Datenbankserver konnte localhost nur während des Bootstraps abhören - Sie müssen eine Art Indikator offiziellen Postgres-Image .

@aanand Postgres ist ein hervorragendes Beispiel für die Auswahl, da das Warten auf das Öffnen des TCP-Ports nicht ausreicht. Wenn Sie dies in einem solchen (Docker-) Szenario tun, wird manchmal ein Fehler wie FATAL: the database system is starting up. angezeigt. psql notwendig zu sein, wenn Sie sicher sein möchten, dass Postgres bereit ist - ich verwende select version() aber vielleicht gibt es eine leichtere Alternative.

Es gibt ein bestimmtes Maven-Docker-Plugin mit einer interessanten Warteimplementierung. Im Moment verwende ich einen Bash-Wrapper, um jeden Dienst zu starten, was den Punkt der Verwendung von Compose eher zunichte macht.

Verwenden Sie dies als Problemumgehung (nicht sicher, ob es kugelsicher ist):

db:
  image: postgres:9.3
  ports:
    - "5432:5432"
createdbs:
  image: postgres:9.3
  links:
    - db
  command: >
    /bin/bash -c "
      while ! psql --host=db --username=postgres; do sleep 1; done;
      psql --host=db --username=postgres -c 'CREATE DATABASE \"somedatabase\";';
    "

Ich habe ähnliche Methoden wie @olalonde verwendet. Wenn ich einfache Anführungszeichen für den Befehl verwende, der nach /bin/bash -c , kann ich auch Umgebungsvariablen verwenden, die von Links in anderen Anwendungen wiederverwendet werden, sodass ich Benutzernamen und Kennwörter verwenden kann, ohne sie in zwei Teilen verwalten zu müssen setzt. Dies funktioniert gut in Situationen, in denen ich einen Dienst wie eine API habe, für den eine Datenbank erforderlich ist, und die richtigen Daten durch Ausführen einer Abfrage überprüft werden, um festzustellen, ob eine bestimmte Tabelle oder ein bestimmter Datensatz vorhanden ist. Dies bedeutet auch, dass im Container eine Art Client installiert sein muss, um die Datenbank ordnungsgemäß abzufragen, aber es funktioniert.

+1 Diese Funktionalität interessiert mich sehr

+1 zu Abhängigkeiten. Ich bin damit einverstanden, dass die Architektur im Prinzip robust genug sein sollte, um jede Startreihenfolge zu unterstützen. ABER oft ist dies nicht praktikabel.

Es scheint, als ob der Rückstoß für diese Funktion von anderen stammt, die versuchen, Architektur aus der Ferne zu diktieren. wo sie wirklich kein Recht dazu haben. Diese Funktion würde es ermöglichen, zeitaufwändige Refaktoren zu umgehen. mit wenig langfristigem Wert. (Refactoring zum Zwecke des Refactorings).

Ja, ich sagte "herumarbeiten"; und ich fühle mich schmutzig. Aber für mich geht es beim Komponieren wirklich darum, anderen zu ermöglichen, produktiv zu sein. Diese einfache Konfiguration ermöglicht dies.

Wenn diese Funktion im Toolest vorhanden wäre, könnte ich mein Problem in einer Minute lösen und einen echten Mehrwert schaffen. Stattdessen stoße ich meinen Kopf gegen eine Wand und versuche, Probleme mit der Startreihenfolge mit externen Abhängigkeiten zu umgehen.

@beardface und alle anderen: Mit welcher Funktion können Sie Ihre App speziell weiterentwickeln?

  1. Die Möglichkeit, anzugeben, dass Dienst A warten muss, bis Dienst B gestartet wurde? (was, denken Sie daran, die Rennbedingung immer noch nicht löst, wenn ein Container gestartet wird, aber nicht bereit ist, Verbindungen zu akzeptieren)
  2. Die Möglichkeit anzugeben, dass ein Dienst A warten muss, bis Dienst B Verbindungen akzeptiert? (Beachten Sie, dass die Race-Bedingung beim Abhören eines Containers immer noch nicht gelöst wird, die Initialisierung jedoch noch nicht abgeschlossen ist - z. B. ein Postgres-Container, der beim Start eine Datenbank erstellt, um das Beispiel von @rarkins zu verwenden).
  3. Die Möglichkeit, eine Integritätsprüfung für Dienst B zu definieren und anzugeben, dass Dienst A mit dem Start warten muss, bis die Integritätsprüfung für Dienst B bestanden ist?

@aanand Nummer 3 ist meine Stimme. Es gibt dem Entwickler die Flexibilität, die Integritätsprüfungslogik zu wählen, anstatt sich erneut auf den Dienst zu verlassen, um zu entscheiden, wann es Zeit ist. Für diesen speziellen Anwendungsfall ist mehr Entwicklerfreiheit besser. Es ist unmöglich, alle Arten von Apps vorwegzunehmen, die in Containern installiert werden.

+3

Es wäre jedoch schön, wenn einige grundlegende Gesundheitschecks enthalten wären. So etwas wie _http 200 ok auf Port 80_ ist so häufig, dass es die Mühe wert sein könnte.

Es wäre schön, eine "Batterien enthalten" Nummer 3 zu haben (also wage ich es, all das oben Genannte zu sagen?)

Dh integrierte Funktion für "Container ist aktiv", "Datei ist vorhanden" und "Port ist offen" Art von Wartezeiten und dann eine Möglichkeit, die Benutzer ihre eigenen "Anwendungsschicht" -Prüfungen definieren zu lassen.

3 bekommt meine Stimme

3 ist allgemeiner 2 ist allgemeiner 1. Jeder wird 3 bevorzugen, aber 2 oder 1 werden für einige gut genug sein.

Stimmen Sie für 3

Offensichtlich die 3. Option. Nur in dieser Diskussion gibt es viele Fälle. Die 1. und 2. Option wären also großartig und viele Menschen wären glücklich, aber das Thema würde offen bleiben.

Stimmen Sie für 3

Stimmen Sie für 3

Stimmen Sie für 3. Interessiert daran, es auch zu testen.

Stimmen Sie für 3.

Stimmen Sie für 3. Interessiert daran, es auch zu testen.

Stimmen Sie für 3

3

Vielen Dank an alle. Sie können jetzt aufhören zu wählen - ich denke, die Botschaft ist klar.

Ich würde mich für Feedback zu dem Design interessieren, das ich unter https://github.com/docker/compose/issues/374#issuecomment -126312313 vorgeschlagen habe - für alternative Designvorschläge sowie für Diskussionen über deren Stärken / Schwächen.

Die wait_for_tcp bequeme Methode wäre nützlich, aber es ist mir nicht klar , wie man einen separaten Behälter mit dem Gesundheits - Check zu tun ist einfacher , als es in dem gleichen Behälter zu tun , wie durch @olalonde beschrieben und @mbentley oben .

Was ist mit so etwas wie dem, was alexec im Docker-Maven-Plugin macht? Er hat seine Konfiguration explizit so gestaltet, dass sie Docker-Compse / Fig ähnelt, und bisher hat sie für meine Projekte hervorragend funktioniert.

healthChecks:
  pings:
     # check this URL for 200 OK
     - https://localhost:8446/info
     # check another URL with non-default time out, with a pattern, and non checking SSL certificates
     - url: https://localhost:8446/info
       timeout: 60000
       pattern: pattern that must be in the body of the return value
       sslVerify: false
  logPatterns:
     - pattern that must be in log file
     - pattern: another pattern with non-default timeout
       timeout: 30000

Quelle: https://github.com/alexec/docker-maven-plugin/blob/master/USAGE.md

Das Überprüfen, ob die TCP-Verbindung hergestellt ist, reicht nicht aus, um zu wissen, dass die Datenbank gestartet wurde. Ich denke besser, einen Befehl zu haben, der den Zustand der Datenbank überprüft.

@ceagan So. Benutzerdefinierte Befehle wären ebenfalls nützlich. z.B

healthChecks:
  custom:
    # retry this command until it returns success exit code
    - cmd: psql --host=localhost --username=postgres
      sleep: 1s

Ich denke auch, dass checks besser wäre als healthChecks weil man sich nicht an die Fallkonvention erinnern muss. Könnte auch nützlich sein, wenn Sie wait (wie viele Sekunden warten müssen, bevor Sie mit der Durchführung von Gesundheitschecks beginnen), attempts (wie oft die Gesundheit überprüft werden sollte, bevor Sie aufgeben), retire (wie viele Sekunden warten, bevor vollständig aufgegeben wird) Parameter.

Dies geht in die Richtung, in die das Marathon-Framework es gelöst hat. grundlegende Abhängigkeiten plus Healthchecks . Da es zum Starten von Docker-Containern sehr beliebt ist, sollten Sie die Optionen (Zeitüberschreitungen, Intervalle, Antwortcodes usw.) überprüfen, um sie zum Erstellen zu übernehmen.

Gleich hier, Option 3.

+1

+1

+1

Ich bleibe bei einem benutzerdefinierten Warteskript, aber es wäre sehr schön.

+1

+1

Optionen 3-healthChecks klingt gut.

+1

+1

+1

+3.
Um der Diskussion einige andere Gedanken hinzuzufügen, sagen die von @aanand vorgeschlagenen Entscheidungen wirklich: Der Zustand der Container liegt in der Verantwortung von Docker, nicht von Docker-Compose. Docker-compose könnte all diese Anwendungsfälle auf saubere und elegante Weise implementieren, wenn Docker Statusinformationen bereitstellt. Aber Docker nicht. Es scheint an der Vision von sofortigen, zustandslosen Containern festzuhalten, die so schnell gestartet werden, dass diese Art von Synchronisationsproblemen unwichtig sind.
In meinem Fall verfolge ich die Idee, für jeden Fall die beste Architektur für meine Dienste auswählen zu können. Zum Beispiel möchte ich manchmal mehrere MariaDB-Instanzen, von denen jede eine einzelne Anwendung bedient. In anderen Fällen möchte ich eine einzelne MariaDB-Instanz, die mehrere Anwendungen bedient. Ich möchte nicht, dass Docker mir sagt, was am besten ist oder was ich stattdessen tun soll. Docker scheint immer solche Versuchungen zu haben;).
Ich denke, die beste Lösung besteht darin, Docker davon zu überzeugen, dass Container beliebige Metadaten über sich selbst deklarieren, und diese Funktion zu verwenden, damit Docker-Compose herausfinden kann, ob ein Container als "bereit" eingestuft wird, damit sich andere darauf verlassen können.
Für den Ansatz "Single DB-Multiple Apps" möchte ich eine Definition wie:

db:
  image: postgres:9.3
  ports:
    - "5432:5432"
app1:
  image: wordpress
  links:
    - db [WP]
app2:
  image: ghost
  links:
    - db [GST]

Docker Compose würde "db" starten und nach seinen Metadaten fragen (relevant für Docker Compose). Aus der yml-Datei geht hervor, dass "app1" erwartet, dass "db" "WordPress-fähig" ist (dh nicht nur Verbindungen akzeptiert, sondern auch die erforderlichen Objekte).

Ich habe keine einfache Lösung, um diese Situation zu lösen. Ich mache es derzeit manuell in zwei Schritten: ein benutzerdefiniertes Postgresql-Bootstrap-Image, in dem ich die Datenbank und den Datenbankbenutzer erstelle, um darauf zuzugreifen; und ein benutzerdefiniertes Liquibase-Postgresql-Image, um die Datenbankobjekte aus den DDLs zu generieren, die vom Wordpress-Container bereitgestellt (oder daraus extrahiert) werden. Nur dann kann ich "app1" starten.
Das zwingt mich, die Container in "Infrastruktur" - und "Apps" -Gruppen zu trennen, wenn die "Infrastruktur" -Container unterschiedliche Anwendungen bedienen.

Docker Compose möchte so zustandslos sein wie Docker selbst. Ich weiß nicht, ob das möglich ist, wenn es wirklich nützlich sein will.

+1 für Option 3

+1

+1 für Option 3.

+1 für Option 3

+1 für Option 3

Dieses Problem gibt es schon eine Weile. Was ist die Lösung?

@ bweston92 Ich denke, der Status ist, dass @aanand früher in diesem Thread eine Lösung vorgeschlagen hat und nach dieser sucht

alternative Designvorschläge sowie Diskussionen über ihre Stärken / Schwächen.

Persönlich halte ich die von @aanand vorgeschlagene Lösung für sehr sinnvoll. Es scheint mir sehr explizit zu sein und gleichzeitig flexibel zu sein. Es würde meine Bedürfnisse abdecken, nicht auf das Öffnen eines TCP-Ports zu warten oder nur eine feste Zeit zu warten.

Mein Anwendungsfall dient nur zum Testen. Meine Tests schlagen fehl, wenn sie gestartet werden, bevor die Datenbank erstellt wurde. Daher habe ich ihren Befehl wie folgt in bash -c "sleep 2; python manage.py test --keepdb" geändert:

db:
    image: postgres:9.5
test:
    build: .
    command: bash -c "sleep 2; python manage.py test --keepdb"
    volumes:
        - ./test_project:/app
    links:
        - db
        - selenium
    environment:
        - EXTERNAL_TEST_SERVER=http://testserver:8000/
        - SELENIUM_HOST=http://selenium:4444/wd/hub
selenium:
    image: selenium/standalone-chrome:2.48.2
    links:
        - testserver
testserver:
    build: .
    command: bash -c "sleep 5; python manage.py testserver 8000 --static"
    volumes:
        - ./test_project:/app
    ports:
      - "8000:8000"
    links:
        - db

damit ich docker-compose run test ausführen kann, ohne zuerst die Datenbank zu starten und zu warten.

Es ist schwer zu sagen, welches Thema heutzutage der "richtige" Ort ist, um für eine neue Docker-Kompositionsfunktion zu stimmen, mit der explizite Abhängigkeiten deklariert werden können, aber meine Stimme ist eine starke. Mit der neuen Docker 1.9-Netzwerkfunktionalität und der sich abzeichnenden Abwertung von Container-Links gibt es jetzt keine gute Möglichkeit, sicherzustellen, dass Container A vor Container B gestartet wird. Wenn Sie das benutzerdefinierte Docker 1.9-Netzwerk verwenden, können Sie dies Geben Sie keine Container-Links mehr an. Das ist ... kaputt.

Genau. Gibt es einen Zeitplan, um Option 3 zu erhalten? Es wäre großartig, wenn dies beschleunigt würde.

Es ist erwähnenswert, dass die Reihenfolge der Abhängigkeiten dieses Problem nicht wirklich behebt. Dieses Problem gilt auch für Links. In vielen Fällen startet ein Container so schnell, dass Sie das Problem nicht bemerken, das Problem jedoch weiterhin besteht.

Um dieses Problem zu lösen, ist ein anwendungsbezogener Healthcheck erforderlich. Ein Healthcheck ist im Grunde eine Schleife, die eine Operation wiederholt, bis entweder die Operation erfolgreich ist oder eine Zeitüberschreitung auftritt. Im Fall eines HTTP-Dienstes werden möglicherweise http-Anforderungen gestellt, bis Sie einen 2xx-Code erhalten. Bei einer Datenbank wird möglicherweise eine Verbindung hergestellt und aus einer Tabelle ausgewählt.

In jedem Fall ist es anwendungsspezifisch und muss vom Entwickler definiert werden. Wenn wir Option 3 von https://github.com/docker/compose/issues/374#issuecomment -135090543 implementieren würden, müssten Sie diese Healthcheck-Logik weiterhin implementieren.

Es wurde bereits einige Male in dieser Ausgabe erwähnt (https://github.com/docker/compose/issues/374#issuecomment-53036154, https://github.com/docker/compose/issues/374#issuecomment-71342299) ), sondern auf Wieder Iterierte, können Sie , indem Sie Ihre Anwendung dieses Problem heute lösen elastisch zum Scheitern von erneuten Versuch , eine Verbindung. Sie müssen dies ohnehin für jedes Produktionssystem tun.

Es stellt sich heraus, dass die Funktionalität, mit der Ihre Anwendung ausfallsicher gemacht wird, praktisch dieselbe Logik wie ein Integritätscheck ist. In beiden Fällen müssen Sie immer noch dieselbe Logik implementieren. Der einzige Unterschied wäre, wo Sie es einschließen. Im Moment können Sie es in Ihre Anwendung oder in ein Einstiegspunktskript aufnehmen. Mit der vorgeschlagenen Änderung können Sie sie in der Compose-Datei definieren. In beiden Fällen müssen Sie immer noch einen Integritätscheck für jeden Dienst implementieren.

Gibt es einen signifikanten Vorteil, wenn Sie es anstelle des Einstiegspunktskripts in die Compose-Datei aufnehmen? Das steht vielleicht noch zur Debatte.

Der große Nachteil beim Einfügen in die Compose-Datei besteht darin, dass up erheblich langsamer wird.

Mit dem neuen Netzwerk können wir up parallel ausführen (wie wir es für Stop, RM und Scale tun). Jeder Container kann sofort gestartet werden, eine Initialisierung durchführen und dann warten, bis die Abhängigkeiten verfügbar sind, um fortzufahren. Dies macht das Starten einer Umgebung sehr schnell.

Wenn Compose warten muss, bis ein Integritätscheck abgeschlossen ist, erfolgt der Start effektiv nacheinander. Container-Start und Anwendungsinitialisierung erfolgen nicht parallel und alles ist langsamer.

Die meisten Apps werden aufgrund von LB überprüft, extern überwacht usw. Es ist nicht schwierig, eine neue zu öffnen. Wenn Compose dies unterstützt, können die Leute diese Wahl treffen. Es ist nicht obligatorisch. In der realen Welt müssen sich die Leute mit einer Vielzahl von Anwendungen auseinandersetzen, und diese Vorstellung, dass plötzlich alle Apps intelligent gemacht werden können, ist unrealistisch und unpraktisch. Und die Wrapper-Logik im Einstiegspunkt ist einfach hässlich. Ich denke, es gab genug Nachfrage in der Community nach einem Feature und wie Sie sehen, hat Option 3 viele Stimmen erhalten.

von meinem Iphone gesendet

Am 18. November 2015, um 11:01 Uhr, schrieb Daniel Nephin [email protected] :

Es ist erwähnenswert, dass die Reihenfolge der Abhängigkeiten dieses Problem nicht wirklich behebt. Dieses Problem gilt auch für Links. In vielen Fällen startet ein Container so schnell, dass Sie das Problem nicht bemerken, das Problem jedoch weiterhin besteht.

Um dieses Problem zu lösen, ist ein anwendungsbezogener Healthcheck erforderlich. Ein Healthcheck ist im Grunde eine Schleife, die eine Operation wiederholt, bis entweder die Operation erfolgreich ist oder eine Zeitüberschreitung auftritt. Im Fall eines HTTP-Dienstes werden möglicherweise http-Anforderungen gestellt, bis Sie einen 2xx-Code erhalten. Bei einer Datenbank wird möglicherweise eine Verbindung hergestellt und aus einer Tabelle ausgewählt.

In jedem Fall ist es anwendungsspezifisch und muss vom Entwickler definiert werden. Wenn wir Option 3 aus # 374 (Kommentar) implementieren würden, müssten Sie diese Healthcheck-Logik noch implementieren.

Es wurde bereits einige Male in dieser Ausgabe erwähnt (# 374 (Kommentar), # 374 (Kommentar)), aber um es noch einmal zu wiederholen, können Sie dieses Problem heute lösen, indem Sie Ihre Anwendung durch erneutes Versuchen einer Verbindung ausfallsicher machen. Sie müssen dies ohnehin für jedes Produktionssystem tun.

Es stellt sich heraus, dass die Funktionalität, mit der Ihre Anwendung ausfallsicher gemacht wird, praktisch dieselbe Logik wie ein Integritätscheck ist. In beiden Fällen müssen Sie immer noch dieselbe Logik implementieren. Der einzige Unterschied wäre, wo Sie es einschließen. Im Moment können Sie es in Ihre Anwendung oder in ein Einstiegspunktskript aufnehmen. Mit der vorgeschlagenen Änderung können Sie sie in der Compose-Datei definieren. In beiden Fällen müssen Sie immer noch einen Integritätscheck für jeden Dienst implementieren.

Gibt es einen signifikanten Vorteil, wenn Sie es anstelle des Einstiegspunktskripts in die Compose-Datei aufnehmen? Das steht vielleicht noch zur Debatte.

Der große Nachteil beim Einfügen in die Compose-Datei besteht darin, dass sie erheblich langsamer ist.

Mit der neuen Vernetzung können wir parallel nachholen (wie bei Stop, RM und Scale). Jeder Container kann sofort gestartet werden, eine Initialisierung durchführen und dann warten, bis die Abhängigkeiten verfügbar sind, um fortzufahren. Dies macht das Starten einer Umgebung sehr schnell.

Wenn Compose warten muss, bis ein Integritätscheck abgeschlossen ist, erfolgt der Start effektiv nacheinander. Container-Start und Anwendungsinitialisierung erfolgen nicht parallel und alles ist langsamer.

- -
Antworte direkt auf diese E-Mail oder sieh sie dir auf GitHub an.

@dnephin Das Endergebnis ist, dass Sie für jeden Dienst die gleichen Wrapper-Skripte haben. Mein Punkt ist, dass es einige Dinge gibt, die so häufig sind (wie HTTP 200 auf 80 und 443 oder TCP auf 5432), dass es eine gute Idee ist, sie mit compose zu versenden.

Sicher, es wäre cool, all dies auf Anwendungsebene zu lösen, aber in Wirklichkeit haben Sie nur die Kontrolle über Ihre eigene Anwendung und nicht über alle anderen beweglichen Teile wie Datenbank, Cache oder Massage-Warteschlange.

Ich stimme sowohl @mbdas als auch @jayfk zu und werde nur hinzufügen: Wenn der Widerstand dagegen besteht, dass selbst bei Abhängigkeitsspezifikationen und der daraus resultierenden Reihenfolge des Container-Starts Fehler auftreten, dann die Verwendung von Container-Links und Volumes von bis Die Startreihenfolge des Steuercontainers hätte niemals passieren dürfen - alles, was wir verlangen, ist, dass jetzt, da das neue Netzwerkmodell bedeutet, dass Links veraltet sind (und das neue Netzwerkmodell buchstäblich nicht mit Links koexistieren kann), dasselbe Startup -bestellen Sie die Funktionalität, die Links in irgendeiner Weise an uns zurückgeben dürfen. Sicher, alle Fehlerfälle, die bei der linkbasierten Containerbestellung aufgetreten sind, können immer noch mit dem neuen Netzwerkmodell und den Containerabhängigkeiten auftreten, aber wir haben alle gelernt, damit zu leben.

@delfuego : näher erläutern, wie Links veraltet sind und insbesondere durch was sie ersetzt werden? Link zu einigen Dokumenten / Beispielen ist ausreichend

@ h17liner : ja das ist interessant! Vielen Dank

Während ich @dnephin zustimme

Es wurde bereits einige Male in dieser Ausgabe erwähnt, aber um es noch einmal zu wiederholen, können Sie dieses Problem heute lösen, indem Sie Ihre Anwendung durch erneutes Versuchen einer Verbindung ausfallsicher machen. Sie müssen dies ohnehin für jedes Produktionssystem tun.

Ich sehe das nicht so, als würde man Tests durchführen. Wenn ich nur teste, ob ein Modell in der Django-App korrekt gespeichert wird, bin ich mir nicht sicher, wie sinnvoll es ist, der Datenbankverbindung Ausfallsicherheit zu verleihen.

@delfuego Ich denke, Sie waren ursprünglich an der richtigen Stelle (# 686) für dieses Problem. Bei diesem Problem geht es nicht um die Bestellung, sondern um eine künstliche Verzögerung beim Start (wenn bereits eine Bestellung vorliegt). Während diese Dinge zusammenhängen, handelt es sich um separate Themen.

Ich bin nicht einverstanden mit Links, die in vom Benutzer erstellten Brückennetzwerken nicht unterstützt werden und dokumentiert sind, dass sie im Allgemeinen veraltet sind. Es gibt keine Bestellung. Option 3 kümmert sich also sowohl um die Bestellung als auch um den Beginn der Ausgabe.

von meinem Iphone gesendet

Am 19. November 2015 um 8:14 Uhr schrieb Daniel Nephin [email protected] :

@delfuego Ich denke, Sie waren ursprünglich an der richtigen Stelle (# 686) für dieses Problem. Bei diesem Problem geht es nicht um die Bestellung, sondern um eine künstliche Verzögerung beim Start (wenn bereits eine Bestellung vorliegt). Während diese Dinge zusammenhängen, handelt es sich um separate Themen.

- -
Antworte direkt auf diese E-Mail oder sieh sie dir auf GitHub an.

Ich möchte Option 4 vorschlagen (einige mögen sagen, es ist tatsächlich eine Variation von 3)
Der Container ist erst bereit, wenn alle Startbefehle mit dem Exit-Code 0 abgeschlossen sind. Muss möglich sein, diese Startbefehle in der yml-Datei für jeden Container zu definieren. Diese Befehle werden wie "docker exec" gegen das Ausführen von Containern ausgeführt. Denken Sie beim klassischen Unit-Test an die Methoden setUp () und tearDown (). Ja, wir könnten auch "Shutdown" -Befehle haben.
Offensichtlich wird der nächste Container in der Hierarchie erst gestartet, wenn alle Container, von denen er abhängt, bereit sind.
PS Danke für die tolle DockerCon.Eu 2015

Eine HEALTHCHECK Direktive ist viel flexibler (dh kann zu einem späteren Zeitpunkt verwendet werden) und hilfreich. Die Einrichtung sollte innerhalb des Skripts CMD oder (noch besser) ENTRYPOINT erfolgen.

Ich denke, der Kern des Problems hier ist, dass die Leute einen einzigen docker-compose up Befehl wollen, um einen Stapel aufzurufen, und alles funktioniert einfach magisch.

Basierend auf all dem Feedback gibt es eindeutig viele Lösungen für verschiedene Anwendungsfälle, aber keine "Einheitsgröße".

Sie können "Initialisierungs" -Aufgaben ganz einfach ausführen, indem Sie mehrere Docker-Compose-Befehle ausführen - und ich denke, dieser Ansatz ist der allgemeinste und flexibelste Ansatz.

Zum Beispiel führe ich ein Ansible-Playbook in einem "Agent" -Container mit einer einzelnen Aufgabe aus, die darauf wartet, dass der MySQL-Container (My Database) auf Port 3306 ausgeführt wird. Dieser "Agent" -Container wird so automatisch mit dem "DB" -Container "My" verknüpft Startet es, wenn Folgendes ausgeführt wird:

$ docker-compose run --rm agent
Creating db_1

PLAY [Probe Host] *************************************************************

TASK: [Set facts] *************************************************************
ok: [localhost]

TASK: [Message] ***************************************************************
ok: [localhost] => {
    "msg": "Probing db:3306 with delay=0s and timeout=180s"
}

TASK: [Waiting for host to respond...] ****************************************
ok: [localhost -> 127.0.0.1]

PLAY RECAP ********************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0

Danach kann ich docker-compose up ausführen und weiß, dass der Datenbankcontainer voll funktionsfähig ist.

Hier ist eine einfache Datei docker-compose.yml, die dies unterstützt:

...
...
db:
  image: mysql
  hostname: db
  expose:
    - "3306"
  environment:
    MYSQL_DATABASE: xxx
    MYSQL_USER: xxx
    MYSQL_PASSWORD: xxx
    MYSQL_ROOT_PASSWORD: xxx

agent:
  image: cloudhotspot/ansible
  links:
    - db
  volumes:
    - ../../ansible/probe:/ansible
  environment:
    PROBE_HOST: "db"
    PROBE_PORT: "3306"

Der "Agent" -Container führt ein Playbook mit dem Namen site.yml in dem unten gezeigten /ansible gemounteten Volume aus:

- name: Probe Host
  hosts: localhost
  connection: local
  gather_facts: no
  tasks: 
    - name: Set facts
      set_fact: 
        probe_host: "{{ lookup('env','PROBE_HOST') }}"
        probe_port: "{{ lookup('env','PROBE_PORT') }}"
        probe_delay: "{{ lookup('env','PROBE_DELAY') | default(0, true) }}"
        probe_timeout: "{{ lookup('env','PROBE_TIMEOUT') | default (180, true) }}"
    - name: Message
      debug: msg="Probing {{ probe_host }}:{{ probe_port }} with delay={{ probe_delay }}s and timeout={{ probe_timeout}}s"
    - name: Waiting for host to respond...
      local_action: >
        wait_for host={{ probe_host }}
        port={{ probe_port }}
        delay={{ probe_delay }}
        timeout={{ probe_timeout }}
      sudo: false

Eine Lösung für das einzelne Ziel von docker-compose up könnte darin bestehen, eine "Workflow" -Funktion zum Erstellen von Docker einzuführen und eine optionale Workflow-Spezifikationsdatei einzuschließen, die komplexere und kontrollierte Orchestrierungsszenarien ermöglicht, indem ein oder mehrere Docker-Compose-Befehle als angegeben werden "Aufgaben", die ausgeführt werden sollen:

# The default workflow, specified tasks will be run before docker-compose up
# The "up" task is implicit and automatically invoked for the default workflow
# The "up" task is explicit for custom workflows as some workflows may not want docker-compose up
default:
  tasks:
    - run --rm agent
    - up

# Custom workflows that can be invoked via a new docker-compose command option
# This example:
# 1. Runs agent container that waits until database container is up on port 3306
# 2. Runs Django database migrations from app container
# 3. Runs Django collect static task from app container
# 4. Runs test container that runs acceptance tests against linked app container
# Does not execute a docker-compose up afterwards

test:
  tasks:
    - run --rm agent 
    - run --rm app manage.py migrate
    - run --rm app manage.py collectstatic --noinput
    - run --rm test

Heute erreiche ich das oben Gesagte mit Makefiles, die eine übergeordnete Möglichkeit bieten, meine eigenen Workflows für verschiedene Szenarien zu definieren.

Es wäre großartig, wenn eine "Workflow" -Funktion oder ähnliches in Docker Compose eingeführt werden könnte, die eine sehr allgemeine und flexible Lösung für dieses spezielle Problem und vieles mehr bietet.

Einverstanden ist das Problem, dass die Leute erwarten, dass Docker-Compose für Produktionsbereitstellungen ausreicht. Persönlich denke ich, dass es lange dauern wird, bis dies machbar wird und Kubernetes / Helm diesem Ziel viel näher zu sein scheinen.

@olalonde , sicher, wir würden gerne komponieren, um produktionsbereit zu sein ... aber wir werden es übernehmen, um wichtige und vorhandene Funktionen zu unterstützen, die angesichts der Ablehnung von Container-Links verschwinden werden, sofern sie nicht auf den neuen Benutzer repliziert werden Modell für erstellte Netzwerke. (Auch hier ist diese Anfrage möglicherweise nicht perfekt auf dieses spezielle Problem abgestimmt - es bleibt mir unklar, ob die Bestellung des Container-Starts hier oder in Ausgabe 686 "gehört" ...)

Mit einer HEALTCHECK Docker-Direktive kann die depends_on -Funktionalität entweder auf den Start des Containers warten (kein Healthcheck) oder das Healthcheck-Skript erfolgreich beendet werden (Exit-Code 0 ). Dies ist so flexibel wie es nur geht (Sie können eine beliebige Logik definieren) und behält die Healthcheck-Logik dort bei, wo sie hingehört (innerhalb des überprüften Containers).

@delfuego Auch für Entwicklung und Test wäre diese Funktionalität hilfreich. Persönlich möchte ich in der Lage sein, docker-compose run test und es funktionieren zu lassen, ohne vorher Dienste aufzurufen und manuell zu warten. Obwohl dies möglich ist, macht es den Einstieg in das Projekt nur etwas schmerzhafter und bietet weitere Möglichkeiten, wie Tests fehlschlagen können.

+1

Ich denke, die Auflösung erfordert einen Mittelweg - Compose wird niemals in der Lage sein, all die verschiedenen Arten zu berücksichtigen, in denen Anwendungen als verfügbar angesehen werden können oder nicht. Die Idee eines Gesundheitschecks bedeutet für verschiedene Menschen verschiedene Dinge und ist möglicherweise nicht so einfach wie "ob es geht oder nicht". In der Produktion können Sie einen Container herunterfahren, wenn er ungewöhnlich lange Antwortzeiten aufweist, selbst wenn er HTTP-Prüfungen bestanden hat.

Ich bin daher der Meinung, dass die grundlegende Unterstützung für HTTP-Antworten, offene Ports, erstellte Dateien oder ausgegebene Protokollzeilen für die Entwicklung ausreichen sollte. Alles, was weiter fortgeschritten ist, wird fast sofort anwendungsspezifisch. Ich mag auch die Idee, Entwickler zu ermutigen, die einzelnen Teile ihrer Anwendungsstapel robuster zu machen.

@pugnascotia danke, das ist ein konstruktiver Kommentar und ein vernünftiger Ansatz ("das Beste aus beiden Welten"?)

Die derzeit diskutierten Lösungen scheinen das ursprünglich gemeldete Problem, das viel einfacher ist, nicht zu lösen. Es wartet NICHT darauf, dass ein Dienst verfügbar ist, sondern darauf, dass ein Dienst beendet wird.

Ich habe einen Anwendungsfall, in dem ich zwei Container habe, die dieselben Ports freigeben. Der erste läuft 15-60 Sekunden und wird dann beendet. Dann sollte der zweite Dienst starten. Es gibt heute keine (offensichtliche?) Möglichkeit, dies zu komponieren, da dadurch der Portkonflikt erkannt und beendet wird. Nicht einmal "Neustart: Immer" ist eine Lösung.

Ja, Compose ist nicht für diesen Anwendungsfall konzipiert. Compose konzentriert sich auf Laufzeitumgebungen und nicht auf das Erstellen von Pipelines. Ich glaube nicht, dass es darum geht, was das ursprünglich gemeldete Problem betrifft.

Es gab einige Anfragen nach mehr Build-orientierten Funktionen, aber ich denke nicht, dass sie für das Komponieren sinnvoll sind. Die beiden Funktionen sind sehr unterschiedlich und der Versuch, sie in dasselbe Konfigurationsformat zu bringen, führt wahrscheinlich zu vielen Verwirrungen und einer schlechten Benutzererfahrung.

@ewindisch Ihr Anwendungsfall kann auf die Ausführung einer Kette von Batch-Jobs verallgemeinert werden. Compose ist in diesem Fall hilfreich (obwohl es nicht dafür konzipiert ist), da es Abhängigkeiten zwischen Diensten beibehält - z. B. diesen Ketten. Aber es behandelt keine Sequenzierung und IMHO sollte es nicht, weil Compose außerhalb von Containern ist und keine Ahnung hat, was ein Prozess innerhalb eines Containers tun wird.

Dieser Teil der Compose-Dokumentation behandelt die Frage, warum Compose diese Funktion nicht bietet:

https://docs.docker.com/compose/faq/#how -do-i-get-compose-to-wait-auf-meine-Datenbank-bereit sein, bevor-meine-Anwendung gestartet wird

Auf diesen Seiten wird das Problem jedoch überhaupt nicht erwähnt:

https://docs.docker.com/compose/django/
https://docs.docker.com/compose/rails/
https://docs.docker.com/compose/wordpress/

Zumindest sollten diese Seiten eine Bestätigung enthalten, dass Compose nicht darauf wartet, dass ein Datenbankcontainer bereit ist. Sie könnten auch Beispiele für den Umgang damit enthalten.

@ewindisch Eigentlich ist es genau das, was ich in https://github.com/docker/compose/issues/374#issuecomment -126312313 vorgeschlagen habe - mit der Hypothese, dass die Lösung dieses Problems den Benutzern auch die Tools zur Lösung des Startbestellungsproblems bietet ( wenn nicht das Problem der längerfristigen Belastbarkeit).

Ich bin immer noch daran interessiert, diesen Lösungsraum zu erkunden, wenn es jemand anderes ist.

Ich bin.

Ich auch.

+1

+3

+1

+1 für die Implementierung der Lösung https://github.com/docker/compose/issues/374#issuecomment -126312313.

Massives Upvoting!
Derzeit wirkt sich dies auf die Verwendung von Tools aus, die im Container ausgeführt werden, sich jedoch auf Docker-Ereignisse stützen (z. B. jwilder / nginx-proxy). Die Art und Weise, wie ich es mache, besteht darin, den Listener manuell von Docker zu komponieren und anschließend alle anderen Container auszuführen (was die ganze Schönheit von Docker-Compose als einen einzigen Einstiegspunkt verdirbt).

@meetmatt hast du versucht, jwilder / nginx-proxy danach auszuführen? Die Startreihenfolge sollte keine Rolle spielen, sie nimmt beim Start vorhandene (laufende) Container auf

+1

+1

Ich würde wirklich gerne eine transparente kanalbasierte Lösung sehen. Grundsätzlich wie Libchan. Auf diese Weise wird die Anforderung gepuffert, wenn ich eine Datenbank abfrage, bis die Datenbank bereit ist.

Ich denke wirklich nicht, dass die Ladereihenfolge in verteilten Systemen eine ausreichende Lösung ist. Was ist, wenn Sie beispielsweise Ihre Datenbank neu starten müssen, andere Dienste jedoch möglicherweise abstürzen? Eine echte Lösung würde auch diesen Anwendungsfall behandeln.

Zählen Sie mich mit, denn die Vorhersehbarkeit der Ausführungspipeline ist eine Schlüsselsache für das, was wir bei der Arbeit tun. +1

+1

Mir muss etwas fehlen.

Warum befürwortet niemand, dem Docker-Lauf (der Docker-Engine selbst) ein "Warten bis" hinzuzufügen? In allen Fällen, die mir einfallen, weiß der abhängige Container, wann er "bereit" ist, aber Docker respektiert das nicht.

Im ursprünglichen Fall (MySQL lädt einen großen Datensatz und im Freien) kann der MySQL-Container zurückkehren oder signalisieren, wenn er bereit ist, und der Alfresco-Container würde bis dahin nicht starten.

Ich möchte eine beliebige Logik ausführen und dem Docker ein Signal geben, wenn ich bereit bin, wie von mir festgelegt (z. B. wenn eine bestimmte Meldung im Protokoll angezeigt wird -> Signal CONTAINER_UP).

net: "container:[name or id]" Warum nicht die Starts meiner Container bestellen? Ich musste links weil es veraltet ist und ich möchte, dass der gesamte Stack net: "host" Networking verwendet. Leider ist dies mit links nicht erlaubt. Gibt es eine andere Möglichkeit, die Startreihenfolge der Container zu ändern, oder muss ich nutzlose Volumes zwischen ihnen teilen?

Aktualisieren:

Ich habe gerade die Nachbestellung mit nutzlosen Volumes anstelle von links :

base:
  build: ./base
  net: "host"
  volumes:
    - /root/lemp_base
phpmyadmin:
  build: ./phpmyadmin
  net: "host"
  volumes_from:
    - base
  volumes:
    - /root/lemp_phpmyadmin
ffmpeg:
  build: ./ffmpeg
  net: "host"
  volumes_from:
    - phpmyadmin
  volumes:
    - /root/lemp_ffmpeg
mariadb:
  build: ./mariadb
  net: "host"
  volumes_from:
    - ffmpeg
  volumes:
    - /root/lemp_mariadb
php:
  build: ./php
  net: "host"
  volumes_from:
    - mariadb
  volumes:
    - /root/lemp_php
nginx:
  build: ./nginx
  net: "host"
  volumes_from:
    - php
  volumes:
    - /root/lemp_nginx

(Ich habe andere freigegebene Volumes aus dem Stapel gelöscht und andere Infos wie container_name, Ports, um einfach auszusehen.)

Wenn ich mit net: "container:base möchte, wird beim Befehl docker-compose build eine Fehlermeldung angezeigt.

ERROR: Service "mariadb" is trying to use the network of "lemp_base", which is not the name of a service or container.

Was mir an dieser Lösung nicht gefällt, ist, dass jeder andere Container die Webserver-Dateien im Ordner /var/www von base erhält.

BEARBEITEN:
Aus irgendeinem Grund löscht dieser Stapel beim Start den gesamten Ordner /var/www .

Meine bescheidene Meinung ist, dass jeder Mechanismus, der dazu führt, dass Docker Compose über die Abhängigkeit zwischen Containern Bescheid weiß, gegen die Trennung von Bedenken verstößt. Docker Compose ist für den Betrieb der Container A und B verantwortlich. Die Container A und B sind für ihren eigenen Service verantwortlich. Wenn B davon abhängt, dass A richtig funktioniert, liegt es in der Verantwortung von B, darauf zu warten, dass A in einwandfreiem Zustand ist. Wie bereits in der Diskussion erwähnt, kann dies durch Timeout, Wiederholungsversuche oder was auch immer erfolgen. Dies ist jedoch das Problem von B, nicht Docker Compose oder A. SoC ist für die Dienstunabhängigkeit und die ordnungsgemäße Skalierung von größter Bedeutung.

Gibt es Arbeiten an Ihrer Idee 3 @aanand ? Es wäre gut zu wissen, ob es Fortschritte gibt. Es klang nach einem vielversprechenden Start, der einige sehr häufige Anwendungsfälle unterstützen würde, auch wenn dies keine perfekte Lösung wäre

+1

+1

Vielleicht irre ich mich, aber args: buildno: kann die Container in docker-compose.yml Version 2 bestellen?

Ich stimme eher zu, dass dies ein Problem ist, das nicht zu Compose gehört. @jwilders exzellentes Dockerize hat gerade Unterstützung erhalten , um auf abhängige Container zu warten, und Sie können das Protokoll / den Port angeben, auf den Sie warten. Ich würde vorschlagen, dass dies für die meisten hier beschriebenen Anwendungsfälle geeignet ist:

api:
  build: .
  ports:
   - "8000:80"
  expose:
  - "80"

test:
  build: test
  command: dockerize -wait http://api:80 -wait tcp://db:5432 somecommand -some arg -another arg2
  links:
    - api:api

Idealerweise verwenden wir die Docker-Ereignis-API, um dies automatisch zu erkennen. Dies würde jedoch bedeuten, dass jeder Container auch Zugriff auf die Docker-Laufzeit benötigt, was wahrscheinlich nicht möglich ist / was wir möchten.

Ich denke, dass das Warten außerhalb des Komponierens erfolgen sollte. In meinem Entwickler werde ich eine Mischung aus den von Statusseiten timercheck.io verwenden. Ich denke, das gibt mir genau das, was ich brauche, ohne RabbitMQ oder ähnliches zu verwenden.

Wir verwenden einen Shell-Skript-Einstiegspunkt, der mit einer Zeitüberschreitung von 15 Sekunden auf den offenen Port wartet:

#!/usr/bin/env bash

# wait for db to come up before starting tests, as shown in https://github.com/docker/compose/issues/374#issuecomment-126312313
# uses bash instead of netcat, because netcat is less likely to be installed
# strategy from http://superuser.com/a/806331/98716
set -e

echoerr() { echo "$@" 1>&2; }

echoerr wait-for-db: waiting for db:5432

timeout 15 bash <<EOT
while ! (echo > /dev/tcp/db/5432) >/dev/null 2>&1;
    do sleep 1;
done;
EOT
RESULT=$?

if [ $RESULT -eq 0 ]; then
  # sleep another second for so that we don't get a "the database system is starting up" error
  sleep 1
  echoerr wait-for-db: done
else
  echoerr wait-for-db: timeout out after 15 seconds waiting for db:5432
fi

exec "$@"

Dies sollte durch das bevorstehende (und anscheinend unmittelbar bevorstehende Aktualisieren der Dokumente) depend_on gelöst werden, ja?

Nee. depends_on bestellt nur. Um das Starten eines anderen Containers tatsächlich zu verzögern, müsste es eine Möglichkeit geben, zu erkennen, wann ein Prozess die Initialisierung abgeschlossen hat.

Ah, danke für die Klarstellung. =)

Ich habe ein reines Bash-Befehlszeilenprogramm namens wait-for-it geschrieben , das in Docker-Bereitstellungen enthalten sein kann, um die Synchronisierung von Dienstbereitstellungen zu unterstützen.

Für mich ist es keine gute Idee, eine beliebige Sammlung von "Verfügbarkeitsprüfungen" fest zu codieren. Es gibt zahlreiche Situationen, die für eine bestimmte Art der Bereitstellung spezifisch sind, und Sie können niemals alle abdecken. In meiner Multi-Container-App muss ich beispielsweise warten, bis eine bestimmte Protokollmeldung in einer bestimmten Protokolldatei angezeigt wird. Erst dann ist der Containerdienst bereit.
Stattdessen wird ein SPI benötigt, das ich implementieren kann. Wenn Docker einige Beispielimplementierungen für die häufigsten Anwendungsfälle (z. B. TCP-Verbindung) bereitstellt, ist dies in Ordnung. Aber es muss eine Möglichkeit geben, meine eigene Funktionalität einzubinden und Docker sie aufrufen zu lassen.
Docker Compose ist für mich als ganzes Produkt so gut wie nutzlos, wenn ich meine Container nicht zuverlässig zum Laufen bringen kann. Daher ist ein stabiler und einheitlicher "Container Service Readiness SPI" erforderlich. Und "bereit" sollte kein Boolescher Wert sein, da es möglicherweise mehr Bereitschaftsstufen gibt (z. B. "Jetzt können Sie lesen" und "Jetzt können Sie schreiben").

@ Realulim Gute Berichterstattung. Ich stimme voll und ganz der Idee zu, dass wir über Plugins definieren können, was der "Bereit" -Status eines Dienstes bedeutet. Ich denke auch, dass es eine gute Idee ist, einen Standard für das Plugin zu haben, der nur überprüft, ob ein Dienst eine http / tcp-Verbindung abhört. Das würde die meisten Fälle genau dort abdecken.

Dies ist, was ich in der Einstiegspunktdatei gefunden habe;

until netcat -z -w 2 database 5432; do sleep 1; done
# do the job here, database host on port 5432 accepts connections

@ Kulbida ,
Ich mache etwas sehr ähnliches mit MySQL. "Datenbank" ist in diesem Fall ein Link in einer Erstellungsdatei.

if [[ "$APP_ENV" == "local" ]]; then
    while ! mysqladmin ping -h database --silent; do
        sleep 1
    done
    # Load in the schema or whatever else is needed here.
fi

In diesem Thread gab es einige Kommentare, die besagen, dass die Startreihenfolge nur eine Teilmenge der Fehlerbehebung auf Anwendungsebene ist, die Ihre Anwendung ohnehin behandeln sollte. Ich möchte ein Beispiel anführen, um zu veranschaulichen, wo dies möglicherweise nicht immer der Fall ist. Überlegen Sie, ob einige Dienste von einer Clusterdatenbank abhängen und wann immer ein Quorum aufgrund eines Absturzes usw. verloren geht, möchten Sie die App nicht automatisch wiederholen. Dies kann beispielsweise der Fall sein, wenn für die Datenbankwiederherstellung einige manuelle Schritte erforderlich sind und die Dienste eindeutig inaktiv bleiben müssen, bis diese Schritte ausgeführt werden.

Jetzt kann sich die Fehlerbehandlungslogik der App erheblich von der Startlogik unterscheiden:

  • Wenn die Datenbank nicht verfügbar ist, weil wir gerade erst starten, warten Sie, bis sie verfügbar ist.
  • Wenn die Datenbank aufgrund eines Absturzes nicht verfügbar ist, protokollieren Sie einen kritischen Fehler und sterben Sie ab.

Es ist möglicherweise nicht das häufigste Szenario, aber Sie sehen dieses Muster gelegentlich. In diesem Fall wird Clustering verwendet, um das Problem "Netzwerk ist unzuverlässig" im allgemeinen Fall zu lösen, wodurch sich einige der Erwartungen ändern, um die herum Fehlerbedingungen in der App wiederholt werden sollten. Cluster-Abstürze können selten genug sein, und ein automatischer Neustart kann so riskant sein, dass ein manueller Neustart von Diensten dem erneuten Versuch in der Anwendung vorgezogen wird. Ich vermute, dass es auch andere Szenarien gibt, die Annahmen in Frage stellen könnten, wann ein erneuter Versuch erforderlich ist.

Generell behaupte ich, dass Startreihenfolge und Fehlerbehandlung nicht immer gleichwertig sind und dass es für ein Framework angemessen ist, (optionale) Funktionen zum Verwalten der Startreihenfolge bereitzustellen. Ich frage mich allerdings, ob dies in die Docker-Engine gehört, anstatt zu komponieren. Es kann bei jedem Start des Dockers erforderlich sein, unabhängig davon, ob compose verwendet wird.

Es gibt eine Diskussion über das Docker-Engine-Repo im Vorschlag https://github.com/docker/docker/issues/21142 , um Unterstützung für die Integritätsprüfung hinzuzufügen. Sobald diese Unterstützung verfügbar ist, kann Compose sie konfigurieren und für einen verzögerten Start verwenden.

Wie wäre es mit dem Dateisystem, um zu überprüfen, ob eine Datei vorhanden ist?

ready_on: /tmp/this_container_is_up_and_ready

Auf diese Weise muss der Containerentwickler entscheiden, wann die Dinge auf dem neuesten Stand sind. Compose kann jedoch warten, bis sich der Container für bereit erklärt. Es ist eine explizite Konvention, kann aber leicht als zusätzliche Ebene zu Bildern hinzugefügt werden, die dieses Verhalten nicht aufweisen.

Die integrierte Unterstützung für Gesundheitschecks wird gut sein. In der Zwischenzeit ist hier der Hack, den ich in meinem lokalen Docker-Compose-Setup bekommen habe:

    nginx:
        image: nginx:latest
        command: /bin/bash -c "sleep 2 && echo starting && nginx -g 'daemon off;'"
        ...

(In der Produktion verwendet meine App Proxys für einige bereits laufende Upstream-Server mit proxy_pass . In lokalen Entwicklern und Tests starte ich Docker-Instanzen davon, und Nginx muss etwas warten, bis sie gestartet werden, andernfalls stürzt ab und stirbt. Das daemon off -Ding hält Nginx in einem einzigen Prozess, andernfalls stoppt Docker den Container, sobald der übergeordnete Prozess sein Daemon-Kind erzeugt.)

Nur um meine zwei Cent hinzuzufügen, wenn Sie zufällig das ANT-Build-Tool verwenden, wird es mit integrierter Unterstützung geliefert, um die Ausführung zu verzögern, bis ein bestimmter Socket geöffnet ist.

Unser Jenkins CI-Server startet die Projektcontainer mit Docker Compose und führt ANT dann wie folgt aus dem Hauptcontainer aus aus:

docker-compose up -d
docker exec -it projectx-fpm-jenkins ant -f /var/www/projectX/build.xml

Dies ist die relevante Konfiguration aus der Datei docker-compose.yml. Beachten Sie, dass es, wie oben erläutert, nicht ausreicht, fpm von MySQL abhängig zu machen, um sicherzustellen, dass der MySQL-Dienst bereit ist, wenn er tatsächlich benötigt wird.

version: '2'
services:
  nginx:
    build: ./docker/nginx
    depends_on:
      - fpm
  fpm:
    build: ./docker/fpm
    depends_on:
      - mysql
  mysql:
    image: mysql:5.7
    environment:
      - MYSQL_ROOT_PASSWORD=projectx
      - MYSQL_DATABASE=projectx

Sie können jedoch während der ANT-Aufgabe darauf warten:

<!-- other targets... -->

<target name="setup db">
    <!-- wait until the 3306 TCP port in the "mysql" host is open -->
    <waitfor>
        <socket server="mysql" port="3306"/>
    </waitfor>

    <exec executable="php">
        <arg value="${consoledir}/console"/>
        <arg value="doctrine:database:create"/>
        <arg value="--no-interaction"/>
    </exec>
</target>

@ Kulbida Das hat den Trick gemacht, danke. Etwas schneller:

while ! nc -w 1 -z db 5432; do sleep 0.1; done

_depends_on_ könnte das Problem lösen.
Aus der Docker-Compose- Dokumentation.
Expressabhängigkeit zwischen Diensten, die zwei Auswirkungen hat:

  1. Docker-Compose Up startet Dienste in Abhängigkeitsreihenfolge. Im folgenden Beispiel werden db und redis vor dem Web gestartet.
  2. Docker-Compose-up-SERVICE enthält automatisch die Abhängigkeiten von SERVICE. Im folgenden Beispiel erstellt und startet Docker-Compose-Up-Web auch DB und Redis.

Version 2'
Dienstleistungen:
Netz:
bauen: .
kommt drauf an:
- db
- Redis
redis:
Bild: Redis
db:
Bild: Postgres

@alexch : bei einem kundenseitigen Leistungstest (

@syamsathyan depends_on scheint nicht zu helfen.

@skorokithakis , @kulbida das ist eine schöne Lösung. Leider ist netcat in keinem der Dienste, die ich zum Herstellen einer Verbindung mit meiner Datenbank benötige, standardmäßig verfügbar (einschließlich postgres ). Kennen Sie eine alternative Methode?

@nottrobin Ich fürchte nicht, ich habe es gerade in meinem Image installiert: /

@nottrobin mein Team arbeitet daran, wird Sie in ein oder zwei Tagen wissen lassen!

Für diejenigen, die kürzlich Bash gespielt haben, gibt es eine Lösung ohne Netzkatze (inspiriert von: http://stackoverflow.com/a/19866239/1581069):

while ! timeout 1 bash -c 'cat < /dev/null > /dev/tcp/db/5432'; do sleep 0.1; done

oder weniger ausführliche Version:

while ! timeout 1 bash -c 'cat < /dev/null > /dev/tcp/db/5432' >/dev/null 2>/dev/null; do sleep 0.1; done

@typekpb das funktioniert perfekt. Vielen Dank!

Nachdem die Unterstützung für HEALTHCHECK gemäß https://github.com/docker/docker/pull/23218 im Upstream zusammengeführt wurde, kann dies berücksichtigt werden, um festzustellen, wann ein Container fehlerfrei ist, bevor der nächste in der Reihenfolge gestartet wird. Die Hälfte des Puzzles gelöst :)

Nachdem die HEALTHCHECK-Unterstützung gemäß Docker / Docker Nr. 23218 vorgelagert zusammengeführt wurde, kann dies berücksichtigt werden, um festzustellen, wann ein Container fehlerfrei ist, bevor der nächste in der Reihenfolge gestartet wird. Die Hälfte des Puzzles gelöst :)

Sieht gut aus. Wie implementiere ich es auf docker-compose.yml ?

Sieht gut aus. Wie implementiere ich es auf docker-compose.yml?

Das andere Teil des Puzzles besteht darin, dass Docker-Compose auf gesunde Container achtet und so etwas wie die in dieser Ausgabe weiter oben beschriebene Syntax "Depends_on" verwendet. Benötigt Patches, um Docker zu komponieren, damit alles funktioniert.

Beachten Sie auch, dass die Integritätsprüfungsfunktion in Docker derzeit nicht freigegeben ist und daher möglicherweise an einem Docker / Docker Compose-Veröffentlichungszyklus ausgerichtet werden muss.

Ich habe eine js-Bibliothek geschrieben, die eine Methode .waitForPort() . Wie bereits erwähnt, funktioniert dies möglicherweise nicht in allen Situationen, kann jedoch in den meisten Anwendungsfällen problemlos ausgeführt werden.
Siehe meinen Blog .

Die HEALTHCHECK-Fusion ist eine gute Nachricht.

In der Zwischenzeit beschreibt dieses Dokument das Problem und einige Lösungen.

@pablofmorales Nein, weil depends_on nur überprüft, ob der Container aktiv ist.

Einige Daemons benötigen zusätzliche Zeit, um sich selbst zu booten und ihre zugewiesenen Ports und Adressen abzuhören, insbesondere MySQL.

Ich denke immer noch, dass eine "READY_ON" -Deklaration immer noch die beste ist. Es lässt die Entscheidung darüber, wann etwas für den Container selbst bereit ist, unabhängig vom Image, bei der Auswahl explizit und die Ressourcenpfadfunktion (innerhalb des Containers) in der Docker Remote-API stellt sicher, dass nur minimale Änderungen erforderlich sind.

Das Verhalten, wenn ein Container "aktiv" ist, ist die einzige Auswirkung, die dies haben sollte. Es wird nur als "up" gemeldet, wenn die Datei READY_ON vorhanden ist.

Ich denke, das sind 90% des Verhaltens, über das alle diskutiert haben. Ich denke, dass "Healthcheck" hier als zwei verschiedene Ereignisse zusammengeführt wird, aber versucht wird, es zu einem zusammenzufassen. Einer ist "bereit" für die Kette von Ereignissen beim Hochfahren der Infrastruktur, der andere ist "Gesundheit", damit die Infrastruktur aufrechterhalten werden kann.

"ready" ist ein geeigneter Ort für Docker, um zu helfen. Was "Gesundheit" betrifft, so ist es in Bezug auf die Systeme so unterschiedlich, dass ich denke, es liegt am Container, damit umzugehen.

Für eine bessere Alternative zum Healthcheck sollten Sie sich einen Containerpiloten ansehen, der nicht nur den Zustand, sondern auch die Erkennung und Überwachung von Diensten abdeckt. https://github.com/joyent/containerpilot

Ja, dies ist eine genaue und wichtige Unterscheidung. Wie schreiben Container diese Datei, ohne dass Bilder wesentlich komplizierter werden? Es scheint mir, dass für jeden einzelnen Container, der dies verwenden möchte, ein Wrapper-Skript erforderlich wäre.

Nun, Sie müssten sowieso ein Skript starten, um die Instanz zu initialisieren ... Das letzte, was das Skript tun muss, ist eine Datei zu berühren. Für mich scheint das viel einfacher zu sein, als zu versuchen, eine Exec auf einem Remotecomputer auszuführen, um eine Integritätsprüfung durchzuführen. Zumindest mit einer Touch-Datei kann sie vollständig passiv über die API usw. angesehen werden, ohne dass der Kontext des Containers eingegeben werden muss.

Ich stimme zu, aber viele Container verwenden kein Skript. Sie installieren lediglich einen Dienst wie Postgres oder Redis und lassen ihn starten, ohne ihn anzusehen.

In meinem Fall verwende ich Kong API Gateway

Bevor ich den Kong-Container starte, überprüfe ich nur, ob Cassandra mit diesem Skript arbeitet

while true; do
    CHECK=`kong-database/check`
    if [[ $CHECK =~ "system.dateof" ]]; then
        break
    fi
    sleep 1;
done

Die Prüfdatei enthält dies

#!/bin/bash
docker cp cassandra-checker kong-database:/root/
docker exec -i kong-database cqlsh -f /root/cassandra-checker

Cassandra-Checker ist nur eine einfache Abfrage

SELECT dateof(now()) FROM system.local ;

Sicher, aber die Alternative ist ein Healthcheck, für den ein Skript erforderlich ist, das Sie sowieso schreiben müssten, sodass es keinen Overhead-Unterschied gibt. Es ist auch eine explizite Anmeldung, was bedeutet, dass Sie angeben, dass Sie dieses Verhalten möchten. Für etwas, das kein Skript ausführt, können Sie jederzeit eine ready_on-Pfadprüfung für eine PID-Datei oder einen Unix-Socket durchführen lassen. das würde kein Skript erfordern.

Das stimmt, du hast recht.

Das Überprüfen auf das Vorhandensein einer Datei kann in vielen Fällen in Ordnung sein, aber das Erzwingen der Verwendung eines Startskripts durch Container, wenn sie sonst keines benötigen würden, ist ein Ärgernis. Warum kann es nicht auch Überprüfungen für andere sehr einfache Bedingungen geben? Besonders nützlich wäre es zu warten, bis der Prozess einen bestimmten TCP-Port abhört.

Diese Idee ist opt-in, so dass nichts erzwungen werden muss. Tatsächlich sagen Sie ausdrücklich, was zu erwarten ist.

Das Abhören eines TCP-Ports reicht möglicherweise nicht aus, um festzustellen, wann ein Container initialisiert wurde, da möglicherweise eine Reihe von Setup-Daten ausgeführt werden müssen. Zum Teufel, wenn Sie zu schnell eine Verbindung zu einem Postgres-Container herstellen, auch über TCP, wird eine Fehlermeldung angezeigt, dass die Datenbank noch nicht bereit ist.

Wenn ich Sie richtig verstehe, ist es "Opt-In, oder Sie können diese Funktion nicht verwenden". Ergo, wenn ich diese Funktion benötige und meine App keine PID-Datei verwendet, muss ich ein Startskript verwenden.

Für MySQL (den Fall des OP) ist es bereit, sobald es zuhört. Sie geben sich viel Mühe, um sicherzustellen, dass dies wahr ist, wahrscheinlich für Fälle wie diesen. Meiner Meinung nach gibt es wahrscheinlich eine kurze Liste von Bedingungen, die aufgezählt werden könnten, so dass Sie sich für die Konfiguration einer Bereitschaftsprüfung für eine dieser Bedingungen "anmelden" können. Ich sehe keinen Grund, warum es nur auf eine Weise gemacht werden muss.

Für MySQL ist es nicht bereit, sobald es zuhört. Im einfachen Fall mit einem Knoten ist es fertig, aber wenn Sie mehr als einen Knoten haben, ist es sicherlich noch nicht fertig. Ich verstehe, was Sie unter "einem und nur einem Weg" verstehen, aber ich denke, als Basisabstraktion ist es einfach perfekt. Ich sehe es eher als einen Ort, an dem Sie jedes gewünschte Werkzeug anwenden können. Ihr Skript könnte sogar mit externen Diensten kommunizieren und diese den Container überprüfen lassen. In diesem Fall könnten Ihre externen Dienste Ihrem Containeragenten signalisieren, die Datei zu schreiben. Flexibilität ftw.

Wenn Sie etwas in dieser Liste der "Bedingungen" versuchen, wird es IMMER einen Fall geben, in dem es nicht funktioniert. Das Berühren einer Datei funktioniert jedoch immer, da das Bild weiß, wann es glaubt, dass es fertig ist (oh, ich muss auf anderen Hosts warten, ich muss Dateien herunterladen, ich muss sicherstellen, dass $ external_service auch verfügbar ist, ich spanne auf richtig, aber aus irgendeinem Grund habe ich nicht die richtigen Berechtigungen für die Datenbank, warum ist dieses Bild schreibgeschützt ... usw. usw.

Diese Art von Skripten gibt es bereits überall ... verdammt noch mal, es war schon notwendig, diese Skripte zu schreiben, weil wir vorher noch keine solche Funktionalität hatten. Das Einfügen eines solchen Skripts ist also minimal, da wahrscheinlich bereits ein Skript vorhanden ist.

Ein anderer wahrscheinlicher Fall ist, dass Sie so etwas wie einen Koch oder einen Ansible gegen diesen Host laufen lassen und dann die Datei schreiben würden.

Wenn es sich um eine Docker-seitige Prüfung handelt, dann so etwas wie;

UPCHECK --port=7474 --interval=0.5s --response="Please log in"

Ich denke, die Dateilösung hat viele Vorteile, bringt aber auch Komplexität mit sich.
In 80% der Fälle funktioniert die Überprüfung der TCP-Antwort einwandfrei.

Nun ... ich nehme an:

UPCHECK --file=/tmp/container_is_ready --interval=0.5s --timeout=2m

Ist genauso.

Ich arbeite gerade an einer Neuimplementierung von Docker-Compose, die Funktionen hinzufügt, um auf bestimmte Bedingungen zu warten. Es verwendet libcompose (damit ich die Docker-Interaktion nicht neu erstellen muss) und fügt hierfür eine Reihe von Konfigurationsbefehlen hinzu. Überprüfen Sie es hier: https://github.com/dansteen/controlled-compose

Beachten Sie, dass der Code fertig ist, aber ich warte darauf, dass einige Upstream-Probleme behoben werden, bevor diese wirklich verwendet werden können.

Goss kann als ziemlich flexibler Shim verwendet werden, um den Start des Containers zu verzögern. Ich habe einen Blog-Beitrag geschrieben, in dem erklärt wird, wie dies mit einer geringfügigen Änderung Ihres Images hier erreicht werden kann:

Kubernetes hat das Konzept von Init-Containern. Ich frage mich, ob Compose / Swarm von einem ähnlichen Konzept profitieren würde.

+1

Ich denke, es ist besser, den Dienst, den Sie auf einem Container verfügbar machen, entscheiden zu lassen, ob er bereit oder in der Lage ist, seinen Dienst verfügbar zu machen.

Beispielsweise kann eine PHP-Anwendung von der MySQL-Verbindung abhängen. Also habe ich auf dem ENTRYPOINT von PHP Container so etwas geschrieben.

#!/bin/bash
cat << EOF > /tmp/wait_for_mysql.php
<?php
\$connected = false;
while(!\$connected) {
    try{
        \$dbh = new pdo( 
            'mysql:host=mysql:3306;dbname=db_name', 'db_user', 'db_pass',
            array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
        );
        \$connected = true;
    }
    catch(PDOException \$ex){
        error_log("Could not connect to MySQL");
        error_log(\$ex->getMessage());
        error_log("Waiting for MySQL Connection.");
        sleep(5);
    }
}
EOF
php /tmp/wait_for_mysql.php
# Rest of entry point bootstrapping

Auf diese Weise kann ich jede Logik hinzufügen, um sicherzustellen, dass die Abhängigkeiten des Dienstes, den ich verfügbar mache, dh PHP, aufgelöst wurden.

Nabin Nepal schrieb:

Ich denke, es ist besser, den Dienst, den Sie auf einem Container verfügbar machen, entscheiden zu lassen, ob er bereit oder in der Lage ist, seinen Dienst verfügbar zu machen.

Sie können dieses Verhalten natürlich in jeden Container fest codieren, der Ihre verwendet
MySQL-Container. Wenn sich jedoch etwas in Ihrem MySQL-Dienst ändert, sind Sie es
Ändern aller abhängigen Container, ganz zu schweigen von der sich wiederholenden Codierung
in jedem benötigt. Dies ist nicht trocken, es gibt keinen stabilen Vertrag und somit wird es
zu spröden Systemen führen.

Vom Standpunkt der Software-Handwerkskunst sollte es eine Art geben
"Container Readiness SPI", das der Containerentwickler implementieren kann. Auf
Auf der anderen Seite sollte es eine "Container Readiness API" geben, die die
Dienstleistungen können abhängen.

Ulrich

@realulim Ich bin damit einverstanden, dass jede Änderung im MySQL-Container repliziert oder an alle betroffenen oder verknüpften Container weitergegeben werden muss.

Wenn es sich bei der Änderung jedoch um Parameter wie DB_HOST, DB_NAME, DB_USER und DB_PASSWORD handelt. Diese können als ARG (Argument) übergeben und von allen zugehörigen Containern gemeinsam genutzt werden. Wenn Sie dann eine docker-compose.yml -Datei verwenden, erfolgt die Änderung für eine Datei.

Und stimme voll und ganz zu, dass eine API zur Überprüfung der Bereitschaft des Containers der eigentliche Weg ist, dies zu lösen, aber ich glaube immer noch, dass der offen gelegte Dienst ein besserer Kandidat wäre, um dies zu deklarieren.

eine Problemumgehung until nc -z localhost 27017; do echo Waiting for MongoDB; sleep 1; done

@ piotr-s-brainhub In den obigen Kommentaren wird erwähnt, dass ein offener Port nicht bedeutet, dass der Dienst bereit ist.

Können wir eine optionale Bereitschaftsbedingung haben, die entweder durch Protokolle, Portöffnung oder Zeitverzögerung ausgelöst werden kann? Etwas wie:

ready_when:
  in_logs: `MySQL init process done`
  ports_open:
  - 3306

Ich habe gerade festgestellt, dass das Warten auf die Bereitschaft von Abhängigkeitscontainern mit Tools wie ansible einfach implementiert werden kann. Hat jemand diesen Ansatz verwendet? Können Sie Docker-Compose einfach durch Ansible / Chef / Puppet ersetzen? Gibt es ein Projekt auf Github, das diesen Ansatz demonstriert?

Hinweis: Ich verstehe, wie wichtig es ist, einen robusten Dienst zu schreiben, der auch dann ausgeführt werden kann, wenn seine Abhängigkeiten derzeit nicht verfügbar sind. Das ist nicht die Frage.

Ich habe dies heutzutage mit einem Tool gelöst, das ich geschrieben habe: https://github.com/betalo-sweden/await

Es kann warten, bis eine bestimmte Liste von Ressourcen verfügbar ist, und mit dem fortfahren, was Sie fortsetzen möchten, indem Sie entweder implizit zum nächsten Befehl wechseln oder ihn explizit aufrufen.

@djui , was macht das

@derekmahar Es Umfragen. Das Standardzeitlimit beträgt 60 Sekunden. Jedes Mal, wenn die Ressource nicht angezeigt wird, wird sie nur in Intervallen von 1 s wiederholt. Derzeit wird keine gleichzeitige Ressourcenerkennung durchgeführt, daher ist es sequentiell, aber das hat sich als gut genug herausgestellt und kann behoben werden.

Ich benutze es im folgenden Szenario:

Ich starte eine Docker-Compose-Infrastruktur und führe dann einen Integrationstesttreiber aus. Der Treiberdienst wird erst gestartet, nachdem alle Komponenten in der Infrastruktur verfügbar sind. also wait wartet schließlich den Befehl run des Treibers auf.

Hier ist eine Möglichkeit, dies mit der neuen Docker HEALTHCHECK-Direktive mit make zu tun:

https://gist.github.com/mixja/1ed1314525ba4a04807303dad229f2e1

[UPDATE: Aktualisierter Inhalt, um zu behandeln, ob der Container mit einem Fehlercode beendet wird, da Docker 1.12 den Healthcheck-Status des gestoppten Containers etwas dumm als "Start" meldet]

Danke @mixja , schöne Lösung.

@mixja , schöne Lösung! Das ist genau die Funktionalität, die ich erwarten würde. Aber jetzt ist die Frage, wenn Sie Ihre Container manuell starten, warum brauchen Sie Docker-Compose überhaupt?

Zum Testen verwende ich https://github.com/avast/docker-compose-gradle-plugin und es verwendet auch den Docker Healthcheck - keine künstlichen Pausen mehr, schnellere Builds.

@korya - Docker Compose ist nicht wirklich ein Orchestrierungswerkzeug - es ist eher ein Umgebungsspezifikations- und Verwaltungstool. Ich verwende Make, um eine prozedurale Orchestrierung über Docker Compose und Docker (und andere Tools nach Bedarf) bereitzustellen. Die Kombination von Make, Docker und Docker Compose ist sehr leistungsfähig und Sie können mit diesen Bausteinen viele verschiedene Szenarien erzielen.

@mixja gut, vielleicht hast du recht. Aber wie viele Leute in diesem Thread darauf hingewiesen haben, ist eine Orchestrierungsfunktionalität in Testumgebungen sehr wichtig, und wenn Ihre Toolbox Docker-Compose enthält, ist es sehr verlockend, diese Art von Funktionalität von Docker-Compose zu fordern.

Laut den Dokumenten ist "Compose ein Tool zum Definieren und Ausführen von Docker-Anwendungen mit mehreren Containern". Obwohl es nicht heißt, dass Compose ein Orchestrierungswerkzeug ist, denke ich, dass es aus Sicht des Benutzers (z. B. mir selbst) selbstverständlich ist, von einem "Werkzeug zum Definieren und Ausführen von Docker-Anwendungen mit mehreren Containern" zu erwarten, um das grundlegende Abhängigkeitsmanagement zwischen den verwalteten Containern zu unterstützen out of the box.

Ich sage nicht, dass das Tool es unterstützen muss. Ich sage nur, dass es sehr natürlich ist, es zu erwarten. Ansonsten muss sich jeder seine superschlauen Wege einfallen lassen. Tatsächlich verwenden wir ein Bash-Skript, das etwas Ähnliches tut wie Ihr Makefile.

@mixja @korya Ich möchte mein Werkzeug zur Verbesserung der await und möchten Sie bitten um Feedback , was Sie Makefile - Versionen bieten, was fehlt / bequemer / Aktivierung über await .

Es scheint, dass die Version healthcheck + make eine "globale" Ansicht zu sein scheint, kein einzelner Container kennt den globalen Status (das Makefile jedoch) und await ist eine "lokale" Ansicht, jeder aktivierte Container kennt (nur) was es wissen muss, ähnlich wie depends_on oder links . Außerdem möchten Sie den Container lieber mit den für den Healthcheck erforderlichen Tools versenden (was manchmal die Standardeinstellung ist, z. B. mysqlshow ) und ansonsten die Docker-Datei unberührt lassen. Außerdem scheinen Sie Docker-Compose nicht mehr hauptsächlich für die Komposition, sondern hauptsächlich für die flexible Konfiguration zu verwenden (z. B. sollte docker-compose up -d mysql docker run -d -e ... -v ... -p ... mysql ).

Hallo @djui - es ist wahrscheinlich eine philosophische Sichtweise, aber ich denke, die ganze Prämisse des HEALTHCHECK fördert das richtige Verhalten - dh ein Container kann ein Mittel zur Feststellung des Containerzustands ohne externe Abhängigkeiten darstellen.

Dies beeinträchtigt keineswegs den Wert einer externen Konnektivitätsüberprüfung. In der Regel würde ich jedoch eine Reihe von Abnahmetests durchführen, um dies abzudecken, da Sie die Konnektivität und vieles mehr überprüfen möchten (z. B. Anwendungsfunktionalität). Natürlich können Sie diese Teststufe im Allgemeinen erst ausführen, wenn eine vollständige Umgebung eingerichtet wurde und der Umfang Ihres await -Tools und anderer Ansätze, die ich in der Vergangenheit verwendet habe (Ansible-Playbooks in einem agent container) konzentriert sich wirklich darauf, die Umgebung korrekt zu orchestrieren (nicht das Endziel von Abnahmetests) und war bis jetzt wirklich der einzige verfügbare Ansatz in einer Docker-Welt.

Mit Docker 1.12 haben wir jetzt die Möglichkeit, die Docker-Umgebung zu überprüfen und gut etablierte Konstrukte (dh Bash / Shell-Mechanismen) zu verwenden, um auf einen bestimmten Zustand zu "warten", natürlich solange unsere Container ihre eigenen Integritätsprüfungen definiert haben . Ich sehe mehr Wert darin, die nativen Funktionen der Plattform zu nutzen und Containerbesitzer zu ermutigen, ihre eigenen Integritätsprüfungen zu definieren, anstatt sich auf den historischen externen Ansatz zu verlassen (ich habe meinen Bewerbungsprozess gestartet, es ist nicht mehr mein Problem), den wir hatten zurückgreifen auf.

Betrachten Sie als verwandte Analogie AWS CloudFormation und das Konzept der automatischen Skalierung von Gruppen und der Orchestrierung fortlaufender Updates. Woher weiß CloudFormation, ob eine neue Instanz "fehlerfrei" ist und wir eine alte Instanz beenden und eine andere neue Instanz einspielen können? Schreiben wir einen externen Healthcheck oder verlassen wir uns auf die Instanz selbst, um den Zustand zu signalisieren? Die Antwort ist die letztere. Dies bedeutet, dass der Instanzeigner die für seine Instanz erforderlichen Erfolgskriterien festlegen und dann dem übergeordneten Orchestrierungssystem (dh CloudFormation) signalisieren kann, dass die Instanz "fehlerfrei" ist.

In Bezug auf Ihre Kommentare zu Docker Compose - es ist ein Tool, das beide von Ihnen erwähnten Aspekte bereitstellen kann. Der docker-compose.yml -Teil ist die gewünschte Umgebungsspezifikation für die Zustandszusammensetzung, während die verschiedenen docker-compose -Befehle die Möglichkeit bieten, auf verschiedene Weise mit der Umgebung zu interagieren. Derzeit benötigen wir externe Orchestrierungs-Tools, da docker-compose das Abhängigkeitsmanagement zwischen Diensten grundsätzlich nicht gut genug durchführt. Da docker-compose Funktionen wie die Unterstützung für native Gesundheitschecks erhält, ist das Ziel eines einzelnen docker-compose up -Befehls realistischer, vorausgesetzt, wir können beispielsweise angeben, dass ein Dienst zuvor als fehlerfrei markiert werden muss Es wird als "up" betrachtet, was bedeutet, dass unsere abhängigen Dienste effektiv warten, bis die Abhängigkeit gesund ist.

@mixja Danke für die ausführliche Erklärung. Meiner Ansicht nach

Ich sehe mehr Wert darin, die nativen Funktionen der Plattform zu nutzen

ist ein guter / der Hauptpunkt. Warten Sie nur darauf, dass Docker Compose die Integritätsprüfungen nativ entweder in abhängigen_on oder einem neuen Schlüssel nutzt, und warten Sie. Ich frage mich nur, ob ich noch einen Schritt weiter gehen sollte / werde und verknüpfte Container im Grunde genommen herunterfährt, wenn z. B. --abort-on-container-exit gesetzt ist und eine Integritätsprüfung zur Laufzeit das Healthcheck-Label auf _unhealthy_ setzt.

Mögliche vorübergehende Problemumgehung für diejenigen unter Ihnen, die nach delay -Funktionalität suchen, um Tests durchzuführen:

Ich habe zwei docker-compose yml-Dateien. Eine dient zum Testen und eine zum Entwickeln. Der Unterschied besteht nur darin, dass sut Container in docker-compose.test.yml . sut Container läuft pytest . Mein Ziel war es, den Test docker-compose auszuführen. Wenn der Befehl pytest in sut fehlschlägt, führen Sie die Entwicklung docker-compose . Folgendes habe ich mir ausgedacht:

# launch test docker-compose; note: I'm starting it with -p argument
docker-compose -f docker-compose.test.yml -p ci up --build -d
# simply get ID of sut container
tests_container_id=$(docker-compose -f docker-compose.test.yml -p ci ps -q sut)
# wait for sut container to finish (pytest will return 0 if all tests passed)
docker wait $tests_container_id
# get exit code of sut container
tests_status=$(docker-compose -f docker-compose.test.yml -p ci ps -q sut | xargs docker inspect -f '{{ .State.ExitCode  }}' | grep -v 0 | wc -l | tr -d ' ')
# print logs if tests didn't pass and return exit code
if [ $tests_status = "1" ] ; then
    docker-compose -f docker-compose.test.yml -p ci logs sut
    return 1
else
    return 0
fi

Jetzt können Sie den obigen Code in jeder Funktion Ihrer Wahl verwenden (meine heißt test ) und so etwas tun:

test
test_result=$?
if [[ $test_result -eq 0 ]] ; then
    docker-compose -f docker-compose.yml up --build -d
fi

Funktioniert gut für mich, aber ich freue mich immer noch darauf zu sehen, dass docker-compose solche Sachen von Haus aus unterstützen :)

+1

Vielleicht könnten Dinge, die außerhalb des Kerns von Docker-Compose betrachtet werden, durch das Zulassen von Plugins unterstützt werden? Ähnlich wie bei der Anfrage Nr. 1341 scheint es zusätzliche Funktionen zu geben, die einige nützlich finden würden, die jedoch nicht unbedingt vollständig mit der aktuellen Vision übereinstimmen. Vielleicht würde die Unterstützung eines Plugin-Systems, wie es von # 3905 vorgeschlagen wurde, eine Möglichkeit bieten, sich auf einen Kernsatz von Funktionen zu konzentrieren, und wenn dies nicht der Fall ist, könnten diejenigen, die es für ihren speziellen Anwendungsfall wünschen, ein Plugin schreiben, um die Leistung von up zu handhaben

Es wäre schön, wenn docker-compose als Einstiegspunkt für alle Projekte fungieren könnte, die wir lokal rund um das Docker-Env-Setup haben, anstatt ein Skript hinzufügen zu müssen, das vor allen sitzt, um stattdessen als Standardeinstiegspunkt zu fungieren Leute, die daran denken müssen, das Skript für die ungeraden Fälle auszuführen.

Hier ist eine Möglichkeit, dies mit healthcheck und Docker-Compose 2.1+ zu tun:

version: "2.1"
services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: password
    healthcheck:
      test: mysqladmin -uroot -ppassword ping
      interval: 2s
      timeout: 5s
      retries: 30
  web:
    image: nginx:latest # your image
    depends_on:
      db:
        condition: service_healthy

Hier startet docker-compose up den Webcontainer erst, nachdem der Datenbankcontainer als fehlerfrei eingestuft wurde.

Es tut mir leid, wenn es bereits erwähnt wurde, aber ich glaube nicht, dass eine vollständige Lösung veröffentlicht wurde.

Hier ist ein Weg für PostgreSQL.

Danke @Silex 👍

version: '2.1'
services:
  db:
    image: postgres:9.6.1
    healthcheck:
      test: "pg_isready -h localhost -p 5432 -q -U postgres"
      interval: 3s
      timeout: 5s
      retries: 5

@Silex leider mit Version "3" und diesem Format:

    image: nginx:latest # your image
    depends_on:
      db:
        condition: service_healthy

Ich bekomme ERROR: The Compose file './docker-compose.yml' is invalid because: depends_on contains an invalid type, it should be an array

2.1 unterstützt es weiterhin und wird nicht veraltet sein. 3.x ist hauptsächlich für den Schwarmdienstmodus (nicht lokal) vorgesehen.

  From: Vlad Filippov <[email protected]>

An: docker / compose
Cc: mbdas [email protected] ; Erwähnen Sie Erwä[email protected]
Gesendet: Mittwoch, 8. März 2017, 11:45 Uhr
Betreff: Betreff: [Docker / Compose] Gibt es eine Möglichkeit, den Start des Containers zu verzögern, um abhängige Dienste mit einer längeren Startzeit zu unterstützen (# 374)?

@Silex leider mit Version "3" und diesem Format: Bild: Nginx: Neueste # Ihr Bild
kommt drauf an:
db:
Bedingung: service_healthy
Ich erhalte FEHLER: Die Compose-Datei './docker-compose.yml' ist ungültig, weil: services.auth.depends_on einen ungültigen Typ enthält, es sollte ein Array sein -
Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail, zeigen Sie sie auf GitHub an oder schalten Sie den Thread stumm.

2.1 unterstützt es weiterhin und wird nicht veraltet sein. 3.x ist hauptsächlich für den Schwarmdienstmodus (nicht lokal) vorgesehen.

Vielen Dank!

@vladikoff : Weitere Informationen zu Version 3 unter https://github.com/docker/compose/issues/4305

Grundsätzlich wird es nicht unterstützt, Sie müssen Ihre Container fehlertolerant machen, anstatt sich auf Docker-Compose zu verlassen.

Ich glaube, das kann jetzt geschlossen werden.

Leider wird der Zustand in Version 3 nicht mehr

website:
    depends_on:
      - 'postgres'
    build: .
    ports:
      - '3000'
    volumes:
      - '.:/news_app'
      - 'bundle_data:/bundle'
    entrypoint: ./wait-for-postgres.sh postgres 5432

  postgres:
    image: 'postgres:9.6.2'
    ports:
      - '5432'

wait-for-postgres.sh:

#!/bin/sh

postgres_host=$1
postgres_port=$2
shift 2
cmd="$@"

# wait for the postgres docker to be running
while ! pg_isready -h $postgres_host -p $postgres_port -q -U postgres; do
  >&2 echo "Postgres is unavailable - sleeping"
  sleep 1
done

>&2 echo "Postgres is up - executing command"

# run the command
exec $cmd

Der benutzerdefinierte Einstiegspunkt @ slava-nikulin ist eine gängige Praxis. Es ist fast die einzige (Docker-native) Möglichkeit, alle Bedingungen zu definieren und zu überprüfen, die Sie benötigen, bevor Sie Ihre App in einem Container starten.

Die Wahrheit ist, dass es viele Debatten gab und ich denke, dass die 2.x-Unterstützung für die bedingte Unterstützung zur nativen Integration in Gesundheitschecks und die Bestellung des Startups eine dringend benötigte Unterstützung war. Docker unterstützt keine lokalen Pods von Containern von Haus aus, und wenn dies der Fall ist, muss es wieder etwas Ähnliches unterstützen, so wie beispielsweise Kubernetes die Semantik bereitstellt.

Docker 3.x ist eine Serie, die die Schwarmunterstützung in das Komponieren einbringt. Daher wurden eine Reihe von Optionen gestrichen, um die verteilte Natur zu berücksichtigen.

Die 2.x-Serie behält die ursprünglichen Funktionen für die Erstellung / lokale Topologie bei.

Docker muss herausfinden, wie diese beiden Versionen zusammengeführt werden können, da das Erzwingen des Schwarms zum Komponieren durch Reduzieren des Kompositionssatzes keine willkommene Richtung ist.

Am 10. Mai 2017 um 20.15 Uhr schrieb Slava Nikulin [email protected] :

Leider wird der Zustand in Version 3 nicht mehr unterstützt. Hier ist eine Problemumgehung, die ich gefunden habe:

Webseite:
kommt drauf an:
- "Postgres"
bauen: .
Häfen:
- "3000"
Bände:
- '.: / news_app'
- 'bundle_data: / bundle'
Einstiegspunkt: ./wait-for-postgres.sh postgres 5432

postgres:
Bild: ' postgres: 9.6.2 '
Häfen:
- '5432'
wait-for-postgres.sh:

! / bin / sh

postgres_host = $ 1
postgres_port = $ 2
cmd = "$ @"

Warten Sie, bis der Postgres-Docker ausgeführt wird

während! pg_isready -h $ postgres_host -p $ postgres_port -q -U postgres; tun

& 2 echo "Postgres ist nicht verfügbar - schlafend"
Schlaf 1
getan

& 2 echo "Postgres ist aktiv - Befehl wird ausgeführt"

Führen Sie den Befehl aus

exec $ cmd
- -
Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail, zeigen Sie sie auf GitHub an oder schalten Sie den Thread stumm.

Ich konnte so etwas machen
// start.sh

#!/bin/sh
set -eu

docker volume create --name=gql-sync
echo "Building docker containers"
docker-compose build
echo "Running tests inside docker container"
docker-compose up -d pubsub
docker-compose up -d mongo
docker-compose up -d botms
docker-compose up -d events
docker-compose up -d identity
docker-compose up -d importer
docker-compose run status
docker-compose run testing

exit $?

// status.sh

#!/bin/sh

set -eu pipefail

echo "Attempting to connect to bots"
until $(nc -zv botms 3000); do
    printf '.'
    sleep 5
done
echo "Attempting to connect to events"
until $(nc -zv events 3000); do
    printf '.'
    sleep 5
done
echo "Attempting to connect to identity"
until $(nc -zv identity 3000); do
    printf '.'
    sleep 5
done
echo "Attempting to connect to importer"
until $(nc -zv importer 8080); do
    printf '.'
    sleep 5
done
echo "Was able to connect to all"

exit 0

// in meiner Docker-Compose-Datei

  status:
    image: yikaus/alpine-bash
    volumes:
      - "./internals/scripts:/scripts"
    command: "sh /scripts/status.sh"
    depends_on:
      - "mongo"
      - "importer"
      - "events"
      - "identity"
      - "botms"

Ich habe ein ähnliches Problem, aber ein bisschen anders. Ich muss warten, bis MongoDB ein Replikatset gestartet und initialisiert hat.
Ich mache die ganze Prozedur in Docker. dh Erstellen und Authentifizieren eines Replikatsatzes. Ich habe jedoch ein anderes Python-Skript, in dem ich eine Verbindung zum Primärknoten des Replikatsatzes herstellen muss. Ich bekomme dort einen Fehler.

docker-compose.txt
Dockerfile.txt
und im Python-Skript versuche ich so etwas zu tun
for x in range(1, 4): client = MongoClient(host='node' + str(x), port=27017, username='admin', password='password') if client.is_primary: print('the client.address is: ' + str(client.address)) print(dbName) print(collectionName) break

Habe ich Schwierigkeiten damit, hat jemand eine Idee?

@patrickml Wenn ich Docker Compose nicht verwende, wie mache ich das mit Dockerfile?
Ich brauche 'cqlsh', um meine build_all.cql auszuführen. 'Cqlsh' ist jedoch nicht bereit ... muss 60 Sekunden warten, um bereit zu sein.

Katze Dockerfile

FROM store / datastax / dse- server: 5.1.8

USER root

RUN apt-get update
RUN apt-get install -y vim

ADD db-scripts-2.1.33.2-RFT-01.tar / docker / cms /
COPY entrypoint.sh /entrypoint.sh

WORKDIR /docker/cms/db-scripts-2.1.33.2/
RUN cqlsh -f build_all.cql

USER dse

=============

Schritt 8/9: RUN cqlsh -f build_all.cql
---> Läuft in 08c8a854ebf4
Verbindungsfehler: ('Verbindung zu keinem Server möglich', {'127.0.0.1': Fehler (111, "Verbindung zu [('127.0.0.1', 9042) versucht]. Letzter Fehler: Verbindung abgelehnt")})
Der Befehl '/ bin / sh -c cqlsh -f build_all.cql' hat einen Code ungleich Null zurückgegeben: 1

Benötigt = var-lib-libvirt.mount var-lib-libvirt-images-ram.mount

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen