web-dev-qa-db-de.com

Docker Compose Warten Sie auf Container X, bevor Sie Y starten

Ich benutze rabbitmq und ein einfaches Python-Sample von hier zusammen mit docker-compose. Mein Problem ist, dass ich warten muss, bis rabbitmq vollständig gestartet ist. Nach dem, was ich bisher gesucht habe, weiß ich nicht, wie ich mit Container x (in meinem Fall Worker) warten soll, bis y (rabbitmq) gestartet wird.

Ich habe diesen Blogpost gefunden, in dem er überprüft, ob der andere Host online ist. Ich fand auch diesen Docker-Befehl :

warte

Verwendung: docker wait CONTAINER [CONTAINER ...]

Blockieren Sie, bis ein Container stoppt, und drucken Sie dann den Beendigungscode.

Das Warten auf das Stoppen eines Containers ist möglicherweise nicht das, wonach ich suche. Ist es jedoch möglich, diesen Befehl in der docker-compose.yml zu verwenden? Meine bisherige Lösung besteht darin, einige Sekunden zu warten und den Port zu überprüfen. Ist dies der richtige Weg, um dies zu erreichen? Wenn ich nicht warte, erhalte ich eine Fehlermeldung.

docker-compose.yml

worker:
    build: myapp/.
    volumes:
    - myapp/.:/usr/src/app:ro

    links:
    - rabbitmq
rabbitmq:
    image: rabbitmq:3-management

Hallo Python-Beispiel (rabbit.py):

import pika
import time

import socket

pingcounter = 0
isreachable = False
while isreachable is False and pingcounter < 5:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        s.connect(('rabbitmq', 5672))
        isreachable = True
    except socket.error as e:
        time.sleep(2)
        pingcounter += 1
    s.close()

if isreachable:
    connection = pika.BlockingConnection(pika.ConnectionParameters(
            Host="rabbitmq"))
    channel = connection.channel()

    channel.queue_declare(queue='hello')

    channel.basic_publish(exchange='',
                          routing_key='hello',
                          body='Hello World!')
    print (" [x] Sent 'Hello World!'")
    connection.close()

Dockerfile für Arbeiter:

FROM python:2-onbuild
RUN ["pip", "install", "pika"]

CMD ["python","rabbit.py"]

Update November 2015 :

Ein Shell-Skript oder Warten in Ihrem Programm ist möglicherweise eine mögliche Lösung. Aber nachdem ich dieses Problem gesehen habe, suche ich nach einem Befehl oder einer Funktion von docker/docker-compose.

Sie erwähnen eine Lösung für die Durchführung eines Gesundheitschecks, die die beste Option sein kann. Eine offene TCP-Verbindung bedeutet nicht, dass Ihr Dienst bereit ist oder möglicherweise bereit bleibt. Außerdem muss ich meinen Eintrittspunkt in meiner Docker-Datei ändern.

Ich hoffe also auf eine Antwort mit Docker-Compose-On-Board-Befehlen, was hoffentlich der Fall sein wird, wenn sie dieses Problem lösen.

Update März 2016

Es gibt einen Vorschlag für die Bereitstellung einer integrierten Methode, um festzustellen, ob ein Container "lebt". So kann Docker-Compose vielleicht in naher Zukunft davon Gebrauch machen.

Update Juni 2016

Es scheint, dass der Healthcheck in Docker in Version 1.12.0 integriert wird

Update Januar 2017

Ich habe eine Docker-Compose-Lösung gefunden. Siehe: Docker Compose warte auf Container X, bevor Y gestartet wird.

198
svenhornberg

Endlich eine Lösung mit einer Docker-Compose-Methode gefunden. Seit dem Docker-Compose-Dateiformat 2.1 können Sie healthchecks definieren. 

Ich habe es in einem Beispielprojekt .__ getan. Sie müssen mindestens Docker 1.12.0 + ..__ installieren. Ich musste auch Erweiterung der rabbitmq-Management Dockerfile , weil curl ist nicht auf dem offiziellen Image installiert.

Jetzt teste ich, ob die Verwaltungsseite des rabbitmq-Containers verfügbar ist. Wenn curl mit Exitcode 0 endet, wird die Container-App (Python Pika) gestartet und eine Nachricht in der Warteschlange „Hallo“ veröffentlicht. Es funktioniert jetzt (Ausgabe).

docker-compose (Version 2.1):

version: '2.1'

services:
  app:
    build: app/.
    depends_on:
      rabbit:
        condition: service_healthy
    links: 
        - rabbit

  rabbit:
    build: rabbitmq/.
    ports: 
        - "15672:15672"
        - "5672:5672"
    healthcheck:
        test: ["CMD", "curl", "-f", "http://localhost:15672"]
        interval: 30s
        timeout: 10s
        retries: 5

