wake-up-neo.net

Woher weiß ich, dass mein Docker-MySQL-Container aktiv ist und MySQL zur Abfrage bereit ist?

Ich setze ein paar verschiedene Docker-Container ein, wobei mysql der erste ist. Ich möchte Skripts ausführen, sobald die Datenbank verfügbar ist, und mit dem Erstellen anderer Container fortfahren. Das Skript ist fehlgeschlagen, weil es versucht hat, ausgeführt zu werden, als das Einstiegspunktskript, das mysql (von diesem offiziellen mysql-Container ) einrichtet, noch ausgeführt wird.

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

Gibt es eine Möglichkeit, auf ein Signal zu warten, in dem ein mysql-Setupskript von entrypoiny im Docker-Container beendet wird? Bash-Schlaf scheint eine suboptimale Lösung zu sein.

EDIT: Wollte so ein Bash-Skript. Nicht die eleganteste und irgendwie rohe Kraft, wirkt aber wie ein Zauber. Vielleicht wird jemand das nützlich finden.

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

Sie können das mysql-client-Paket installieren und mit mysqladmin den Zielserver anpingen. Nützlich bei der Arbeit mit mehreren Docker-Containern. Kombinieren Sie mit Schlaf und erstellen Sie eine einfache Warteschleife:

while ! mysqladmin ping -h"$DB_Host" --silent; do
    sleep 1
done
37
flamemyst

Diese kleine Bash-Schleife wartet, bis mysql geöffnet ist. Es sollten keine zusätzlichen Pakete installiert werden: 

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

Dies wurde mehr oder weniger in Kommentaren zu anderen Antworten erwähnt, aber ich denke, es verdient seinen eigenen Eintrag. 

Zunächst können Sie Ihren Container auf folgende Weise ausführen:

docker run --name mysql --health-cmd='mysqladmin ping --silent' -d mysql

Es gibt auch ein Äquivalent in der Dockerfile.

Mit diesem Befehl zeigen Ihnen docker ps und docker inspect den Status Ihres Containers an. Insbesondere für mysql hat diese Methode den Vorteil, dass mysqladminim Container verfügbar ist. Sie müssen sie also nicht auf dem Docker-Host installieren.

Dann können Sie einfach ein Bash-Skript einschleifen, um auf den Status zu warten, um fehlerfrei zu werden. Das folgende Bash-Skript wird von Dennis erstellt.

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"
}

Jetzt können Sie dies in Ihrem Skript tun:

waitContainer mysql

und Ihr Skript wartet, bis der Container betriebsbereit ist. Das Skript wird beendet, wenn der Container fehlerhaft wird. Dies ist möglich, wenn der Docker-Host beispielsweise nicht über genügend Arbeitsspeicher verfügt, sodass der MySQL-Server nicht genug für ihn reservieren kann.

18
Andrew Savinykh

Das Problem mit dem Port ist manchmal, dass der Port open sein könnte, die Datenbank jedoch noch nicht bereit ist.

Bei anderen Lösungen ist es erforderlich, dass Sie den mysql o a mysql client auf Ihrem Host-Computer installiert haben. Sie haben ihn jedoch bereits im Docker-Container.

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

Die folgende Zustandsprüfung funktioniert für alle meine MySQL-Container:

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

Ich bin mir also nicht sicher, ob jemand dies gepostet hat. Es sieht nicht so aus, als ob jemand es getan hätte, also gibt es in mysqladmin einen Befehl, der ein Warten beinhaltet. Er verarbeitet das Testen der Verbindung, versucht es dann intern erneut und gibt nach Abschluss einen Erfolg zurück.

Sudo docker run --name mysql -e MYSQL_ROOT_PASSWORD=MY_ROOT_PASS -p 3306:3306 -d mysql
mysqladmin ping -h 127.0.0.1 -u root --password=MY_ROOT_PASS --wait=30 && mysql -h 127.0.0.1 -P 3306 -u root --password=MY_ROOT_PASS < MY_SQL_SCRIPT.sql

Das wichtige Stück ist mysqladmin ping -h 127.0.0.1 -u root --password=MY_ROOT_PASS --wait=30 -v mit dem --wait ist das Flag, mit dem gewartet wird, bis die Verbindung erfolgreich hergestellt wurde, und die Anzahl gibt die Anzahl der Wiederholungsversuche an.

Idealerweise würden Sie diesen Befehl im Docker-Container ausführen, aber ich wollte den ursprünglichen Befehl posters nicht zu sehr ändern.

