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:
Для более сложных ожиданий:
Ответ 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. это позволит вам точно настроить политику перезапуска.