J'essaie de m'assurer que mon conteneur d'application n'exécute pas les migrations / démarre tant que le conteneur DB n'est pas démarré et PRÊT À accepter les connexions.
J'ai donc décidé d'utiliser le contrôle de santé et dépend de l'option dans le fichier docker compose v2.
Dans l'application, j'ai les éléments suivants
app:
...
depends_on:
db:
condition: service_healthy
La base de données, d'autre part, a le contrôle de santé suivant
db:
...
healthcheck:
test: TEST_GOES_HERE
timeout: 20s
retries: 10
J'ai essayé quelques approches comme:
- s'assurer que le db DIR est créé
test: ["CMD", "test -f var/lib/mysql/db"]
- Obtenir la version mysql:
test: ["CMD", "echo 'SELECT version();'| mysql"]
- Ping the admin (marque le conteneur db comme sain mais ne semble pas être un test valide)
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
Quelqu'un at-il une solution à cela?
mysql
docker
docker-compose
dockerfile
John Kariuki
la source
la source
mysql -u USER -p PASSWORD -h MYSQLSERVERNAME -e 'select * from foo...' database-name
Réponses:
Le conteneur d'API ne démarrera pas tant que le conteneur de base de données ne sera pas sain (essentiellement tant que mysqladmin ne sera pas opérationnel et n'acceptera pas les connexions.)
la source
mysqladmin ping
renverra un faux positif si le serveur est en cours d'exécution mais n'accepte pas encore les connexions.condition
underdepends_on
n'est pas pris en charge dans la version 3+test: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ]
- si vous avez définiMYSQL_ROOT_PASSWORD
dans laenvironments
sectionSi vous utilisez docker-compose v3 + ,
condition
l'option dedepends_on
a été supprimée .Le chemin est recommandé d'utiliser plutôt
wait-for-it
,dockerize
ouwait-for
. Dans votredocker-compose.yml
fichier, changez votre commande pour être:command: sh -c 'bin/wait-for db:3306 -- bundle exec rails s'
Personnellement, je préfère
wait-for
car il peut fonctionner dans un conteneur Alpine (sh
compatible, sans dépendancebash
). L'inconvénient est que cela dépend denetcat
, donc si vous décidez de l'utiliser, assurez-vous de l'avoirnetcat
installé dans le conteneur, ou installez-le dans votre Dockerfile, par exemple avec:RUN apt-get -q update && apt-get -qy install netcat
J'ai également forké le
wait-for
projet afin qu'il puisse vérifier l'état HTTP sain (il utilisewget
). Ensuite, vous pouvez faire quelque chose comme ça:command: sh -c 'bin/wait-for http://api/ping -- jest test'
PS: Un PR est également prêt à être fusionné pour ajouter cette capacité au
wait-for
projet.la source
Cela devrait suffire
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
la source
$
?$
Im accédant à la variable env de l'hôte alors je suppose? c'est gentil merci!Si vous pouvez changer le conteneur pour attendre que mysql soit prêt, faites-le.
Si vous n'avez pas le contrôle du conteneur auquel vous souhaitez connecter la base de données, vous pouvez essayer d'attendre le port spécifique.
Pour cela, j'utilise un petit script pour attendre un port spécifique exposé par un autre conteneur.
Dans cet exemple, myserver attendra que le port 3306 du conteneur mydb soit accessible.
# 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
Vous pouvez trouver la documentation du script wait-for-it ici
la source
wait-for-it.sh
plus tôt mais cela remplace le Dockerfile par défaut, n'est-ce pas? À quoi ressemble entrypoint.sh?Bonjour pour un simple bilan de santé à l'aide de docker-compose v2.1 , j'ai utilisé:
Fondamentalement, il exécute une
mysql
commande simple enSHOW DATABASES;
utilisant comme exemple l'utilisateurroot
avec le motrootpasswd
de passe dans la base de données.Si la commande réussit, la base de données est active et prête donc le chemin de vérification de l'état. Vous pouvez l'utiliser
interval
pour qu'il teste à intervalle.En supprimant l'autre champ pour la visibilité, voici à quoi il ressemblerait dans votre
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
la source
command: ["/home/app/jswebservice/wait-for-it.sh", "maria:3306", "--", "node", "webservice.js"]
--execute \"SHOW DATABASES;\"
est ce qui m'a fait attendre que la base de données soit disponible pour que l'application puisse y accéderJ'ai modifié le
docker-compose.yml
selon l'exemple suivant et cela a fonctionné.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
Dans mon cas,
../schemaAndSeedData
contient plusieurs fichiers sql de schéma et de données d'amorçage.Design your own check script
peut être similaire au suivantselect * from LastSchema.LastDBInsert
.Alors que le code de conteneur dépendant du Web était
depends_on: mysql: condition: service_healthy
la source
LastSchema.LastDBInsert
moteur de base de données par défaut ou MySQL est-il spécifique?J'ai eu le même problème, j'ai créé un script de bash externe à cet effet (il est inspiré de la réponse de Maxim). Remplacez
mysql-container-name
par le nom de votre conteneur MySQL et un mot de passe / utilisateur est également nécessaire: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
Dans mon MakeFile, j'appelle ce script juste après mon
docker-compose
appel:wait-for-mysql: ## Wait for MySQL to be ready bin/wait-for-mysql.sh run: up wait-for-mysql reload serve ## Start everything...
Ensuite, je peux appeler d'autres commandes sans avoir l'erreur:
Exemple de sortie:
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.
J'ai supprimé le bilan de santé car il est désormais inutile avec cette approche.
la source
Ajout d'une solution mise à jour pour l'approche Healthcheck. Extrait simple:
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; }
Explication : Puisque
mysqladmin ping
renvoie des faux positifs (en particulier pour un mot de passe incorrect), j'enregistre la sortie dans une variable temporaire, puis j'utilisegrep
pour trouver la sortie attendue (mysqld is alive
). S'il est trouvé, il renverra le code d'erreur 0. Au cas où il ne serait pas trouvé, j'imprime le message entier et je renvoie le code d'erreur 1.Extrait étendu:
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
Explication : J'utilise des secrets de docker au lieu de variables env (mais cela peut également être réalisé avec des variables d'environnement régulières). L'utilisation de
$$
est pour littéral$
signe qui est supprimé lorsqu'il est passé au conteneur.Sortie de
docker inspect --format "{{json .State.Health }}" db | jq
à diverses occasions:Tout va bien:
{ "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 n'est pas (encore) en place:
{ "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" } ] }
Mauvais mot de passe:
{ "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" } ] }
la source
REDÉMARRER EN CAS D'ÉCHEC
Depuis la v3
condition: service_healthy
n'est plus disponible. L'idée est que le développeur doit implémenter un mécanisme de récupération après incident dans l'application elle-même. Cependant, pour les cas d'utilisation simples, un moyen simple de résoudre ce problème consiste à utiliser l'restart
option.Si l'état du service mysql provoque votre application,
exited with code 1
vous pouvez utiliser l'une desrestart
options de stratégie disponibles. par exemple,on-failure
la source