Ich stelle einige verschiedene Docker-Container bereit, wobei MySQL der erste ist. Ich möchte Skripte ausführen, sobald die Datenbank verfügbar ist, und andere Container erstellen. Das Skript ist fehlgeschlagen, weil versucht wurde, es auszuführen, als das Einstiegspunkt-Skript, das MySQL (aus diesem offiziellen MySQL-Container ) einrichtet , noch ausgeführt wurde.
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 das Signal zu warten, dass ein MySQL-Setup-Skript von entrypoiny im Docker-Container beendet wird? Bash Sleep scheint eine suboptimale Lösung zu sein.
EDIT: Ging für ein Bash-Skript wie dieses. Nicht die eleganteste und etwas brutalste Kraft, aber sie wirkt wie ein Zauber. Vielleicht findet das jemand nützlich.
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
Antworten:
Sie können das mysql-client-Paket installieren und mysqladmin verwenden, um den Zielserver zu pingen. 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
quelle
docker run --health-cmd='mysqladmin ping --silent' -d mysql
while [ $(docker inspect --format "{{json .State.Health.Status }}" <container-name>) != "\"healthy\"" ]; do printf "."; sleep 1; done
mysqladmin ping
Dies ist erfolgreich, bevor ich die Datenbank tatsächlich verwenden kann. Ich denke, der Container wird noch ausgeführt. Es handelt sich um Initialisierungsskripte.sql
.while ! wget mysql:3306; do sleep 1 done
Diese kleine Bash-Schleife wartet darauf, dass 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
quelle
mysqladmin ping
ist hier die richtige Antwort.Dies wurde mehr oder weniger in Kommentaren zu anderen Antworten erwähnt, aber ich denke, es verdient einen eigenen Eintrag.
Zunächst können Sie Ihren Container folgendermaßen ausführen:
docker run --name mysql --health-cmd='mysqladmin ping --silent' -d mysql
Es gibt auch ein Äquivalent in der Docker-Datei.
Mit diesem Befehl wird Ihr
docker ps
und derdocker inspect
Gesundheitszustand Ihres Containers angezeigt. Insbesondere für MySQL hat diese Methode den Vorteil,mysqladmin
dass sie im Container verfügbar ist , sodass Sie sie nicht auf dem Docker-Host installieren müssen.Dann können Sie einfach ein Bash-Skript durchlaufen, um zu warten, bis der Status fehlerfrei ist. Das folgende Bash-Skript wurde von Dennis erstellt .
function getContainerHealth { docker inspect --format "{{.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:
und Ihr Skript wartet, bis der Container betriebsbereit ist. Das Skript wird beendet, wenn der Container fehlerhaft wird. Dies ist möglich, wenn beispielsweise der Docker-Host nicht über genügend Speicher verfügt, sodass die MySQL nicht genug davon für sich selbst zuweisen kann.
quelle
mysqladmin ping
fängt auch den ersten Spin-up, was wahrscheinlich nicht der Fall ist. In meinem Fall funktionierte das Ausführen einer Dummy-Abfrage mit dem Schema, von dem Sie erwarten, dass es dort vorhanden ist, am besten, dh ändern Sie den Befehl zum Ändern des Zustandsmysql -u root -e "use your_schema;"
.healthcheck
für Ihren Container haben. Aber das.State.Health.Status
gibt es nicht, wenn du es nicht tust. Möglicherweise müssen Sie sich.State.Status
stattdessen damit zufrieden geben. aber das sagtrunning
zu früh für die Bedürfnisse dieses OP.--health-cmd
ist?healthcheck
auch mit dockerManchmal besteht das Problem mit dem Port darin, dass der Port möglicherweise geöffnet ist, die Datenbank jedoch noch nicht bereit ist.
Andere Lösungen erfordern , dass Sie die installiert haben mysql oa MySQL - Client in Ihrem Host - Rechner, aber wirklich haben Sie es bereits in den Docker Behälter, so dass ich auf die Verwendung so etwas wie dies bevorzugen:
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
quelle
mysql -u root -proot -e 'status' &> /dev/null
anstelle vonmysqladmin ping
Ich habe festgestellt, dass die Verwendung des
mysqladmin ping
Ansatzes nicht immer zuverlässig ist, insbesondere wenn Sie eine neue Datenbank aufrufen. In diesem Fall können Sie möglicherweise keine Verbindung herstellen, selbst wenn Sie den Server anpingen können, wenn die Benutzer- / Berechtigungstabellen noch initialisiert werden. Stattdessen mache ich so etwas wie das Folgende:while ! docker exec db-container mysql --user=foo --password=bar -e "SELECT 1" >/dev/null 2>&1; do sleep 1 done
Bisher habe ich keine Probleme mit dieser Methode festgestellt. Ich sehe, dass VinGarcia in einem Kommentar zu einer der
mysqladmin ping
Antworten etwas Ähnliches vorgeschlagen hat .quelle
Der folgende Health-Check 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
quelle
Ich bin mir also nicht sicher, ob jemand dies gepostet hat. Es sieht nicht so aus, wie es jemand getan hat, also ... gibt es in mysqladmin einen Befehl, der eine Wartezeit enthält, das Testen der Verbindung übernimmt, dann intern erneut versucht und nach Abschluss einen Erfolg zurückgibt.
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
Der wichtige Teil ist
mysqladmin ping -h 127.0.0.1 -u root --password=MY_ROOT_PASS --wait=30 -v
mit dem--wait
Wesen des Flag zu warten , bis die Verbindung erfolgreich ist , und die Zahl ist die Menge an Versuchen , erneut zu versuchen.Idealerweise würden Sie diesen Befehl im Docker-Container ausführen, aber ich wollte den ursprünglichen Poster-Befehl nicht zu stark ä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
quelle
Ich hatte das gleiche Problem, als mein Django-Container kurz nach dem Start versuchte, den MySQL-Container zu verbinden. Ich habe es mit dem Skript wait-for.it.sh von vishnubob gelöst . Es ist ein Shell-Skript, das darauf wartet, dass eine IP und ein Host bereit sind, bevor es fortfährt. Hier ist das Beispiel, das ich für meine Anwendung 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, maximal 90 Sekunden (er wird normal ausgeführt, wenn er fertig ist) in Port 3306 (Standard-MySQL-Port) und dem vom Docker für meinen MYSQL_CONTAINER_NAME zugewiesenen Host zu warten. Das Skript hat mehr Variablen, aber für mw hat mit diesen drei gearbeitet.
quelle
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:
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()
Verwendung:
wait-for-mysql-db.py
im Quellcode Ihrer Anwendung ein.startup.py
zum Beispiel) ein anderes Python-Skript , das zuerst den obigen Code ausführt und anschließend Ihre Anwendung startet.command: ["python3", "startup.py"]
.Beachten Sie, dass diese Lösung für eine MySQL-Datenbank entwickelt wurde. Sie müssen es leicht für eine andere Datenbank anpassen.
quelle
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 mit dem Container herzustellen. Die vollständigen Details finden Sie im waitdb- Repository. Meine Lösung besteht jedoch darin, sich auf das aus dem Container abgerufene Protokoll zu verlassen. Das Skript wartet, bis das Protokoll die Nachricht auslöst, die für Verbindungen bereit ist . Das Skript kann feststellen, 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 wurde, und wartet erneut auf eine neue verbindungsbereite Nachricht. 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 machen die Verwendung klar:
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 eine Auswahl aus , um zu überprüfen, ob alles in Ordnung ist. Danach wird der Container neu gestartet und auf den Start gewartet. Anschließend wird eine neue Auswahl ausgelöst, um zu überprüfen, ob die Verbindung hergestellt werden kann.
quelle
So habe ich die Adams- Lösung in mein Docker-Compose-basiertes Projekt integriert:
Erstellt eine Bash-Datei mit dem Titel
db-ready.sh
in meinemserver
Container-Ordner (deren Inhalt in meinen Container kopiert wird -server
):#!bin/bash until nc -z -v -w30 $MYSQL_HOST 3306 do echo "Waiting a second until the database is receiving connections..." # wait for a second before checking again sleep 1 done
Ich kann dann ausführen,
docker-compose run server sh ./db-ready.sh && docker-compose run server yarn run migrate
um sicherzustellen, dass die Datenbank Verbindungen akzeptiert , wenn ich meinemigrate
Aufgabe in meinemserver
Container ausführe .Ich mag diesen Ansatz, da die Bash-Datei von jedem Befehl getrennt ist, den ich ausführen möchte. Ich könnte die
db-ready.sh
vor jeder anderen Datenbank mit der von mir ausgeführten Aufgabe leicht ausführen.quelle
https://github.com/docker-library/mysql/blob/master/5.7/docker-entrypoint.sh docker-entrypoint.sh unterstützt das Zusammenführen von benutzerdefiniertem SQL noch nicht.
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.
quelle
In Ihrem
ENTRYPOINT
Skript müssen Sie überprüfen, ob Sie eine gültige MySQL-Verbindung haben oder nicht.In dieser Lösung werde ich Ihnen zeigen, wie Sie ein PHP-Skript ausführen, um zu überprüfen, ob ein MySQL-Container eine Verbindung herstellen kann. Wenn Sie wissen möchten, warum ich dies für einen besseren Ansatz halte, lesen 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
Auf diese Weise blockieren Sie im Wesentlichen jede Bootstrapping-Logik Ihres Containers, BIS Sie eine gültige MySQL-Verbindung haben.
quelle
Ich benutze 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);
quelle