web-dev-qa-db-fra.com

Docker Compose attend le conteneur X avant de commencer Y

J'utilise rabbitmq et un exemple python simple extrait de ici Avec docker-compose. Mon problème est que je dois attendre que rabbitmq soit complètement démarré. D'après ce que j'ai cherché jusqu'à présent, je ne sais pas comment attendre avec le conteneur x (dans mon assistant social) jusqu'à ce que y (rabbitmq) soit démarré.

J'ai trouvé ceci blogpost où il vérifie si l'autre hôte est en ligne . J'ai aussi trouvé ceci commande docker :

attendre

Utilisation: docker wait CONTAINER [CONTAINER ...]

Bloquer jusqu'à ce qu'un conteneur s'arrête, puis imprimer son code de sortie.

Attendre que le conteneur s’arrête n’est peut-être pas ce que je cherche mais si. vérifiez le port, mais est-ce le moyen d'y parvenir? Si je n'attends pas, j'obtiens une erreur.

docker-compose.yml

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

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

échantillon python hello (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 for worker:

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

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

Mise à jour novembre 2015:

Un script shell ou une attente dans votre programme est peut-être une solution possible. Mais après avoir vu ceci Issue Je cherche une commande ou une fonctionnalité de docker/docker-compose lui-même.

Ils mentionnent une solution pour mettre en œuvre un bilan de santé, qui peut être la meilleure option. Une connexion TCP ouverte ne signifie pas que votre service est prêt ou peut rester prêt. En plus de cela, je dois changer mon point d’entrée dans mon fichier de docker.

J'espère donc une réponse avec les commandes intégrées de docker-compose, ce qui sera le cas si elles résolvent ce problème.

Mise à jour mars 2016

Il existe une proposition pour fournir un moyen intégré de déterminer si un conteneur est "vivant". Donc docker-compose peut peut-être l'utiliser dans un proche avenir.

Mise à jour juin 2016

Il semble que le bilan de santé sera intégré dans docker dans la version 1.12.0.

Mise à jour janvier 2017

J'ai trouvé une solution docker-compose, voir: Docker Compose attend le conteneur X avant de commencer Y

198
svenhornberg

Nativement, ce n'est pas encore possible. Voir aussi cette demande de fonctionnalité .

Jusqu'ici, vous devez le faire dans vos conteneurs CMD pour attendre que tous les services requis soient disponibles.

Dans Dockerfiles CMD, vous pouvez faire référence à votre propre script de démarrage qui encapsule le démarrage de votre service de conteneur. Avant de commencer, vous attendez un type dépendant tel que:

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

Vous devez probablement également installer netcat dans votre Dockerfile. Je ne sais pas ce qui est pré-installé sur l'image Python.

Il existe quelques outils qui fournissent une logique d’attente facile à utiliser, pour de simples vérifications de ports TCP:

Pour des attentes plus complexes:

64
h3nrik

Utiliser restart: unless-stopped ou restart: always peut résoudre ce problème.

Si l'ouvrier container s'arrête quand rabbitMQ n'est pas prêt, elle sera redémarrée jusqu'à ce qu'elle le soit.

34
Toilal

Tout récemment, ils ont ajouté la fonctionnalité depends_on .

Modifier:

À compter de la version 2.1+ de Compose, vous pouvez utiliser depends_on conjointement avec healthcheck pour atteindre cet objectif: 

À partir de la documentation :

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"

Avant la version 2.1

Vous pouvez toujours utiliser depends_on, mais cela n'affecte que les ordre dans lesquels les services sont démarrés - pas s'ils sont prêts avant le démarrage du service dépendant.

Il semble nécessiter au moins la version 1.6.0. 

L'utilisation ressemblerait à quelque chose comme ça:

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

De la docs: 

Dépendance express entre services, ce qui a deux effets:

  • docker-compos up va démarrer les services dans l’ordre des dépendances. Dans l'exemple suivant, db et redis seront démarrés avant Web.
  • docker-compos up up SERVICE inclura automatiquement les dépendances de SERVICE. Dans l'exemple suivant, docker-compose up web va également créer et démarrer db et redis.

Note: Si je comprends bien, ceci définit l’ordre dans lequel les conteneurs sont chargés. Cela ne garantit pas que le service à l'intérieur du conteneur a effectivement été chargé. 

Par exemple, votre postgres conteneur est peut-être en hausse. Mais le service postgres lui-même pourrait toujours être en cours d'initialisation dans le conteneur. 

28
toast38coza

vous pouvez aussi simplement l'ajouter à l'option de commande, par exemple.

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

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

attendre sur un port, vous pouvez aussi utiliser quelque chose comme ça

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

pour augmenter le temps d'attente, vous pouvez en pirater un peu plus:

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 a fait le tour pour moi ... voir ci-dessous

---
version: '2.1'
services:
  consumer:
    image: golang:Alpine
    volumes:
      - ./:/go/src/srv-consumer
    working_dir: /go/src/srv-consumer
    environment:
      AMQP_DSN: "amqp://guest:guest@rabbitmq: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

Pour commencer à commander utiliser

depends_on:

Pour attendre le conteneur précédent, commencez par utiliser le script

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

Cet article vous aidera https://docs.docker.com/compose/startup-order/

6
quit

Vous pouvez également résoudre ce problème en définissant un noeud final qui attend que le service soit opérationnel à l'aide de netcat (à l'aide du script docker-wait ). J'aime cette approche car vous avez toujours une section propre command dans votre docker-compose.yml et vous n'avez pas besoin d'ajouter de code spécifique à votre menu fixe à votre application:

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

