Al ejecutar un servicio dentro de un contenedor, digamos mongodb, el comando
docker run -d myimage
saldrá instantáneamente y devolverá la identificación del contenedor. En mi script de CI, ejecuto un cliente para probar la conexión mongodb, justo después de ejecutar el contenedor mongo. El problema es: el cliente no puede conectarse porque el servicio aún no está activo. Aparte de agregar un gran sleep 10
en mi script, no veo ninguna opción para esperar a que un contenedor esté en funcionamiento.
Docker tiene un comando wait
que no funciona en ese caso, porque el contenedor no existe. ¿Es una limitación de Docker?
Encontré esta solución simple, he estado buscando algo mejor pero sin suerte ...
until [ "`/usr/bin/docker inspect -f {{.State.Running}} CONTAINERNAME`"=="true" ]; do sleep 0.1; done;
o si desea esperar hasta que el contenedor se informe como saludable (suponiendo que tenga una verificación de estado)
until [ "`/usr/bin/docker inspect -f {{.State.Health.Status}} CONTAINERNAME`"=="healthy" ]; do sleep 0.1; done;
fuente
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; hecho; echo "mysql estáSi no desea exponer los puertos, como es el caso si planea vincular el contenedor y podría estar ejecutando varias instancias para realizar pruebas, descubrí que esta era una buena manera de hacerlo en una línea :) Este ejemplo es basado en esperar a que ElasticSearch esté listo:
docker inspect --format '{{ .NetworkSettings.IPAddress }}:9200' elasticsearch | xargs wget --retry-connrefused --tries=5 -q --wait=3 --spider
Esto requiere que wget esté disponible, que es estándar en Ubuntu. Reintentará 5 veces, 3 segundos entre intentos, incluso si se rechaza la conexión, y tampoco descarga nada.
fuente
--waitretry=3
lugar de--wait=3
--wait=seconds Wait the specified number of seconds between the retrievals.
y--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.
Si el servicio en contenedor que inició no necesariamente responde bien a las solicitudes curl o wget (lo cual es muy probable para muchos servicios), entonces puede usarlo
nc
.Aquí hay un fragmento de un script de host que inicia un contenedor de Postgres y espera a que esté disponible antes de continuar:
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
Editar : este ejemplo no requiere que EXPONGA el puerto que está probando, ya que accede a la dirección IP 'privada' asignada por Docker para el contenedor. Sin embargo, esto solo funciona si el daemon del host de la ventana acoplable está escuchando en el loopback (127.xxx). Si (por ejemplo) está en una Mac y está ejecutando la máquina virtual boot2docker, no podrá utilizar este método ya que no puede enrutar a las direcciones IP 'privadas' de los contenedores desde su shell de Mac.
fuente
--format
opción ha cambiado desde esta respuesta; lo que funciona ahora esdocker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' [NAME|ID...]
(ver el ejemplo en docs.docker.com/engine/reference/commandline/inspect ).Suponiendo que conoce el puerto host + de su servidor MongoDB (ya sea porque usó un
-link
o porque los inyectó-e
), puede usarlocurl
para verificar si el servidor MongoDB se está ejecutando y acepta conexiones.El siguiente fragmento intentará conectarse cada segundo, hasta que tenga éxito:
#!/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"
fuente
IP=$(docker inspect -f '{{ .NetworkSettings.IPAddress }}' mysql)
para obtener la dirección IP de su contenedor de MySQL (donde "mysql" es el nombre o contenedor id) y vuelva a colocar la url con:http://$IP:3306
. ¡funciona para mi!Terminé con algo como:
#!/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
fuente
Lanzando mi propia solución por ahí:
Estoy usando redes docker, por lo que el truco de netcat de Mark no funcionó para mí (no hay acceso desde la red de host), y la idea de Erik no funciona para un contenedor de postgres (el contenedor está marcado como en ejecución aunque postgres aún no disponible para conectarse). Así que solo intento conectarme a postgres a través de un contenedor efímero en un bucle:
#!/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...
fuente
postgres
host no se resuelve, así que utilizodocker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' postgres
en su lugar. 2.psql
necesita una contraseña,pg_isready
se adapta mejor. 3. Conpg_isready
tampoco hay necesidad de entrar-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
Estoy probando así si el puerto está escuchando o no. En este caso, tengo una prueba que se ejecuta desde el interior del contenedor, pero también es posible desde el exterior si mongodb está listo o no.
$ docker run -p 37017:27017 -d myimage
Y verifique si el puerto 37017 está escuchando o no desde el contenedor del host.
fuente
Tuve que abordar esto recientemente y se me ocurrió una idea. Al hacer una investigación para esta tarea, llegué aquí, así que pensé en compartir mi solución con los futuros visitantes de esta publicación.
Solución basada en Docker compose
Si está utilizando docker-compose, puede consultar mi POC de sincronización de docker . Combiné algunas de las ideas en otras preguntas (gracias por eso, voté a favor).
La idea básica es que cada contenedor del compuesto expone un servicio de diagnóstico. Llamar a este servicio verifica si el conjunto de puertos requerido está abierto en el contenedor y devuelve el estado general del contenedor (WARMUP / RUNNING según el POC). Cada contenedor también tiene una utilidad para verificar al inicio si los servicios dependientes están en funcionamiento. Solo entonces se pone en marcha el contenedor.
En el entorno docker-compose de ejemplo, hay dos servicios server1 y server2 y el servicio de cliente que espera que ambos servidores se inicien y luego envía una solicitud a ambos y sale.
Extracto del 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
Esperando varios contenedores:
trap 'kill $(jobs -p)' EXIT for server in $DEPENDS_ON do /assets/wait_for_server.sh $server & wait $! done
Implementación básica del servicio de diagnóstico ( 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";
Cableado del servicio de diagnóstico a un puerto:
nc -v -lk -p 7070 -e /assets/checkports.sh
fuente
Puede usar wait-for-it ", un script bash puro que esperará la disponibilidad de un host y un puerto TCP. Es útil para sincronizar la puesta en marcha de servicios interdependientes, como contenedores acoplados de Docker. Dado que es un script bash puro, no tiene dependencias externas ".
Sin embargo, debe intentar diseñar sus servicios para evitar este tipo de interdependencias entre servicios. ¿Puede su servicio intentar volver a conectarse a la base de datos? ¿Puede dejar que su contenedor muera si no puede conectarse a la base de datos y dejar que un orquestador de contenedores (por ejemplo, Docker Swarm) lo haga por usted?
fuente
Para verificar si un contenedor Docker de PostgreSQL o MySQL (actualmente) está en funcionamiento (especialmente para herramientas de migración como Flyway), puede usar el binario de espera : https://github.com/ArcanjoQueiroz/wait-for .
fuente
Solución Docker-compose
Después de docker-compose, no sé el nombre del contenedor de la ventana acoplable, así que uso
docker inspect -f {{.State.Running}} $(docker-compose ps -q <CONTAINER_NAME>)
y comprobando
true
como aquí https://stackoverflow.com/a/33520390/7438079fuente
Para la instancia de la ventana acoplable mongoDB, hicimos esto y funciona como un encanto:
#!/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
fuente