Estoy tratando de asegurarme de que el contenedor de mi aplicación no ejecute migraciones / inicio hasta que el contenedor db se inicie y esté LISTO PARA aceptar conexiones.
Así que decidí usar la verificación de estado y depende de la opción en el archivo de composición de la ventana acoplable v2.
En la aplicación, tengo lo siguiente
app:
...
depends_on:
db:
condition: service_healthy
La base de datos, por otro lado, tiene la siguiente verificación de estado
db:
...
healthcheck:
test: TEST_GOES_HERE
timeout: 20s
retries: 10
He probado un par de enfoques como:
- asegurándose de que se crea el db DIR
test: ["CMD", "test -f var/lib/mysql/db"] - Obteniendo la versión de mysql:
test: ["CMD", "echo 'SELECT version();'| mysql"] - Haga ping al administrador (marca el contenedor de la base de datos como saludable pero no parece ser una prueba válida)
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
¿Alguien tiene una solución para esto?
mysql
docker
docker-compose
dockerfile
John Kariuki
fuente
fuente

mysql -u USER -p PASSWORD -h MYSQLSERVERNAME -e 'select * from foo...' database-nameRespuestas:
El contenedor api no se iniciará hasta que el contenedor db esté en buen estado (básicamente hasta que mysqladmin esté activo y aceptando conexiones).
fuente
mysqladmin pingdevolverá un falso positivo si el servidor se está ejecutando pero aún no acepta conexiones.conditionmenosdepends_onno es compatible con la versión 3+test: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ]- si lo definióMYSQL_ROOT_PASSWORDen laenvironmentssección.Si está utilizando docker-compose v3 + , se ha eliminado
conditionuna opción de .depends_onLa ruta recomendada es utilizar en lugar
wait-for-it,dockerizeowait-for. En sudocker-compose.ymlarchivo, cambie su comando para que sea:command: sh -c 'bin/wait-for db:3306 -- bundle exec rails s'Personalmente lo prefiero,
wait-forya que puede funcionar en un contenedor Alpine (shcompatible, sin dependenciabash). El inconveniente es que depende denetcat, por lo que si decides usarlo, asegúrate de haberlonetcatinstalado en el contenedor, o instálalo en tu Dockerfile, por ejemplo con:RUN apt-get -q update && apt-get -qy install netcatTambién bifurqué el
wait-forproyecto para que pueda verificar el estado de HTTP saludable (usawget). Entonces puedes hacer algo como eso:command: sh -c 'bin/wait-for http://api/ping -- jest test'PD: Un RP también está listo para fusionarse para agregar esa capacidad al
wait-forproyecto.fuente
Esto debería ser suficiente
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_PASSWORDfuente
$de?$estoy accediendo a la variable env desde el host, ¿entonces supongo? eso es lindo gracias!Si puede cambiar el contenedor para esperar a que mysql esté listo, hágalo.
Si no tiene el control del contenedor al que desea conectar la base de datos, puede intentar esperar el puerto específico.
Para ese propósito, estoy usando un pequeño script para esperar un puerto específico expuesto por otro contenedor.
En este ejemplo, myserver esperará a que se pueda acceder al puerto 3306 del contenedor mydb .
# 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.shPuede encontrar la documentación del script wait-for-it aquí
fuente
wait-for-it.shanteriormente pero anula el Dockerfile predeterminado, ¿verdad? ¿Cómo se ve el archivo entrypoint.sh?Hola para una simple verificación de estado usando docker-compose v2.1 , usé:
Básicamente, ejecuta un
mysqlcomando simpleSHOW DATABASES;usando como ejemplo el usuariorootcon la contraseñarootpasswden la base de datos.Si el comando tiene éxito, la base de datos está activa y lista, por lo que la ruta de verificación de estado. Puede usarlo
intervalpara que pruebe a intervalos.Eliminando el otro campo para visibilidad, así es como se vería en su
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_healthyfuente
command: ["/home/app/jswebservice/wait-for-it.sh", "maria:3306", "--", "node", "webservice.js"]--execute \"SHOW DATABASES;\"es lo que me hizo esperar hasta que la base de datos estuviera disponible para que la aplicaciónModifiqué
docker-compose.ymlsegún el siguiente ejemplo y funcionó.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 ' LastSchemaEn mi caso,
../schemaAndSeedDatacontiene varios archivos SQL de siembra de datos y esquemas.Design your own check scriptpuede ser similar a seguirselect * from LastSchema.LastDBInsert.Mientras que el código de contenedor dependiente de la web era
depends_on: mysql: condition: service_healthyfuente
LastSchema.LastDBInsertun motor de base de datos predeterminado o específico de MySQL?Tuve el mismo problema, creé un script bash externo para este propósito (está inspirado en la respuesta de Maxim). Reemplazar
mysql-container-namepor el nombre de su contenedor MySQL y también se necesita contraseña / usuario: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 doneEn mi MakeFile, llamo a este script justo después de mi
docker-composellamada:wait-for-mysql: ## Wait for MySQL to be ready bin/wait-for-mysql.sh run: up wait-for-mysql reload serve ## Start everything...Entonces puedo llamar a otros comandos sin tener el error:
Ejemplo de salida:
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.He eliminado la verificación de estado, ya que ahora es inútil con este enfoque.
fuente
REINICIAR EN CASO DE FALLO
Dado que v3
condition: service_healthyya no está disponible. La idea es que el desarrollador debería implementar un mecanismo para la recuperación de fallos dentro de la propia aplicación. Sin embargo, para casos de uso simples, una forma sencilla de resolver este problema es utilizarrestartoption.Si el estado del servicio mysql hace que su aplicación se
exited with code 1apague, puede usar una derestartlas opciones de política disponibles. p.ej,on-failurefuente
Añadiendo una solución actualizada para el enfoque de chequeo de salud. Fragmento 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; }Explicación : Dado que
mysqladmin pingdevuelve falsos positivos (especialmente para una contraseña incorrecta), estoy guardando la salida en una variable temporal y luego la usogreppara encontrar la salida esperada (mysqld is alive). Si lo encuentra, devolverá el código de error 0. En caso de que no se encuentre, estoy imprimiendo el mensaje completo y devolviendo el código de error 1.Fragmento extendido:
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_passwordExplicación : Estoy usando secretos de Docker en lugar de variables env (pero esto también se puede lograr con vars env regulares). El uso de
$$es para un$signo literal que se elimina cuando se pasa al contenedor.Salida de
docker inspect --format "{{json .State.Health }}" db | jqen varias ocasiones:Todo 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 no está activo (todavía):
{ "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" } ] }Contraseña incorrecta:
{ "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" } ] }fuente