Puis votre docker-entrypoint.sh:

#!/bin/sh

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

# 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

Ceci est de nos jours documenté dans la documentation officielle docker .

PS: Vous devriez installer netcat dans votre instance de menu fixe si ce n’est pas disponible. Pour ce faire, ajoutez ceci à votre fichier Docker:

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

Il existe un utilitaire prêt à utiliser appelé " docker-wait " qui peut être utilisé pour attendre.

4
Adrian Mitev

Dans la version 3 d'un fichier Docker Compose, vous pouvez utiliser RESTART .

Par exemple:

docker-compose.yml

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

Notez que j'ai utilisé depend_on au lieu de liens car ce dernier est déconseillé dans la version 3.

Même si cela fonctionne, il se peut que ce ne soit pas la solution idéale car vous redémarrez le conteneur de menu fixe à chaque échec.

Consultez RESTART_POLICY également. cela vous permet d'affiner la politique de redémarrage.

Lorsque vous tilisez Compose en production , il est préférable d'utiliser la règle de redémarrage:

Spécifier une politique de redémarrage comme redémarrage: toujours pour éviter les temps d'arrêt

1
Mathieu Gemard

en se basant sur ce blog https://8thlight.com/blog/dariusz-pasciak/2016/10/17/docker-compose-wait-for-dependencies.html

J'ai configuré mon docker-compose.yml comme indiqué ci-dessous:

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: {}

Alors je fais pour run =>:

docker-compose up start_dependencies

Le service rabbitmq démarre en mode démon, start_dependencies termine le travail.

1
Igor Komar

Non recommandé pour les déploiements sérieux, mais il s'agit essentiellement d'une commande "attendre x secondes".

Avec docker-compose version 3.4, une instruction start_period A ÉTÉ AJOUTÉE À healthcheck . Cela signifie que nous pouvons faire ce qui suit:

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

Qu'est-ce qui se passe ici est que la healthcheck est invoquée après 5 secondes. Ceci appelle le script status.sh, qui renvoie toujours "Pas de problème". Nous avons juste fait zmq_client conteneur attendre 5 secondes avant de commencer!

Remarque: Il est important que vous ayez version: "3.4". Si le .4 n'y est pas, docker-compose se plaint.

0
NumesSanguis

L'une des solutions alternatives consiste à utiliser une solution d'orchestration de conteneur telle que Kubernetes. Kubernetes prend en charge les conteneurs init qui s’achèvent avant que d’autres conteneurs ne puissent démarrer. Vous pouvez trouver ici un exemple avec le conteneur Linux SQL Server 2017 où le conteneur API utilise le conteneur init pour initialiser une base de données. 

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

0
Nilesh Gule

Voici l'exemple où main conteneur attend worker lorsqu'il commence à répondre aux pings:

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

Cependant, la méthode appropriée consiste à utiliser healthcheck (> = 2.1).

0
kenorb

Essayé de nombreuses façons différentes, mais a aimé la simplicité de ceci: https://github.com/ufoscout/docker-compose-wait

L'idée que vous pouvez utiliser les vars ENV dans le fichier de composition du menu fixe pour soumettre une liste d'hôtes de services (avec les ports) qui devrait être "attendue" comme ceci: WAIT_HOSTS: postgres:5432, mysql:3306, mongo:27017.

Supposons donc que vous ayez le fichier docker-compose.yml suivant (copier/coller du 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

Ensuite, pour que les services attendent, vous devez ajouter les deux lignes suivantes à vos fichiers Docker (dans le fichier Docker des services qui devraient attendre que d’autres services soient lancés):

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

L’exemple complet de cet exemple de fichier Dockerfile (à nouveau tiré du projet repo 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

Pour plus de détails sur les utilisations possibles, voir README

0
Evereq