Ich versuche sicherzustellen, dass mein App-Container keine Migrationen / Starts ausführt, bis der Datenbankcontainer gestartet wird und BEREIT ist, Verbindungen zu akzeptieren.
Also habe ich mich für den Healthcheck entschieden und bin abhängig von der Option in Docker Compose File v2.
In der App habe ich folgendes
app:
...
depends_on:
db:
condition: service_healthy
Die Datenbank hat dagegen den folgenden Healthcheck
db:
...
healthcheck:
test: TEST_GOES_HERE
timeout: 20s
retries: 10
Ich habe einige Ansätze ausprobiert wie:
- Stellen Sie sicher, dass die Datenbank DIR erstellt wurde
test: ["CMD", "test -f var/lib/mysql/db"]
- Erhalten der MySQL-Version:
test: ["CMD", "echo 'SELECT version();'| mysql"]
- Pingen Sie den Administrator an (markiert den Datenbankcontainer als fehlerfrei, scheint aber kein gültiger Test zu sein)
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
Hat jemand eine Lösung dafür?
mysql
docker
docker-compose
dockerfile
John Kariuki
quelle
quelle
mysql -u USER -p PASSWORD -h MYSQLSERVERNAME -e 'select * from foo...' database-name
Antworten:
Der API-Container wird erst gestartet, wenn der DB-Container fehlerfrei ist (im Grunde genommen, bis mysqladmin aktiv ist und Verbindungen akzeptiert).
quelle
mysqladmin ping
gibt ein falsches Positiv zurück, wenn der Server ausgeführt wird, aber noch keine Verbindungen akzeptiert.condition
unterdepends_on
wird in Version 3+ nicht unterstützttest: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ]
- Wenn SieMYSQL_ROOT_PASSWORD
imenvironments
Abschnitt definiert haben .Wenn Sie mit Docker-compose v3 + ,
condition
als Optiondepends_on
wurde entfernt .Der empfohlene Weg ist eher zu verwenden
wait-for-it
,dockerize
oderwait-for
.docker-compose.yml
Ändern Sie in Ihrer Datei Ihren Befehl wie folgt:command: sh -c 'bin/wait-for db:3306 -- bundle exec rails s'
Ich persönlich bevorzuge,
wait-for
da es in einem alpinen Container laufen kann (sh
kompatibel, keine Abhängigkeit vonbash
). Der Nachteil ist, dass es davon abhängt.netcat
Wenn Sie sich also für die Verwendung entscheiden, stellen Sie sicher, dass Sienetcat
im Container installiert haben , oder installieren Sie ihn in Ihrer Docker-Datei, zum Beispiel mit:RUN apt-get -q update && apt-get -qy install netcat
Ich habe das
wait-for
Projekt auch gegabelt , damit es auf einen fehlerfreien HTTP-Status prüfen kann (es wird verwendetwget
). Dann können Sie so etwas tun:command: sh -c 'bin/wait-for http://api/ping -- jest test'
PS: Ein PR kann auch zusammengeführt werden, um diese Kapazität zum
wait-for
Projekt hinzuzufügen .quelle
Das sollte reichen
version: '2.1' services: mysql: image: mysql ports: ['3306:3306'] environment: MYSQL_USER: myuser MYSQL_PASSWORD: mypassword healthcheck: test: mysqladmin ping -h 127.0.0.1 -u $$MYSQL_USER --password=$$MYSQL_PASSWORD
quelle
$
?$
Im, der vom Host auf die env-Variable zugreift, nehme ich an? das ist schön danke!Wenn Sie den Container ändern können, um darauf zu warten, dass MySQL bereit ist, tun Sie es.
Wenn Sie nicht die Kontrolle über den Container haben, mit dem Sie die Datenbank verbinden möchten, können Sie versuchen, auf den bestimmten Port zu warten.
Zu diesem Zweck verwende ich ein kleines Skript, um auf einen bestimmten Port zu warten, der von einem anderen Container verfügbar gemacht wird.
In diesem Beispiel wartet myserver darauf, dass Port 3306 des mydb- Containers erreichbar ist.
# Your database mydb: image: mysql ports: - "3306:3306" volumes: - yourDataDir:/var/lib/mysql # Your server myserver: image: myserver ports: - "....:...." entrypoint: ./wait-for-it.sh mydb:3306 -- ./yourEntryPoint.sh
Die Dokumentation zum Warten auf das Skript finden Sie hier
quelle
wait-for-it.sh
früher versucht, aber es überschreibt die Standard-Docker-Datei, oder? Wie sieht die entrypoint.sh aus?Hallo für einen einfachen Healthcheck mit Docker-Compose v2.1 , ich habe verwendet:
Grundsätzlich wird ein einfacher
mysql
Befehl ausgeführtSHOW DATABASES;
, der als Beispiel den Benutzerroot
mit dem Kennwortrootpasswd
in der Datenbank verwendet.Wenn der Befehl erfolgreich ist, ist die Datenbank aktiv und bereit, also der Healthcheck-Pfad. Sie können verwenden,
interval
damit es in Intervallen getestet wird.Wenn Sie das andere Feld zur besseren Sichtbarkeit entfernen, sehen Sie hier, wie es in Ihrem Feld aussehen würde
docker-compose.yaml
.version: '2.1' services: db: ... # Other db configuration (image, port, volumes, ...) healthcheck: test: "/usr/bin/mysql --user=root --password=rootpasswd --execute \"SHOW DATABASES;\"" interval: 2s timeout: 20s retries: 10 app: ... # Other app configuration depends_on: db: condition: service_healthy
quelle
command: ["/home/app/jswebservice/wait-for-it.sh", "maria:3306", "--", "node", "webservice.js"]
--execute \"SHOW DATABASES;\"
Ich habe das
docker-compose.yml
wie im folgenden Beispiel geändert und es hat funktioniert.mysql: image: mysql:5.6 ports: - "3306:3306" volumes: # Preload files for data - ../schemaAndSeedData:/docker-entrypoint-initdb.d environment: MYSQL_ROOT_PASSWORD: rootPass MYSQL_DATABASE: DefaultDB MYSQL_USER: usr MYSQL_PASSWORD: usr healthcheck: test: mysql --user=root --password=rootPass -e 'Design your own check script ' LastSchema
In meinem Fall
../schemaAndSeedData
enthält mehrere Schema- und Data-Seeding-SQL-Dateien.Design your own check script
kann ähnlich sein wie folgtselect * from LastSchema.LastDBInsert
.Während webabhängiger Containercode war
depends_on: mysql: condition: service_healthy
quelle
LastSchema.LastDBInsert
ein MySQL-Standard oder ein Datenbank-Engine spezifisch?Ich hatte das gleiche Problem, ich habe ein externes Bash-Skript für diesen Zweck erstellt (es ist inspiriert von Maxim Antwort). Ersetzen Sie
mysql-container-name
durch den Namen Ihres MySQL-Containers und es wird auch ein Passwort / Benutzer benötigt:bin / wait-for-mysql.sh :
#!/bin/sh until docker container exec -it mysql-container-name mysqladmin ping -P 3306 -proot | grep "mysqld is alive" ; do >&2 echo "MySQL is unavailable - waiting for it... 😴" sleep 1 done
In meinem MakeFile rufe ich dieses Skript direkt nach meinem
docker-compose
Aufruf auf:wait-for-mysql: ## Wait for MySQL to be ready bin/wait-for-mysql.sh run: up wait-for-mysql reload serve ## Start everything...
Dann kann ich andere Befehle aufrufen, ohne den Fehler zu haben:
Ausgabebeispiel:
docker-compose -f docker-compose.yaml up -d Creating network "strangebuzzcom_default" with the default driver Creating sb-elasticsearch ... done Creating sb-redis ... done Creating sb-db ... done Creating sb-app ... done Creating sb-kibana ... done Creating sb-elasticsearch-head ... done Creating sb-adminer ... done bin/wait-for-mysql.sh MySQL is unavailable - waiting for it... 😴 MySQL is unavailable - waiting for it... 😴 MySQL is unavailable - waiting for it... 😴 MySQL is unavailable - waiting for it... 😴 MySQL is unavailable - waiting for it... 😴 MySQL is unavailable - waiting for it... 😴 MySQL is unavailable - waiting for it... 😴 MySQL is unavailable - waiting for it... 😴 mysqld is alive php bin/console doctrine:cache:clear-metadata // Clearing all Metadata cache entries [OK] Successfully deleted cache entries.
Ich habe den Gesundheitscheck gelöscht, da er bei diesem Ansatz jetzt unbrauchbar ist.
quelle
NEUSTART BEI AUSFALL
Da v3
condition: service_healthy
nicht mehr verfügbar ist. Die Idee ist, dass der Entwickler einen Mechanismus für die Wiederherstellung nach einem Absturz in der App selbst implementieren sollte. Für einfache Anwendungsfälle besteht eine einfache Möglichkeit, dieses Problem zu beheben, darin, dierestart
Option zu verwenden .Wenn der MySQL-Dienststatus Ihre Anwendung veranlasst, können
exited with code 1
Sie eine derrestart
verfügbaren Richtlinienoptionen verwenden. z.B,on-failure
quelle
Hinzufügen einer aktualisierten Lösung für den Healthcheck-Ansatz. Einfacher Ausschnitt:
healthcheck: test: out=$$(mysqladmin ping -h localhost -P 3306 -u foo --password=bar 2>&1); echo $$out | grep 'mysqld is alive' || { echo $$out; exit 1; }
Erläuterung : Da
mysqladmin ping
falsch positive Ergebnisse zurückgegeben werden (insbesondere bei falschem Kennwort), speichere ich die Ausgabe in einer temporären Variablen und verwende sie danngrep
, um die erwartete Ausgabe zu finden (mysqld is alive
). Wenn es gefunden wird, wird der 0-Fehlercode zurückgegeben. Falls es nicht gefunden wird, drucke ich die gesamte Nachricht und gebe den 1-Fehlercode zurück.Erweitertes Snippet:
version: "3.8" services: db: image: linuxserver/mariadb environment: - FILE__MYSQL_ROOT_PASSWORD=/run/secrets/mysql_root_password - FILE__MYSQL_PASSWORD=/run/secrets/mysql_password secrets: - mysql_root_password - mysql_password healthcheck: test: out=$$(mysqladmin ping -h localhost -P 3306 -u root --password=$$(cat $${FILE__MYSQL_ROOT_PASSWORD}) 2>&1); echo $$out | grep 'mysqld is alive' || { echo $$out; exit 1; } secrets: mysql_root_password: file: ${SECRETSDIR}/mysql_root_password mysql_password: file: ${SECRETSDIR}/mysql_password
Erläuterung : Ich verwende Docker-Geheimnisse anstelle von env-Variablen (dies kann jedoch auch mit regulären env-Variablen erreicht werden). Die Verwendung von
$$
ist für ein wörtliches$
Zeichen, das beim Übergeben an den Container entfernt wird.Ausgabe von
docker inspect --format "{{json .State.Health }}" db | jq
bei verschiedenen Gelegenheiten:Alles in Ordnung:
{ "Status": "healthy", "FailingStreak": 0, "Log": [ { { "Start": "2020-07-20T01:03:02.326287492+03:00", "End": "2020-07-20T01:03:02.915911035+03:00", "ExitCode": 0, "Output": "mysqld is alive\n" } ] }
DB ist (noch) nicht aktiv:
{ "Status": "starting", "FailingStreak": 1, "Log": [ { "Start": "2020-07-20T01:02:58.816483336+03:00", "End": "2020-07-20T01:02:59.401765146+03:00", "ExitCode": 1, "Output": "\u0007mysqladmin: connect to server at 'localhost' failed error: 'Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2 \"No such file or directory\")' Check that mysqld is running and that the socket: '/var/run/mysqld/mysqld.sock' exists!\n" } ] }
Falsches Passwort:
{ "Status": "unhealthy", "FailingStreak": 13, "Log": [ { "Start": "2020-07-20T00:56:34.303714097+03:00", "End": "2020-07-20T00:56:34.845972979+03:00", "ExitCode": 1, "Output": "\u0007mysqladmin: connect to server at 'localhost' failed error: 'Access denied for user 'root'@'localhost' (using password: YES)'\n" } ] }
quelle