web-dev-qa-db-fra.com

Comment savoir si mon conteneur Docker MySQL est prêt et que MySQL est prêt à répondre aux requêtes

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
34
haren

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
37
flamemyst

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
28
Adam

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.

18
Andrew Savinykh

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
6
alejandropg

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
3
developer10214

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.

2
Matt Kramer

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:

  • Ce n'est pas basé sur wait-for-it.sh , qui attend que l'IP et le port de mysql soit prêt, mais cela ne signifie pas automatiquement que l'initialisation de mysql est terminée.
  • Ce n'est pas un script Shell basé sur un exécutable mysql ou mysqladmin qui doit être présent dans votre conteneur: puisque votre conteneur est basé sur une image python, vous devrez installer mysql par dessus cette image. Avec la solution ci-dessous, vous utilisez la technologie déjà présente dans le conteneur: le python pur.

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:

  1. Ajoutez ce code dans un fichier python (wait-for-mysql-db.py par exemple) dans le code source de votre application. 
  2. Écrivez un autre script python (startup.py par exemple) qui exécute d'abord le code ci-dessus, puis lance votre application. 
  3. Assurez-vous que le fichier Docker de votre conteneur d'applications intègre ces deux scripts python avec le code source de l'application dans une image Docker.
  4. Dans votre fichier docker-compose, configurez votre conteneur d'applications avec: 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.

0

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.

0
kmsheng

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);
0
Erdinç Çorbacı

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 sous nc 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.

0
Starx