Ausgabe:

rabbit_1  | =INFO REPORT==== 25-Jan-2017::14:44:21 ===
rabbit_1  | closing AMQP connection <0.718.0> (172.18.0.3:36590 -> 172.18.0.2:5672)
app_1     |  [x] Sent 'Hello World!'
healthcheckcompose_app_1 exited with code 0

Dockerfile (rabbitmq + curl):

FROM rabbitmq:3-management
RUN apt-get update
RUN apt-get install -y curl 
EXPOSE 4369 5671 5672 25672 15671 15672

Version 3 unterstützt nicht mehr die Bedingungsform von abhängig_on . Also wechselte ich von depend_on, um On-Failure neu zu starten. Jetzt wird mein App-Container 2-3 Mal neu gestartet, bis er funktioniert, aber es ist immer noch eine Docker-Compose-Funktion, ohne den Einstiegspunkt zu überschreiben. 

docker-compose (Version 3):

version: "3"

services:

  rabbitmq: # login guest:guest
    image: rabbitmq:management
    ports:
    - "4369:4369"
    - "5671:5671"
    - "5672:5672"
    - "25672:25672"
    - "15671:15671"
    - "15672:15672"
    healthcheck:
        test: ["CMD", "curl", "-f", "http://localhost:15672"]
        interval: 30s
        timeout: 10s
        retries: 5

  app:
    build: ./app/
    environment:
      - HOSTNAMERABBIT=rabbitmq
    restart: on-failure
    depends_on:
      - rabbitmq
    links: 
        - rabbitmq
173
svenhornberg

Natürlich ist das noch nicht möglich. Siehe auch diese Funktionsanfrage .

Bisher müssen Sie das in Ihren Containern CMD tun, um zu warten, bis alle erforderlichen Dienste vorhanden sind.

In der Variablen Dockerfiles CMD können Sie auf Ihr eigenes Startskript verweisen, das den Start des Container-Services beendet. Bevor Sie damit anfangen, warten Sie auf ein abhängiges wie:

Dockerfile

FROM python:2-onbuild
RUN ["pip", "install", "pika"]
ADD start.sh /start.sh
CMD ["/start.sh"]

start.sh

#!/bin/bash
while ! nc -z rabbitmq 5672; do sleep 3; done
python rabbit.py

Wahrscheinlich müssen Sie netcat auch in Ihrer Dockerfile installieren. Ich weiß nicht, was auf dem Python-Image vorinstalliert ist.

Es gibt einige Tools, die eine einfache Wartelogik für einfache TCP-Port-Prüfungen bieten:

Für komplexere Wartezeiten:

64
h3nrik

Die Verwendung von restart: unless-stopped oder restart: always kann dieses Problem lösen.

Wenn Worker container stoppt, wenn rabbitMQ nicht bereit ist, wird es neu gestartet, bis dies der Fall ist.

34
Toilal

Vor kurzem haben sie die depends_on-Funktion hinzugefügt.

Bearbeiten:

Ab Compose Version 2.1+ können Sie depends_on in Verbindung mit healthcheck verwenden, um dies zu erreichen: 

Aus den Dokumenten :

version: '2.1'
services:
  web:
    build: .
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
  redis:
    image: redis
  db:
    image: redis
    healthcheck:
      test: "exit 0"

Vor Version 2.1

Sie können zwar weiterhin depends_on verwenden, dies wirkt sich jedoch nur auf die Reihenfolge aus, in der Dienste gestartet werden, nicht wenn sie bereit sind, bevor der abhängige Dienst gestartet wird.

Es scheint mindestens Version 1.6.0 zu erfordern. 

Die Nutzung würde ungefähr so ​​aussehen:

version: '2'
services:
  web:
    build: .
    depends_on:
      - db
      - redis
  redis:
    image: redis
  db:
    image: postgres 

Aus den Dokumenten: 

Expressabhängigkeit zwischen Diensten, die zwei Auswirkungen hat:

  • docker-compose up startet Dienste in Abhängigkeitsreihenfolge. Im folgenden Beispiel werden db und redis vor dem Web gestartet.
  • docker-compose up SERVICE schließt automatisch die Abhängigkeiten von SERVICE ein. Im folgenden Beispiel erstellt und erstellt docker-compose up web auch db und redis.

Note: Soweit ich es verstehe, legt dies zwar die Reihenfolge fest, in der Container geladen werden. Es kann nicht garantiert werden, dass der Dienst im Container tatsächlich geladen wurde. 

Beispielsweise könnten Sie Container postgres up sein. Der Postgres-Dienst selbst wird jedoch möglicherweise noch im Container initialisiert. 

28
toast38coza

sie können es auch einfach zur Befehlsoption hinzufügen, z.

command: bash -c "sleep 5; start.sh"

https://github.com/docker/compose/issues/374#issuecomment-156546513

