que - mysql en docker
¿Cómo puedo saber cuándo está activo el contenedor mysql de mi docker y mysql está listo para responder consultas? (9)
Algunas veces el problema con el puerto es que el puerto podría estar abierto , pero la base de datos aún no está lista.
Otras soluciones requieren que haya instalado mysql o mysql client en su máquina host, pero realmente ya lo tiene dentro del contenedor de Docker, por lo que prefiero usar algo como esto:
while ! docker exec mysql mysqladmin --user=root --password=root --host "127.0.0.1" ping --silent &> /dev/null ; do
echo "Waiting for database connection..."
sleep 2
done
Estoy desplegando algunos contenedores docker diferentes, siendo mysql el primero. Quiero ejecutar scripts tan pronto como la base de datos esté activa y proceder a crear otros contenedores. La secuencia de comandos ha fallado porque estaba intentando ejecutarse cuando la secuencia de comandos entrypoint, que configura mysql (desde este contenedor oficial de mysql ), aún estaba en ejecución.
sudo docker run --name mysql -e MYSQL_ROOT_PASSWORD=MY_ROOT_PASS -p 3306:3306 -d mysql
[..] wait for mysql to be ready [..]
mysql -h 127.0.0.1 -P 3306 -u root --password=MY_ROOT_PASS < MY_SQL_SCRIPT.sql
¿Hay alguna manera de esperar una señal de un script de configuración mysql entrypoiny que termine dentro del contenedor de la ventana acoplable? Bash sleep parece una solución subóptima.
EDITAR: Fui para un guión de bash como este. No es la fuerza bruta más elegante y un poco pero funciona como un encanto. Tal vez alguien lo encontrará útil.
OUTPUT="Can''t connect"
while [[ $OUTPUT == *"Can''t connect"* ]]
do
OUTPUT=$(mysql -h $APP_IP -P :$APP_PORT -u yyy --password=xxx < ./my_script.sql 2>&1)
done
El siguiente chequeo de salud funciona para todos mis contenedores mysql:
db:
image: mysql:5.7.16
healthcheck:
test: ["CMD-SHELL", ''mysql --database=$$MYSQL_DATABASE --password=$$MYSQL_ROOT_PASSWORD --execute="SELECT count(table_name) > 0 FROM information_schema.tables;" --skip-column-names -B'']
interval: 30s
timeout: 10s
retries: 4
extends:
file: docker-compose-common-config.yml
service: common_service
En su script ENTRYPOINT
, debe comprobar si tiene una conexión MySQL válida o no.
Esta solución no requiere que instale un cliente MySQL en el contenedor y, al ejecutar el contenedor con
php:7.0-fpm
ejecutarnc
no era una opción, ya que también tenía que instalarse. Además, verificar si el puerto está abierto no significa necesariamente que el servicio se esté ejecutando y esté expuesto correctamente. [más de esto]
Por lo tanto, en esta solución, le mostraré cómo ejecutar un script PHP para verificar si un contenedor MySQL puede conectarse. Si desea saber por qué creo que este es un mejor enfoque, consulte mi comentario here .
Archivo entrypoint.sh
#!/bin/bash
cat << EOF > /tmp/wait_for_mysql.php
<?php
/$connected = false;
while(!/$connected) {
try{
/$dbh = new pdo(
''mysql:host=mysql:3306;dbname=db_name'', ''db_user'', ''db_pass'',
array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
);
/$connected = true;
}
catch(PDOException /$ex){
error_log("Could not connect to MySQL");
error_log(/$ex->getMessage());
error_log("Waiting for MySQL Connection.");
sleep(5);
}
}
EOF
php /tmp/wait_for_mysql.php
# Rest of entry point bootstrapping
Al ejecutar esto, esencialmente está bloqueando cualquier lógica de arranque de su contenedor HASTA que tenga una conexión MySQL válida.
Este pequeño bucle de bash espera a que mysql esté abierto, no debería requerir la instalación de ningún paquete adicional:
until nc -z -v -w30 $CFG_MYSQL_HOST 3306
do
echo "Waiting for database connection..."
# wait for 5 seconds before check again
sleep 5
done
Esto fue más o menos mencionado en los comentarios a otras respuestas, pero creo que merece su propia entrada.
En primer lugar, puede ejecutar su contenedor de la siguiente manera:
docker run --name mysql --health-cmd=''mysqladmin ping --silent'' -d mysql
También hay un equivalent en el Dockerfile.
Con ese comando, su docker ps
y docker inspect
le mostrarán el estado de salud de su contenedor. En particular, para mysql, este método tiene la ventaja de que mysqladmin
está disponible dentro del contenedor , por lo que no es necesario instalarlo en el host de la ventana acoplable.
Luego puede simplemente hacer un bucle en un script de bash para esperar a que el estado se vuelva saludable. El siguiente script de bash es creado por Dennis .
function getContainerHealth {
docker inspect --format "{{json .State.Health.Status }}" $1
}
function waitContainer {
while STATUS=$(getContainerHealth $1); [ $STATUS != "/"healthy/"" ]; do
if [ $STATUS == "/"unhealthy/"" ]; then
echo "Failed!"
exit -1
fi
printf .
lf=$''/n''
sleep 1
done
printf "$lf"
}
Ahora puedes hacer esto en tu script:
waitContainer mysql
y su script esperará hasta que el contenedor esté en funcionamiento. La secuencia de comandos se cerrará si el contenedor se vuelve insalubre, lo que es posible si, por ejemplo, el host de la ventana acoplable se queda sin memoria, por lo que el mysql no puede asignar suficiente de sí mismo.
Puede instalar el paquete mysql-client y usar mysqladmin para hacer ping al servidor de destino. Útil cuando se trabaja con múltiples contenedores docker. Combina con el sueño y crea un simple circuito de espera:
while ! mysqladmin ping -h"$DB_HOST" --silent; do
sleep 1
done
Si el contenedor de la ventana acoplable que espera un contenedor de mysql se basa en una imagen de Python (por ejemplo, para una aplicación Django) , puede usar el siguiente código.
Las ventajas son:
- No se basa en wait-for.it.sh , que espera que la IP y el puerto de mysql estén listos, pero esto no significa automáticamente que la inicialización de mysql haya finalizado.
- No es un script de shell basado en un ejecutable mysql o mysqladmin que debe estar presente en su contenedor: dado que su contenedor se basa en una imagen de python, esto requeriría instalar mysql sobre esa imagen. Con la siguiente solución, utiliza la tecnología que ya está presente en el contenedor: python puro.
Código:
import time
import pymysql
def database_not_ready_yet(error, checking_interval_seconds):
print(''Database initialization has not yet finished. Retrying over {0} second(s). The encountered error was: {1}.''
.format(checking_interval_seconds,
repr(error)))
time.sleep(checking_interval_seconds)
def wait_for_database(host, port, db, user, password, checking_interval_seconds):
"""
Wait until the database is ready to handle connections.
This is necessary to ensure that the application docker container
only starts working after the MySQL database container has finished initializing.
More info: https://docs.docker.com/compose/startup-order/ and https://docs.docker.com/compose/compose-file/#depends_on .
"""
print(''Waiting until the database is ready to handle connections....'')
database_ready = False
while not database_ready:
db_connection = None
try:
db_connection = pymysql.connect(host=host,
port=port,
db=db,
user=user,
password=password,
charset=''utf8mb4'',
connect_timeout=5)
print(''Database connection made.'')
db_connection.ping()
print(''Database ping successful.'')
database_ready = True
print(''The database is ready for handling incoming connections.'')
except pymysql.err.OperationalError as err:
database_not_ready_yet(err, checking_interval_seconds)
except pymysql.err.MySQLError as err:
database_not_ready_yet(err, checking_interval_seconds)
except Exception as err:
database_not_ready_yet(err, checking_interval_seconds)
finally:
if db_connection is not None and db_connection.open:
db_connection.close()
Uso:
- Agregue este código en un archivo python (
wait-for-mysql-db.py
por ejemplo) dentro del código fuente de su aplicación. - Escribe otra secuencia de comandos de python (
startup.py
por ejemplo) que primero ejecute el código anterior y luego inicie la aplicación. - Asegúrese de que el Dockerfile del contenedor de la aplicación incluya estos dos scripts de Python junto con el código fuente de la aplicación en una imagen de Docker.
- En su archivo docker-compose, configure su contenedor de aplicaciones con:
command: ["python3", "startup.py"]
.
Tenga en cuenta que esta solución está hecha para una base de datos MySQL. Tendrá que adaptarlo ligeramente para otra base de datos.
Tuve el mismo problema cuando mi contenedor Django intentó conectar el contenedor mysql justo después de que empezara. Lo resolví utilizando el script wait-for.it.sh de wait-for.it.sh . Es un script de shell que espera que una IP y un host estén listos antes de continuar. Aquí está el ejemplo que utilizo para mi aplicación.
./wait-for-it.sh /
-h $(docker inspect --format ''{{ .NetworkSettings.IPAddress }}'' $MYSQL_CONTAINER_NAME) /
-p 3306 /
-t 90
En ese script le pido al contenedor mysql que espere un máximo de 90 segundos (se ejecutará normalmente cuando esté listo) en el puerto 3306 (puerto mysql predeterminado) y el host asignado por la ventana acoplable para mi MYSQL_CONTAINER_NAME. El script tiene más variables pero para mw trabajé con estos tres.
https://github.com/docker-library/mysql/blob/master/5.7/docker-entrypoint.sh docker-entrypoint.sh no admite la combinación de .sql personalizado todavía.
Creo que puedes modificar docker-entrypoint.sh para fusionar tu sql para que pueda ejecutarse una vez que la instancia de mysql esté lista.