Bei Verwendung in meiner make-Datei zur Initialisierung

db.initialize: db.wait db.initialize


db.wait:
  docker-compose exec -T db mysqladmin ping -u $(DATABASE_USERNAME) -p$(DATABASE_PASSWORD) --wait=30 --silent

db.initialize:
  docker-compose exec -T db mysql -u $(DATABASE_USERNAME) -p$(DATABASE_PASSWORD) $(DATABASE_NAME) < dev/sql/base_instance.sql
2
ctatro85

Ich habe festgestellt, dass die Verwendung des mysqladmin ping-Ansatzes nicht immer zuverlässig ist, insbesondere wenn Sie eine neue Datenbank erstellen. In diesem Fall können Sie möglicherweise selbst dann keine Verbindung herstellen, wenn die Benutzer-/Berechtigungstabellen initialisiert werden, auch wenn Sie ein Ping-Signal an den Server senden können. Stattdessen mache ich so etwas wie:

while ! docker exec db-container mysql --user=foo --password=bar -e "SELECT 1" >/dev/null 2>&1; do
    sleep 1
done

Mit dieser Methode habe ich bisher keine Probleme gehabt. Ich sehe, dass etwas Ähnliches von VinGarcia in einem Kommentar zu einer der mysqladmin ping-Antworten vorgeschlagen wurde.

2
Matt Kramer

Ich hatte das gleiche Problem, als mein Django-Container versuchte, den MySQL-Container direkt nach dem Start zu verbinden. Ich habe es mit dem Script vishnubob wait-for.it.sh gelöst. Ein Shell-Skript, das darauf wartet, dass eine IP-Adresse und ein Host bereit sind, bevor Sie fortfahren. Hier ist das Beispiel, das ich für meine Bewerbung verwende.

./wait-for-it.sh \
    -h $(docker inspect --format '{{ .NetworkSettings.IPAddress }}' $MYSQL_CONTAINER_NAME) \
    -p 3306 \
    -t 90

In diesem Skript fordere ich den mysql-Container auf, im Port 3306 (Standard-mysql-Port) und dem Host, der vom Docker für meinen MYSQL_CONTAINER_NAME zugewiesen wurde, maximal 90 Sekunden zu warten. Das Skript hat mehr Variablen, aber mit diesen drei hat mw gearbeitet.

Ich habe eine neue Lösung für dieses Problem entwickelt, die auf einem neuen Ansatz basiert. Alle Ansätze, die ich gefunden habe, basieren auf einem Skript, das immer wieder versucht, eine Verbindung zur Datenbank herzustellen oder eine TCP Verbindung zum Container herzustellen. Die vollständigen Details finden Sie auf der Seite waitdb repository, aber meine Lösung besteht darin, mich auf das abgerufene Protokoll aus dem Container zu verlassen. Das Skript wartet, bis das Protokoll die Nachricht auslöst, die für Verbindungen bereit ist . Das Skript erkennt, ob der Container zum ersten Mal gestartet wird. In diesem Fall wartet das Skript, bis das erste Datenbankskript ausgeführt und die Datenbank neu gestartet wird, und wartet erneut auf ein neues . Ready for Connections - Meldung Ich habe diese Lösung unter MySQL 5.7 und MySQL 8.0 getestet.

Das Skript selbst ( wait_db.sh):

#!/bin/bash

STRING_CONNECT="mysqld: ready for connections"

findString() {
    ($1 logs -f $4 $5 $6 $7 $8 $9 2>&1 | grep -m $3 "$2" &) | grep -m $3 "$2" > /dev/null
}

echo "Waiting startup..."
findString $1 "$STRING_CONNECT" 1 $2 $3 $4 $5 $6 $7
$1 logs $2 $3 $4 $5 2>&1 | grep -q "Initializing database"
if [ $? -eq 0 ] ; then
    echo "Almost there..."
    findString $1 "$STRING_CONNECT" 2 $2 $3 $4 $5 $6 $7
fi
echo "Server is up!"

Das Skript kann in Docker Compose oder in Docker selbst verwendet werden. Ich hoffe die folgenden Beispiele verdeutlichen die Verwendung:

Beispiel 01: Verwenden mit Docker Compose

SERVICE_NAME="mysql" && \
docker-compose up -d $SERVICE_NAME && \
./wait_db.sh docker-compose --no-color $SERVICE_NAME