um auf einen Port zu warten, können Sie auch so etwas verwenden

command: bash -c "while ! curl -s rabbitmq:5672 > /dev/null; do echo waiting for xxx; sleep 3; done; start.sh"

um die Wartezeit zu erhöhen, können Sie ein wenig mehr hacken:

command: bash -c "for i in {1..100} ; do if ! curl -s rabbitmq:5672 > /dev/null ; then echo waiting on rabbitmq for $i seconds; sleep $i; fi; done; start.sh"
14
AmanicA

restart: on-failure hat den Trick für mich gemacht

---
version: '2.1'
services:
  consumer:
    image: golang:Alpine
    volumes:
      - ./:/go/src/srv-consumer
    working_dir: /go/src/srv-consumer
    environment:
      AMQP_DSN: "amqp://guest:[email protected]:5672"
    command: go run cmd/main.go
    links:
          - rabbitmq
    restart: on-failure

  rabbitmq:
    image: rabbitmq:3.7-management-Alpine
    ports:
      - "15672:15672"
      - "5672:5672"
6
Edwin Ikechukwu

Verwenden Sie für Container die Bestellung

depends_on:

Zum Warten auf den Start des vorherigen Containers verwenden Sie das Skript

entrypoint: ./wait-for-it.sh db:5432

Dieser Artikel hilft Ihnen dabei https://docs.docker.com/compose/startup-order/

6
quit

Sie können dieses Problem auch lösen, indem Sie einen Endpunkt festlegen, der mit netcat (mit dem Skript docker-wait ) wartet, bis der Dienst in Betrieb ist. Ich mag diesen Ansatz, da Sie noch einen sauberen command-Abschnitt in Ihrem docker-compose.yml haben und Sie Ihrer Anwendung keinen docker-spezifischen Code hinzufügen müssen:

version: '2'
services:
  db:
    image: postgres
  Django:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    entrypoint: ./docker-entrypoint.sh db 5432
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

Dann dein docker-entrypoint.sh:

#!/bin/sh

postgres_Host=$1
postgres_port=$2
shift 2
cmd="[email protected]"

# wait for the postgres docker to be running
while ! nc $postgres_Host $postgres_port; do
  >&2 echo "Postgres is unavailable - sleeping"
  sleep 1
done

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

# run the command
exec $cmd

Dies ist heutzutage in der offiziellen docker-Dokumentation dokumentiert.

PS: Sie sollten netcat in Ihrer Docker-Instanz installieren, falls diese nicht verfügbar ist. Fügen Sie dies Ihrer Docker-Datei hinzu:

RUN apt-get update && apt-get install netcat-openbsd -y 
5
Martijn Jacobs

Es gibt ein gebrauchsfertiges Dienstprogramm namens " docker-wait ", das zum Warten verwendet werden kann.

4
Adrian Mitev

In Version 3 einer Docker Compose-Datei können Sie RESTART verwenden.

Zum Beispiel:

docker-compose.yml

worker:
    build: myapp/.
    volumes:
    - myapp/.:/usr/src/app:ro
    restart: on-failure
    depends_on:
    - rabbitmq
rabbitmq:
    image: rabbitmq:3-management

Beachten Sie, dass ich depend_on anstelle von links verwendet habe, da letzteres in Version 3 veraltet ist.

Auch wenn es funktioniert, ist es möglicherweise nicht die ideale Lösung, da Sie den Docker-Container bei jedem Fehler neu starten.

Schauen Sie sich auch RESTART_POLICY an. Damit können Sie die Neustart-Richtlinie optimieren.

Wenn Sie in der Produktion Compose verwenden , empfiehlt es sich, die Neustartrichtlinie zu verwenden:

Festlegen einer Neustartrichtlinie wie "Neustart": Immer, um Ausfallzeiten zu vermeiden

1
Mathieu Gemard

basierend auf diesem Blogeintrag https://8thlight.com/blog/dariusz-pasciak/2016/10/17/docker-compose-wait-for-dependencies.html

Ich habe meinen docker-compose.yml wie folgt konfiguriert:

version: "3.1"

services:
  rabbitmq:
    image: rabbitmq:3.7.2-management-Alpine
    restart: always
    environment:
      RABBITMQ_HIPE_COMPILE: 1
      RABBITMQ_MANAGEMENT: 1
      RABBITMQ_VM_MEMORY_HIGH_WATERMARK: 0.2
      RABBITMQ_DEFAULT_USER: "rabbitmq"
      RABBITMQ_DEFAULT_PASS: "rabbitmq"
    ports:
      - "15672:15672"
      - "5672:5672"
    volumes:
      - data:/var/lib/rabbitmq:rw

  start_dependencies:
    image: Alpine:latest
    links:
      - rabbitmq
    command: >
      /bin/sh -c "
        echo Waiting for rabbitmq service start...;
        while ! nc -z rabbitmq 5672;
        do
          sleep 1;
        done;
        echo Connected!;
      "

