При запуске службы внутри контейнера, скажем, mongodb, команда
docker run -d myimage
мгновенно выйдет и вернет идентификатор контейнера. В моем сценарии CI я запускаю клиент для проверки соединения mongodb сразу после запуска контейнера mongo. Проблема в том, что клиент не может подключиться, потому что служба еще не запущена. sleep 10
Я не вижу возможности ждать, пока контейнер заработает, кроме добавления большого значения в свой скрипт.
В Docker есть команда, wait
которая в этом случае не работает, потому что контейнера не существует. Это ограничение докера?
Ответы:
Как прокомментировано в аналогичной проблеме для докера 1.12
Это доступно с docker 1.12rc3 (2016-07-14)
docker-compose
находится в процессе поддержки функции ожидания определенных условий.Вы можете использовать его в Dockerfile следующим образом:
HEALTHCHECK --interval=5m --timeout=3s \ CMD curl -f http://localhost/ || exit 1
Официальные документы: https://docs.docker.com/engine/reference/builder/#/healthcheck
источник
Нашел это простое решение, искал что-то получше, но не повезло ...
until [ "`/usr/bin/docker inspect -f {{.State.Running}} CONTAINERNAME`"=="true" ]; do sleep 0.1; done;
или если вы хотите подождать, пока контейнер не сообщит о работоспособности (при условии, что у вас есть проверка работоспособности)
until [ "`/usr/bin/docker inspect -f {{.State.Health.Status}} CONTAINERNAME`"=="healthy" ]; do sleep 0.1; done;
источник
while [ "`docker inspect -f {{.State.Health.Status}} $container_id`" != "healthy" ]; do sleep 2; done
/usr/bin/docker inspect -f {{.State.Running}} local_mysql
== true $ do sleep 0.1; сделанный; echo "mysql is on"Если вы не хотите открывать порты, как в случае, если вы планируете связать контейнер и можете запускать несколько экземпляров для тестирования, я обнаружил, что это хороший способ сделать это в одной строке :) Этот пример на основе ожидания готовности ElasticSearch:
docker inspect --format '{{ .NetworkSettings.IPAddress }}:9200' elasticsearch | xargs wget --retry-connrefused --tries=5 -q --wait=3 --spider
Для этого требуется, чтобы wget был доступен, что является стандартным для Ubuntu. Он будет повторять 5 раз, 3 секунды между попытками, даже если в соединении отказано, а также ничего не загружает.
источник
--waitretry=3
вместо--wait=3
--wait=seconds Wait the specified number of seconds between the retrievals.
и--waitretry=seconds If you don't want Wget to wait between every retrieval, but only between retries of failed downloads, you can use this option. Wget will use linear backoff, waiting 1 second after the first failure on a given file, then waiting 2 seconds after the second failure on that file, up to the maximum number of seconds you specify.
Если запущенная вами контейнерная служба не обязательно хорошо реагирует на запросы curl или wget (что весьма вероятно для многих служб), вы можете использовать
nc
вместо этого.Вот фрагмент сценария хоста, который запускает контейнер Postgres и ожидает его доступности, прежде чем продолжить:
POSTGRES_CONTAINER=`docker run -d --name postgres postgres:9.3` # Wait for the postgres port to be available until nc -z $(sudo docker inspect --format='{{.NetworkSettings.IPAddress}}' $POSTGRES_CONTAINER) 5432 do echo "waiting for postgres container..." sleep 0.5 done
Изменить - в этом примере не требуется, чтобы вы открывали порт, который вы тестируете, поскольку он получает доступ к назначенному Docker «частному» IP-адресу для контейнера. Однако это работает только в том случае, если демон хоста докеров прослушивает loopback (127.xxx). Если (например) вы работаете на Mac и запускаете виртуальную машину boot2docker, вы не сможете использовать этот метод, так как вы не можете выполнить маршрутизацию на «частные» IP-адреса контейнеров из оболочки Mac.
источник
--format
после этого ответа вариант изменился; сейчас работаетdocker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' [NAME|ID...]
(см. пример на docs.docker.com/engine/reference/commandline/inspect ).Предполагая, что вы знаете хост + порт вашего сервера MongoDB (либо потому, что вы использовали
-link
, либо потому, что вы их внедрили-e
), вы можете просто использовать,curl
чтобы проверить, работает ли сервер MongoDB и принимает ли соединения.Следующий фрагмент будет пытаться подключиться каждую секунду, пока не добьется успеха:
#!/bin/sh while ! curl http://$DB_PORT_27017_TCP_ADDR:$DB_PORT_27017_TCP_PORT/ do echo "$(date) - still trying" sleep 1 done echo "$(date) - connected successfully"
источник
IP=$(docker inspect -f '{{ .NetworkSettings.IPAddress }}' mysql)
чтобы получить IP - адрес вашего тузд контейнера (где «MySQL» это имя или идентификатор контейнера) и заменить URL с:http://$IP:3306
. работает на меня!У меня получилось что-то вроде:
#!/bin/bash attempt=0 while [ $attempt -le 59 ]; do attempt=$(( $attempt + 1 )) echo "Waiting for server to be up (attempt: $attempt)..." result=$(docker logs mongo) if grep -q 'waiting for connections on port 27017' <<< $result ; then echo "Mongodb is up!" break fi sleep 2 done
источник
Выбросил свое собственное решение:
Я использую докерные сети, так что трюк Марка с netcat у меня не сработал (нет доступа из сети хоста), а идея Эрика не работает для контейнера postgres (контейнер отмечен как работающий, хотя postgres еще не доступны для подключения). Итак, я просто пытаюсь подключиться к postgres через эфемерный контейнер в цикле:
#!/bin/bash docker network create my-network docker run -d \ --name postgres \ --net my-network \ -e POSTGRES_USER=myuser \ postgres # wait for the database to come up until docker run --rm --net my-network postgres psql -h postgres -U myuser; do echo "Waiting for postgres container..." sleep 0.5 done # do stuff with the database...
источник
postgres
host не разрешается, поэтому я используюdocker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' postgres
вместо него. 2.psql
нужен пароль,pg_isready
лучше подходит. 3. Приpg_isready
этом тоже нет необходимости-U myuser
.test/test_runner
#!/usr/bin/env ruby $stdout.sync = true def wait_ready(port) until (`netstat -ant | grep #{port}`; $?.success?) do sleep 1 print '.' end end print 'Running supervisord' system '/usr/bin/supervisord' wait_ready(3000) puts "It's ready :)"
$ docker run -v /tmp/mnt:/mnt myimage ruby mnt/test/test_runner
Я вот так тестирую, слушает порт или нет. В этом случае у меня есть тест, запущенный изнутри контейнера, но это также возможно извне, независимо от того, готов mongodb или нет.
$ docker run -p 37017:27017 -d myimage
И проверьте, прослушивает ли порт 37017 хост-контейнер или нет.
источник
Мне пришлось решить эту проблему недавно, и в голову пришла идея. Изучая эту задачу, я попал сюда, поэтому решил поделиться своим решением с будущими посетителями этого поста.
Решение на основе Docker-compose
Если вы используете docker-compose, вы можете проверить мой POC синхронизации докеров . Я объединил некоторые идеи в других вопросах (спасибо за это - проголосовали за).
Основная идея состоит в том, что каждый контейнер в композите предоставляет диагностический сервис. Вызов этой службы проверяет, открыт ли требуемый набор портов в контейнере, и возвращает общий статус контейнера (WARMUP / RUNNING согласно POC). В каждом контейнере также есть утилита для проверки при запуске, запущены ли зависимые службы. Только после этого контейнер запускается.
В примере среды docker-compose есть две службы server1 и server2, и клиентская служба, которая ожидает запуска обоих серверов, отправляет запрос им обоим и завершает работу.
Выдержка из POC
wait_for_server.sh
#!/bin/bash server_host=$1 sleep_seconds=5 while true; do echo -n "Checking $server_host status... " output=$(echo "" | nc $server_host 7070) if [ "$output" == "RUNNING" ] then echo "$server_host is running and ready to process requests." break fi echo "$server_host is warming up. Trying again in $sleep_seconds seconds..." sleep $sleep_seconds done
Ожидание нескольких контейнеров:
trap 'kill $(jobs -p)' EXIT for server in $DEPENDS_ON do /assets/wait_for_server.sh $server & wait $! done
Базовая реализация диагностического сервиса ( checkports.sh ):
#!/bin/bash for port in $SERVER_PORT; do nc -z localhost $port; rc=$? if [[ $rc != 0 ]]; then echo "WARMUP"; exit; fi done echo "RUNNING";
Подключение диагностической службы к порту:
nc -v -lk -p 7070 -e /assets/checkports.sh
источник
Вы можете использовать wait-for-it " чистый сценарий bash, который будет ожидать доступности хоста и TCP-порта. Он полезен для синхронизации развертывания взаимозависимых служб, таких как связанные контейнеры докеров. Поскольку это чистый сценарий bash, он не имеет внешних зависимостей ».
Однако вы должны попытаться разработать свои сервисы так, чтобы избежать подобных взаимозависимостей между сервисами. Может ли ваш сервис попытаться переподключиться к базе данных? Можете ли вы позволить своему контейнеру просто умереть, если он не может подключиться к базе данных, и позволить оркестратору контейнеров (например, Docker Swarm) сделать это за вас?
источник
Чтобы проверить, запущен ли контейнер Docker PostgreSQL или MySQL (в настоящее время) (особенно для инструментов миграции, таких как Flyway), вы можете использовать двоичный файл ожидания : https://github.com/ArcanjoQueiroz/wait-for .
источник
Docker-compose решение
После docker-compose я не знаю имя контейнера докеров, поэтому использую
docker inspect -f {{.State.Running}} $(docker-compose ps -q <CONTAINER_NAME>)
и проверяя,
true
как здесь, https://stackoverflow.com/a/33520390/7438079источник
Для экземпляра докера mongoDB мы сделали это и работает как шарм:
#!/usr/bin/env bash until docker exec -i ${MONGO_IMAGE_NAME} mongo -u ${MONGO_INITDB_ROOT_USERNAME} -p ${MONGO_INITDB_ROOT_PASSWORD}<<EOF exit EOF do echo "Waiting for Mongo to start..." sleep 0.5 done
источник