Docker Compose ждет контейнер X перед запуском Y

Я использую RabbitMQ и простой питон образец из здесь вместе с Докером-композом. Моя проблема в том, что мне нужно дождаться полного запуска rabbitmq. Из того, что я искал до сих пор, я не знаю, как ждать с контейнером x (в моем случае рабочий), пока не будет запущен y (rabbitmq).

Я нашел этот пост, где он проверяет, есть ли другой хост в сети. Я также нашел эту команду Docker:

Подождите

Использование: докер подождите КОНТЕЙНЕР [КОНТЕЙНЕР...]

Блокируйте, пока контейнер не остановится, затем напечатайте его код выхода.

Ожидание остановки контейнера, возможно, не то, что я ищу, но если это так, возможно ли использовать эту команду внутри docker-compose.yml? Мое решение пока состоит в том, чтобы подождать несколько секунд и проверить порт, но так ли это можно сделать? Если я не жду, я получаю ошибку.

докер-compose.yml

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

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

Пример приветствия Python (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 для работника:

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

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

Обновление ноябрь 2015:

Сценарий оболочки или ожидание внутри вашей программы, возможно, является возможным решением. Но после просмотра этой проблемы я ищу команду или функцию docker/docker-compose.

Они упоминают решение для проверки работоспособности, которое может быть лучшим вариантом. Открытое TCP-соединение не означает, что ваш сервис готов или может оставаться готовым. В дополнение к этому мне нужно изменить мою точку входа в моем файле Docker.

Поэтому я надеюсь получить ответ с командами docker-compose на борту, что, будем надеяться, произойдет, если они закончат эту проблему.

Обновление март 2016

Существует предложение по предоставлению встроенного способа определения, является ли контейнер "живым". Так что docker-compose может использовать его в ближайшем будущем.

Обновление июнь 2016

Похоже, что проверка работоспособности будет интегрирована в докер в версии 1.12.0

Обновление январь 2017

Я нашел решение docker-compose, см.: Docker Compose ждет контейнера X перед запуском Y

Ответ 1

Наконец-то нашел решение с помощью метода docker-compose. Начиная с формата файла docker-compose 2.1 вы можете определять проверки работоспособности.

Я сделал это в примере проекта, вам нужно установить как минимум докер 1.12. 0+. Мне также нужно было расширить Dockerfile с управлением rabbitmq, потому что curl не установлен на официальном образе.

Теперь я проверяю, доступна ли страница управления rabbitmq-контейнером. Если curl заканчивается с кодом выхода 0, приложение контейнера (python pika) будет запущено и опубликует сообщение в очередь приветствия. Его сейчас работает (выходной).

docker-compose (версия 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

выход:

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

Версия 3 больше не поддерживает форму условия depen_on. Таким образом, я перешел от зависит от перезапуска при сбое. Теперь мой контейнер приложения будет перезапускаться 2-3 раза, пока он не заработает, но это все еще функция создания docker без перезаписи точки входа.

docker-compose (версия 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

Ответ 2

Натурально это пока невозможно. См. Также этот запрос функции.

Пока вам нужно сделать это в своих контейнерах CMD, чтобы ждать, пока не появятся все необходимые службы.

В Dockerfile CMD вы можете обратиться к своему собственному запуску script, который завершает запуск вашего контейнерного сервиса. Прежде чем вы начнете, вы ждете зависящего, например:

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

Возможно, вам нужно установить netcat в свой Dockerfile. Я не знаю, что предварительно установлено на образ python.

Есть несколько инструментов, которые обеспечивают удобную логику ожидания для простых проверок TCP:

Для более сложных ожиданий:

  • goss - Объяснение .

Ответ 3

Использование restart: unless-stopped или restart: always может решить эту проблему.

Если рабочий container останавливается, когда rabbitMQ не готов, он будет перезагружен до тех пор, пока он не будет.

Ответ 4

Совсем недавно они добавили функцию depends_on.

Edit:

Как и в случае с версией 2.1+, вы можете использовать depends_on в сочетании с healthcheck для достижения этой цели:

Из документов:

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"

До версии 2.1

Вы все еще можете использовать depends_on, но это влияет только на порядок запуска служб - не если они готовы до запуска зависимой службы.

Кажется, требуется, по крайней мере, версия 1.6.0.

Использование будет выглядеть примерно так:

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

Из документов:

Экспресс-зависимость между службами, которая имеет два эффекта:

  • docker-compose up запустит службы в порядке зависимости. В следующем примере db и redis будут запущены перед веб-интерфейсом.
  • Docker-compose up SERVICE автоматически включит зависимости SERVICE. В следующем примере сборка докеры также создаст и запустит db и redis.

Примечание: Как я понимаю, хотя это и определяет порядок загрузки контейнеров. Это не гарантирует, что сервис внутри контейнера загружен.

Например, контейнер postgres может быть вставлен. Но служба postgres сама по себе может быть инициализирована внутри контейнера.

Ответ 5

вы также можете просто добавить его к параметру команды, например.

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

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

чтобы ждать на порту, вы также можете использовать что-то вроде этого

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

чтобы увеличить время ожидания, вы можете немного взломать:

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"

Ответ 6

Для заказа контейнера начните использовать

depends_on:

Для ожидания предыдущего запуска контейнера используйте скрипт

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

Эта статья поможет вам https://docs.docker.com/compose/startup-order/

Ответ 7

restart: on-failure сделал свое дело для меня.. см. ниже

---
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"

Ответ 8

Вы также можете решить эту проблему, установив конечную точку, которая ожидает запуска службы, используя netcat (используя сценарий docker-wait). Мне нравится такой подход, так как у вас все еще есть чистый command раздел в вашем docker-compose.yml и вам не нужно добавлять специфичный для docker код в ваше приложение:

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

Тогда ваш 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

В настоящее время это задокументировано в официальной документации докера.

PS: Вы должны установить netcat в свой экземпляр докера, если это не доступно. Для этого добавьте это в свой файл Docker:

RUN apt-get update && apt-get install netcat-openbsd -y 

Ответ 9

Существует готовая к использованию утилита под названием " docker-wait", которая может быть использована для ожидания.

Ответ 10

основываясь на этом сообщении в блоге https://8thlight.com/blog/dariusz-pasciak/2016/10/17/docker-compose-wait-for-dependencies.html

Я настроил свой docker-compose.yml как показано ниже:

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

Тогда я делаю для запуска =>:

docker-compose up start_dependencies

rabbitmq запустится в режиме демона, start_dependencies завершит работу.

Ответ 11

Пробовал много разных способов, но понравилась простота этого: https://github.com/ufoscout/docker-compose-wait

Идея в том, что вы можете использовать ENV-переменные в файле docker compose для отправки списка хостов служб (с портами), которые следует " WAIT_HOSTS: postgres:5432, mysql:3306, mongo:27017 ", например: WAIT_HOSTS: postgres:5432, mysql:3306, mongo:27017.

Допустим, у вас есть следующий файл docker-compose.yml (копия/история из репозитория 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

Затем, чтобы службы могли ждать, вам нужно добавить следующие две строки в ваши Dockerfiles (в Dockerfile служб, которые должны ожидать запуска других служб):

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

Полный пример такого примера Dockerfile (снова из репозитория проекта 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

Для получения дополнительной информации о возможном использовании см. README.

Ответ 12

Одним из альтернативных решений является использование решения для оркестровки контейнеров, такого как Kubernetes. В Kubernetes есть поддержка контейнеров init, которые выполняются до завершения, прежде чем другие контейнеры могут быть запущены. Вы можете найти пример здесь с контейнером SQL Server 2017 Linux, где контейнер API использует контейнер инициализации для инициализации базы данных

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

Ответ 13

Я не мог использовать этот подход, я на самом деле не могу использовать том в docker-compose, в обоих случаях я не работал:

s-flyway_1_d7a78571d25c | env: невозможно выполнить 'bash': нет такого файла или каталога docker_s-flyway_1_d7a78571d25c с кодом 127

Что мне здесь не хватает? Я немного растерялся об этом прямо сейчас.....

Ссылка на проект: https://github.com/jbrasileiro/introduction-example/blob/master/introduction-example-docker-flyway/src/docker/docker-compose.yml

Dockerfile

FROM dhoer/flyway:alpine
COPY ./sql/*.sql /flyway/sql
VOLUME ./wait-for-it.sh:/usr/local/bin/wait-for-it.sh
ADD ./wait-for-it.sh /
RUN chmod 777 /wait-for-it.sh 

версия докера: '3'

version: '3'
services:
  hello-world:
    image: ubuntu
    volumes:
      - ./wait-for-it.sh:/usr/local/bin/wait-for-it.sh
    command:
      - wait-for-it.sh
      - github.com:80
      - --
      - echo
      - "hello world"

версия докера: '2'

version: '2'
services:
  s-postgres:
    image: postgres:9.4-alpine
    hostname: host-postgres
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=postgres
    networks:
       - network-docker
  s-flyway:
    image: dhoer/flyway:alpine
    build: flyway/
    entrypoint: /wait-for-it.sh s-postgres 5432 --timeout=0 --strict
    command: -url=jdbc:postgresql://s-postgres:5432/postgres -baselineOnMigrate=true -schemas=public -user=postgres -password=postgres migrate
    networks:
       - network-docker
    depends_on:
      - s-postgres
networks:
  network-docker:
    driver: bridge

Ответ 14

Вот пример, когда main контейнер ожидает worker когда он начинает отвечать на запросы ping:

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

Тем не менее, правильный способ - использовать healthcheck (> = 2.1).

Ответ 15

Не рекомендуется для серьезных развертываний, но здесь, по сути, есть команда "подождите х секунд".

В версии 3.4 для docker-compose в start_period была добавлена инструкция healthcheck. Это означает, что мы можем сделать следующее:

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

Здесь происходит то, что healthcheck через 5 секунд. Это вызывает сценарий status.sh, который всегда возвращает "Нет проблем". Мы только что заставили контейнер zmq_client ждать 5 секунд перед запуском!

Примечание: важно, чтобы у вас была version: "3.4". Если .4 там нет, docker-compose жалуется.

Ответ 16

В версии 3 файла Docker Compose вы можете использовать RESTART.

Например:

докер-compose.yml

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

Обратите внимание, что я использовал зависимость__ вместо ссылок, поскольку последняя версия устарела в версии 3.

Несмотря на то, что это работает, это может быть не идеальным решением, поскольку вы перезапускаете контейнер докера при каждом сбое.

Посмотрите также на RESTART_POLICY. это позволит вам точно настроить политику перезапуска.