volumes:
  data: {}

Dann mache ich für run =>:

docker-compose up start_dependencies

Der Dienst rabbitmq wird im Daemon-Modus gestartet, start_dependencies beendet die Arbeit.

1
Igor Komar

Nicht empfohlen für ernsthafte Bereitstellungen, aber hier ist im Wesentlichen ein "wait x seconds" -Befehl.

Mit docker-compose version 3.4 wurde eine start_period ANWEISUNG ZU healthcheck hinzugefügt. Dies bedeutet, dass wir Folgendes tun können:

docker-compose.yml:

version: "3.4"
services:
  # your server docker container
  zmq_server:
    build:
      context: ./server_router_router
      dockerfile: Dockerfile

  # container that has to wait
  zmq_client:
    build:
      context: ./client_dealer/
      dockerfile: Dockerfile
    depends_on:
      - zmq_server
    healthcheck:
      test: "sh status.sh"
      start_period: 5s

status.sh:

#!/bin/sh

exit 0

Was hier passiert ist, dass die healthcheck nach 5 Sekunden aufgerufen wird. Dies ruft das status.sh-Skript auf, das immer "Kein Problem" zurückgibt. Wir haben gerade zmq_client container 5 Sekunden warten lassen, bevor es losgeht!

Hinweis: Es ist wichtig, dass Sie version: "3.4" haben. Wenn der .4 nicht vorhanden ist, beschwert sich Docker-Compose.

0
NumesSanguis

Eine der alternativen Lösungen ist die Verwendung einer Container-Orchestrierungslösung wie Kubernetes. Kubernetes unterstützt Init-Container, die vollständig ausgeführt werden, bevor andere Container gestartet werden können. Ein Beispiel finden Sie hier mit dem SQL Server 2017-Linux-Container, in dem der API-Container zum Initialisieren einer Datenbank einen Init-Container verwendet 

https://www.handsonarchitect.com/2018/08/understand-kubernetes-object-init.html

0
Nilesh Gule

Hier ist das Beispiel, in dem main container auf worker wartet, wenn er auf Pings antwortet:

version: '3'
services:
  main:
    image: bash
    depends_on:
     - worker
    command: bash -c "sleep 2 && until ping -qc1 worker; do sleep 1; done &>/dev/null"
    networks:
      intra:
        ipv4_address: 172.10.0.254
  worker:
    image: bash
    hostname: test01
    command: bash -c "ip route && sleep 10"
    networks:
      intra:
        ipv4_address: 172.10.0.11
networks:
  intra:
    driver: bridge
    ipam:
      config:
      - subnet: 172.10.0.0/24

Die richtige Methode ist jedoch die Verwendung von healthcheck (> = 2.1).

0
kenorb

Versuchte viele verschiedene Arten, aber mochte die Einfachheit dieses: https://github.com/ufoscout/docker-compose-wait

Die Idee, dass Sie ENV-Variablen in der Docker-Compose-Datei verwenden können, um eine Liste von Service-Hosts (mit Ports) zu senden, die wie folgt "erwartet" werden sollten: WAIT_HOSTS: postgres:5432, mysql:3306, mongo:27017.

Nehmen wir also an, Sie haben die folgende docker-compose.yml-Datei (kopieren/einfügen von repo README ):

version: "3"

services:

  mongo:
    image: mongo:3.4
    hostname: mongo
    ports:
      - "27017:27017"

  postgres:
    image: "postgres:9.4"
    hostname: postgres
    ports:
      - "5432:5432"

  mysql:
    image: "mysql:5.7"
    hostname: mysql
    ports:
      - "3306:3306"

  mySuperApp:
    image: "mySuperApp:latest"
    hostname: mySuperApp
    environment:
      WAIT_HOSTS: postgres:5432, mysql:3306, mongo:27017

Damit die Dienste warten können, müssen Sie als Nächstes die folgenden zwei Zeilen zu Ihren Dockerdateien hinzufügen (in Dockerdatei der Dienste, die auf den Start anderer Dienste warten sollen):

ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.5.0/wait /wait
RUN chmod +x /wait

Das vollständige Beispiel für ein solches Dockerfile (ebenfalls aus dem Projektrepo README ):

FROM Alpine

## Add your application to the docker image
ADD MySuperApp.sh /MySuperApp.sh

## Add the wait script to the image
ADD https://github.com/ufoscout/docker-compose-wait/releases/download/2.5.0/wait /wait
RUN chmod +x /wait

## Launch the wait tool and then your application
CMD /wait && /MySuperApp.sh

Für weitere Details zur möglichen Verwendung siehe LIESMICH

0
Evereq