Podczas uruchamiania usługi w kontenerze, powiedzmy mongodb, polecenie
docker run -d myimage
wyjdzie natychmiast i zwróci identyfikator kontenera. W moim skrypcie CI uruchamiam klienta w celu przetestowania połączenia mongodb zaraz po uruchomieniu kontenera mongo. Problem polega na tym, że klient nie może się połączyć, ponieważ usługa jeszcze nie działa. Oprócz dodania dużego sleep 10
w moim skrypcie, nie widzę żadnej opcji czekania na uruchomienie kontenera.
Docker ma polecenie, wait
które w tym przypadku nie działa, ponieważ kontener nie istnieje. Czy to ograniczenie dockera?
Znalazłem to proste rozwiązanie, szukałem czegoś lepszego, ale bez powodzenia ...
until [ "`/usr/bin/docker inspect -f {{.State.Running}} CONTAINERNAME`"=="true" ]; do sleep 0.1; done;
lub jeśli chcesz poczekać, aż kontener zgłosi się jako zdrowy (zakładając, że masz kontrolę stanu)
until [ "`/usr/bin/docker inspect -f {{.State.Health.Status}} CONTAINERNAME`"=="healthy" ]; do sleep 0.1; done;
źródło
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; Gotowe; echo "mysql jest włączone"Jeśli nie chcesz ujawniać portów, jak ma to miejsce, jeśli planujesz połączyć kontener i możesz uruchamiać wiele instancji do testowania, stwierdziłem, że to dobry sposób na zrobienie tego w jednej linii :) Ten przykład to na podstawie oczekiwania na gotowość ElasticSearch:
docker inspect --format '{{ .NetworkSettings.IPAddress }}:9200' elasticsearch | xargs wget --retry-connrefused --tries=5 -q --wait=3 --spider
Wymaga to dostępności wget, co jest standardem w Ubuntu. Spróbuje ponownie 5 razy, 3 sekundy między próbami, nawet jeśli połączenie zostanie odrzucone, a także nie pobierze niczego.
źródło
--waitretry=3
zamiast--wait=3
--wait=seconds Wait the specified number of seconds between the retrievals.
i--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.
Jeśli uruchomiona usługa kontenerowa niekoniecznie dobrze reaguje na żądania curl lub wget (co jest całkiem prawdopodobne w przypadku wielu usług), możesz użyć
nc
zamiast tego.Oto fragment skryptu hosta, który uruchamia kontener Postgres i czeka, aż będzie dostępny, zanim przejdziesz dalej:
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
Edycja - ten przykład nie wymaga WYKRYWANIA testowanego portu, ponieważ uzyskuje on dostęp do przypisanego przez platformę Docker „prywatnego” adresu IP dla kontenera. Jednak działa to tylko wtedy, gdy demon hosta Dockera nasłuchuje na sprzężeniu zwrotnym (127.xxx). Jeśli (na przykład) jesteś na komputerze Mac i używasz maszyny wirtualnej boot2docker, nie będziesz w stanie użyć tej metody, ponieważ nie możesz przekierować do „prywatnych” adresów IP kontenerów z powłoki Maca.
źródło
--format
opcja uległa zmianie od czasu tej odpowiedzi; teraz działadocker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' [NAME|ID...]
(zobacz przykład na docs.docker.com/engine/reference/commandline/inspect ).Zakładając, że znasz port + host swojego serwera MongoDB (albo dlatego, że użyłeś a
-link
, albo dlatego, że je wstrzyknąłeś-e
), możesz po prostu użyćcurl
do sprawdzenia, czy serwer MongoDB działa i akceptuje połączenia.Poniższy fragment będzie próbował łączyć się co sekundę, aż się powiedzie:
#!/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"
źródło
IP=$(docker inspect -f '{{ .NetworkSettings.IPAddress }}' mysql)
aby uzyskać adres IP mysql pojemnika (gdzie „mysql” to nazwa lub pojemnik id) i zastąpić URL z:http://$IP:3306
. pracuje dla mnie!Skończyło się na czymś takim:
#!/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
źródło
Rzucam własne rozwiązanie:
Używam sieci dockerowych, więc sztuczka z netcatem Marka nie zadziałała (brak dostępu z sieci hosta), a pomysł Erika nie działa dla kontenera postgres (kontener jest oznaczony jako uruchomiony, mimo że postgres jeszcze nie jest dostępne do połączenia). Po prostu próbuję połączyć się z postgresem za pośrednictwem kontenera efemerycznego w pętli:
#!/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...
źródło
postgres
host nie zostaje rozwiązany, więc używamdocker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' postgres
zamiast tego. 2.psql
potrzebuje hasła,pg_isready
lepiej pasuje. 3.pg_isready
Nie ma też potrzeby w-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
Testuję w ten sposób, czy port nasłuchuje, czy nie. W tym przypadku mam test uruchomiony z wnętrza kontenera, ale jest też możliwe z zewnątrz, czy mongodb jest gotowy czy nie.
$ docker run -p 37017:27017 -d myimage
I sprawdź, czy port 37017 nasłuchuje z kontenera hosta.
źródło
Musiałem sobie z tym poradzić i wpadłem na pewien pomysł. Robiąc badania związane z tym zadaniem, dostałem tutaj, więc pomyślałem, że podzielę się moim rozwiązaniem z przyszłymi odwiedzającymi ten post.
Rozwiązanie oparte na Docker Compose
Jeśli używasz docker-compose, możesz sprawdzić mój POC synchronizacji docker . Część pomysłów połączyłem w innych pytaniach (dzięki za to - zaopracowano).
Podstawową ideą jest to, że każdy kontener w kompozycie udostępnia usługę diagnostyczną. Wywołanie tej usługi sprawdza, czy wymagany zestaw portów jest otwarty w kontenerze i zwraca ogólny stan kontenera (WARMUP / RUNNING zgodnie z POC). Każdy kontener ma również narzędzie do sprawdzania po uruchomieniu, czy usługi zależne działają. Dopiero wtedy kontener się uruchamia.
W przykładowym środowisku docker-compose są dwie usługi serwer1 i serwer2 oraz usługa klienta , która czeka na uruchomienie obu serwerów, a następnie wysyła żądanie do obu z nich i kończy pracę.
Wyciąg z 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
Czekam na wiele kontenerów:
trap 'kill $(jobs -p)' EXIT for server in $DEPENDS_ON do /assets/wait_for_server.sh $server & wait $! done
Podstawowa implementacja usługi diagnostycznej ( 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";
Podłączanie usługi diagnostycznej do portu:
nc -v -lk -p 7070 -e /assets/checkports.sh
źródło
Można użyć „ czekaj na to ”, czystego skryptu bash, który będzie czekał na dostępność hosta i portu TCP. Jest to przydatne do synchronizowania działania współzależnych usług, takich jak połączone kontenery docker. czysty skrypt bash, nie ma żadnych zewnętrznych zależności ”.
Należy jednak spróbować zaprojektować swoje usługi, aby uniknąć tego rodzaju współzależności między usługami. Czy Twoja usługa może spróbować ponownie połączyć się z bazą danych? Czy możesz pozwolić swojemu kontenerowi umrzeć, jeśli nie może połączyć się z bazą danych i pozwolić, aby koordynator kontenerów (np. Docker Swarm) zrobił to za Ciebie?
źródło
Aby sprawdzić, czy kontener Docker PostgreSQL lub MySQL (obecnie) działa i działa (szczególnie w przypadku narzędzi do migracji, takich jak Flyway), możesz użyć pliku binarnego czekającego na : https://github.com/ArcanjoQueiroz/wait-for .
źródło
Rozwiązanie Docker-Compose
Po docker-compose nie znam nazwy kontenera docker, więc używam
docker inspect -f {{.State.Running}} $(docker-compose ps -q <CONTAINER_NAME>)
i sprawdzając
true
jak tutaj https://stackoverflow.com/a/33520390/7438079źródło
W przypadku instancji dokera mongoDB zrobiliśmy to i działa jak marzenie:
#!/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
źródło