Je déploie plusieurs conteneurs Docker différents, mysql étant le premier. Je souhaite exécuter des scripts dès que la base de données est opérationnelle et créer d'autres conteneurs. Le script a échoué car il essayait de s'exécuter lorsque le script entrypoint, qui configurait mysql (à partir de ce conteneur mysql officiel ), était toujours en cours d'exécution.
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
Est-il possible d'attendre le signal d'un script d'installation entrypoiny mysql se terminant à l'intérieur du conteneur de menu fixe? Bash sleep semble être une solution sous-optimale.
EDIT: Je suis allé pour un script bash comme celui-ci. Pas la force la plus élégante et un peu brute, mais fonctionne comme un charme. Peut-être que quelqu'un trouvera cela utile.
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
Vous pouvez installer le paquet mysql-client et utiliser mysqladmin pour envoyer un ping au serveur cible. Utile lorsque vous travaillez avec plusieurs conteneurs Docker. Combinez avec le sommeil et créez une boucle d'attente simple:
while ! mysqladmin ping -h"$DB_Host" --silent; do
sleep 1
done
Cette petite boucle bash attend que mysql soit ouvert, ne devrait nécessiter l'installation d'aucun paquet supplémentaire:
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
Cela a été plus ou moins mentionné dans les commentaires aux autres réponses, mais je pense que cela mérite sa propre entrée.
Tout d’abord, vous pouvez exécuter votre conteneur de la manière suivante:
docker run --name mysql --health-cmd='mysqladmin ping --silent' -d mysql
Il existe également un équivalent dans le fichier Docker.
Avec cette commande, vos docker ps
et docker inspect
vous montreront l’état de santé de votre conteneur. Pour mysql en particulier, cette méthode présente l'avantage d'être mysqladmin
disponible à l'intérieur du conteneur, vous n'avez donc pas besoin de l'installer sur l'hôte de docker.
Ensuite, vous pouvez simplement boucler dans un script bash pour attendre que le statut devienne sain. Le script bash suivant est créé par 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"
}
Maintenant, vous pouvez le faire dans votre script:
waitContainer mysql
et votre script attendra que le conteneur soit opérationnel. Le script se fermera si le conteneur devient insalubre, ce qui est possible si par exemple le docker Host manque de mémoire afin que mysql ne puisse pas en allouer suffisamment pour lui-même.
Parfois, le problème avec le port est que le port peut être open , mais la base de données n’est pas encore prête.
D'autres solutions requièrent que vous ayez installé le mysql o a le client mysql sur votre ordinateur hôte, mais vous l'avez déjà dans le conteneur Docker, je préfère utiliser quelque chose comme ceci:
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
Le bilan de santé suivant fonctionne pour tous mes conteneurs 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
J'ai constaté que l'utilisation de l'approche mysqladmin ping
n'est pas toujours fiable, surtout si vous créez une nouvelle base de données. Dans ce cas, même si vous êtes en mesure d’envoyer une requête ping au serveur, vous ne pourrez peut-être pas vous connecter si les tables utilisateur/privilège sont toujours en cours d’initialisation. Au lieu de cela, je fais quelque chose comme ceci:
while ! docker exec db-container mysql --user=foo --password=bar -e "SELECT 1" >/dev/null 2>&1; do
sleep 1
done
Jusqu'à présent, je n'ai rencontré aucun problème avec cette méthode. Je vois que quelque chose de similaire a été suggéré par VinGarcia dans un commentaire à l’une des réponses mysqladmin ping
.
J'ai eu le même problème lorsque mon conteneur Django a essayé de se connecter au conteneur mysql juste après son démarrage. Je l'ai résolu en utilisant le script wait-for.it.sh de vishnubob. C'est un script shell qui attend qu'une adresse IP et un hôte soit prêt avant de continuer. Voici l'exemple que j'utilise pour mon application.
./wait-for-it.sh \
-h $(docker inspect --format '{{ .NetworkSettings.IPAddress }}' $MYSQL_CONTAINER_NAME) \
-p 3306 \
-t 90
Dans ce script, je demande au conteneur mysql d'attendre 90 secondes maximum (il fonctionnera normalement lorsqu'il est prêt) dans le port 3306 (port mysql par défaut) et à l'hôte désigné par docker pour mon nom MYSQL_CONTAINER_NAME. Le script a plus de variables mais pour mw travaillé avec ces trois.
Si le conteneur docker en attente d'un conteneur mysql est basé sur une image python (par exemple pour une application Django), vous pouvez utiliser le code ci-dessous.
Les avantages sont:
Code:
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()
Usage:
wait-for-mysql-db.py
par exemple) dans le code source de votre application. startup.py
par exemple) qui exécute d'abord le code ci-dessus, puis lance votre application. command: ["python3", "startup.py"]
.Notez que cette solution est faite pour une base de données MySQL. Vous devrez l'adapter légèrement pour une autre base de données.
https://github.com/docker-library/mysql/blob/master/5.7/docker-entrypoint.sh docker-entrypoint.sh ne prend pas encore en charge la fusion .sql personnalisé.
Je pense que vous pouvez modifier docker-entrypoint.sh pour fusionner votre sql afin qu'il puisse être exécuté une fois que l'instance mysql est prête.
J'utilise le code suivant;
export COMPOSE_PROJECT_NAME = web;
export IS_DATA_CONTAINER_EXISTS = $ (volume du menu fixe ls | grep $ {COMPOSE_PROJECT_NAME} _sqldata);
docker-compose up -d;
docker-compose ps;
export NETWORK_GATEWAY=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.Gateway}}{{end}}' ${COMPOSE_PROJECT_NAME}_webserver1_con);
Sur votre script ENTRYPOINT
, vous devez vérifier si vous avez une connexion MySQL valide ou non.
Cette solution ne nécessite pas l'installation d'un client MySQL sur le conteneur. L'exécution du conteneur avec
php:7.0-fpm
sousnc
n'était pas une option car il devait également être installé. De plus, vérifier si le port est ouvert ne signifie pas nécessairement que le service est en cours d'exécution et exposé correctement. [plus de ceci]
Donc, dans cette solution, je vais vous montrer comment exécuter un script PHP pour vérifier si un conteneur MySQL est capable de se connecter. Si vous voulez savoir pourquoi je pense que c'est une meilleure approche, vérifiez mon commentaire ici .
Fichier 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
En l'exécutant, vous bloquez toute logique d'amorçage de votre conteneur jusqu'à ce que vous disposiez d'une connexion MySQL valide.