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-name
Respuestas:
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 ping
devolverá un falso positivo si el servidor se está ejecutando pero aún no acepta conexiones.condition
menosdepends_on
no es compatible con la versión 3+test: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ]
- si lo definióMYSQL_ROOT_PASSWORD
en laenvironments
sección.Si está utilizando docker-compose v3 + , se ha eliminado
condition
una opción de .depends_on
La ruta recomendada es utilizar en lugar
wait-for-it
,dockerize
owait-for
. En sudocker-compose.yml
archivo, cambie su comando para que sea:command: sh -c 'bin/wait-for db:3306 -- bundle exec rails s'
Personalmente lo prefiero,
wait-for
ya que puede funcionar en un contenedor Alpine (sh
compatible, sin dependenciabash
). El inconveniente es que depende denetcat
, por lo que si decides usarlo, asegúrate de haberlonetcat
instalado en el contenedor, o instálalo en tu Dockerfile, por ejemplo con:RUN apt-get -q update && apt-get -qy install netcat
También bifurqué el
wait-for
proyecto 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-for
proyecto.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_PASSWORD
fuente
$
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.sh
Puede encontrar la documentación del script wait-for-it aquí
fuente
wait-for-it.sh
anteriormente 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
mysql
comando simpleSHOW DATABASES;
usando como ejemplo el usuarioroot
con la contraseñarootpasswd
en 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
interval
para 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_healthy
fuente
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.yml
segú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 ' LastSchema
En mi caso,
../schemaAndSeedData
contiene varios archivos SQL de siembra de datos y esquemas.Design your own check script
puede ser similar a seguirselect * from LastSchema.LastDBInsert
.Mientras que el código de contenedor dependiente de la web era
depends_on: mysql: condition: service_healthy
fuente
LastSchema.LastDBInsert
un 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-name
por 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 done
En mi MakeFile, llamo a este script justo después de mi
docker-compose
llamada: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_healthy
ya 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 utilizarrestart
option.Si el estado del servicio mysql hace que su aplicación se
exited with code 1
apague, puede usar una derestart
las opciones de política disponibles. p.ej,on-failure
fuente
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 ping
devuelve falsos positivos (especialmente para una contraseña incorrecta), estoy guardando la salida en una variable temporal y luego la usogrep
para 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_password
Explicació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 | jq
en 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