Beispiel 02: Verwenden mit Docker

CONTAINER_NAME="wait-db-test" && \
ISO_NOW=$(date -uIs) && \
  docker run --rm --name $CONTAINER_NAME \
    -e MYSQL_ROOT_PASSWORD=$ROOT_PASSWORD \
    -d mysql:5.7 && \
./wait_db.sh docker --since "$ISO_NOW" $CONTAINER_NAME

Beispiel 3: Ein vollständiges Beispiel (der Testfall)

Ein vollständiges Beispiel finden Sie im Testfall des Repositorys . Dieser Testfall startet ein neues MySQL, erstellt eine Dummy-Datenbank, wartet, bis alles gestartet ist, und löst dann ein select aus, um zu überprüfen, ob alles in Ordnung ist. Danach wird der Container neu gestartet und gewartet, bis er gestartet wurde. Anschließend wird ein neuer select ausgelöst, um zu prüfen, ob er für die Verbindung bereit ist.

1
Marcelo Barros

Wenn der Docker-Container, der auf einen Mysql-Container wartet, auf einem python-Image basiert (z. B. für eine Django-Anwendung), können Sie den folgenden Code verwenden.

Vorteile sind:

  • Es basiert nicht auf wait-for-it.sh , was darauf wartet, dass die IP und der Port von mysql fertig sind, aber dies bedeutet nicht automatisch, dass die mysql-Initialisierung abgeschlossen ist.
  • Es ist kein auf einer ausführbaren Datei von mysql oder mysqladmin basierendes Shell-Skript, das in Ihrem Container vorhanden sein muss: Da Ihr Container auf einem Python-Image basiert, müssen Sie mysql oben auf diesem Image installieren. Bei der folgenden Lösung verwenden Sie die Technologie, die bereits im Container vorhanden ist: Pure Python.

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()

Verwendungszweck:

  1. Fügen Sie diesen Code in eine Python-Datei (beispielsweise wait-for-mysql-db.py) im Quellcode Ihrer Anwendung ein. 
  2. Schreiben Sie ein anderes Python-Skript (beispielsweise startup.py), das den obigen Code zuerst ausführt und anschließend Ihre Anwendung startet. 
  3. Stellen Sie sicher, dass die Dockerfile Ihres Anwendungscontainers diese beiden Python-Skripts zusammen mit dem Quellcode der Anwendung in ein Docker-Image packt.
  4. Konfigurieren Sie in Ihrer Docker-Compose-Datei Ihren Anwendungscontainer mit: command: ["python3", "startup.py"].

Beachten Sie, dass diese Lösung für eine MySQL-Datenbank erstellt wurde. Sie müssen es leicht an eine andere Datenbank anpassen.

https://github.com/docker-library/mysql/blob/master/5.7/docker-entrypoint.sh docker-entrypoint.sh unterstützt noch nicht das Zusammenführen angepasster .sql.

Ich denke, Sie können docker-entrypoint.sh so ändern, dass Ihre SQL zusammengeführt wird, damit sie ausgeführt werden kann, sobald die mysql-Instanz fertig ist.

0
kmsheng

Ich verwende den folgenden Code; 

export COMPOSE_PROJECT_NAME = web;

export IS_DATA_CONTAINER_EXISTS = $ (Docker-Volume 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

In Ihrem ENTRYPOINT-Skript müssen Sie überprüfen, ob Sie über eine gültige MySQL-Verbindung verfügen oder nicht. 

Für diese Lösung ist es nicht erforderlich, dass Sie einen MySQL-Client im Container installieren. Während der Container ausgeführt wird, während php:7.0-fpm ausgeführt wurde, war nc keine Option, da er ebenfalls installiert werden musste. Die Überprüfung, ob der Port offen ist, bedeutet nicht notwendigerweise, dass der Dienst ausgeführt wird und ordnungsgemäß verfügbar ist. [mehr davon]

In dieser Lösung zeige ich Ihnen, wie Sie ein PHP -Skript ausführen, um zu prüfen, ob ein MySQL-Container eine Verbindung aufnehmen kann. Wenn Sie wissen wollen, warum ich denke, dass dies ein besserer Ansatz ist, überprüfen Sie meinen Kommentar hier .

Datei 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

Wenn Sie dies ausführen, blockieren Sie im Wesentlichen jede Bootstrapping-Logik Ihres Containers, bis Sie eine gültige MySQL-Verbindung haben.

0
Starx