How do I know when my docker mysql container is up and mysql is ready for taking queries?

75,124

Solution 1

You can install mysql-client package and use mysqladmin to ping target server. Useful when working with multiple docker container. Combine with sleep and create a simple wait-loop:

while ! mysqladmin ping -h"$DB_HOST" --silent; do
    sleep 1
done

Solution 2

This little bash loop waits for mysql to be open, shouldn't require any extra packages to be installed:

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

Solution 3

This was more or less mentioned in comments to other answers, but I think it deserves its own entry.

First of all you can run your container in the following manner:

docker run --name mysql --health-cmd='mysqladmin ping --silent' -d mysql

There is also an equivalent in the Dockerfile.

With that command your docker ps and docker inspect will show you health status of your container. For mysql in particular this method has the advantage of mysqladmin being available inside the container, so you do not need to install it on the docker host.

Then you can simply loop in a bash script to wait on the status to become healthy. The following bash script is created by Dennis.

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"
}

Now you can do this in your script:

waitContainer mysql

and your script will wait until the container is up and running. The script will exit if the container becomes unhealthy, which is possible, if for example docker host is out of memory, so that the mysql cannot allocate enough of it for itself.

Solution 4

I've found that using the mysqladmin ping approach isn't always reliable, especially if you're bringing up a new DB. In that case, even if you're able to ping the server, you might be unable to connect if the user/privilege tables are still being initialized. Instead I do something like the following:

while ! docker exec db-container mysql --user=foo --password=bar -e "SELECT 1" >/dev/null 2>&1; do
    sleep 1
done

So far I haven't encountered any problems with this method. I see that something similar was suggested by VinGarcia in a comment to one of the mysqladmin ping answers.

Solution 5

Some times the problem with the port is that the port could be open, but the database is not ready yet.

Other solutions require that you have installed the mysql o a mysql client in your host machine, but really you already have it inside the Docker container, so I prefer to use something like this:

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
Share:
75,124

Related videos on Youtube

haren
Author by

haren

https://linkedin.com/in/lukaszharezlak

Updated on July 08, 2022

Comments

  • haren
    haren almost 2 years

    I am deploying a few different docker containers, mysql being the first one. I want to run scripts as soon as database is up and proceed to building other containers. The script has been failing because it was trying to run when the entrypoint script, which sets up mysql (from this official mysql container), was still running.

    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
    

    Is there a way to wait for a signal of an entrypoiny mysql setup script finishing inside the docker container? Bash sleep seems like a suboptimal solution.

    EDIT: Went for a bash script like this. Not the most elegant and kinda brute force but works like a charm. Maybe someone will find that useful.

    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
    
    • fabrizioM
      fabrizioM over 9 years
      add a bash loop to check mysql service status ? I think that is as good as it can get.
    • haren
      haren over 9 years
      Thanks @fabrizioM. I went with this approach.
    • eexit
      eexit about 7 years
      this question has been answered here.
  • user2707671
    user2707671 almost 8 years
    Nice one. I used it as a 1 liner: until nc -z $CFG_MYSQL_HOST 3306; do sleep 1; echo "Waiting for DB to come up..."; done
  • Nathan Arthur
    Nathan Arthur over 7 years
    This is a beautiful thing. Works great for a Docker healthcheck, too: docker run --health-cmd='mysqladmin ping --silent' -d mysql
  • ronkot
    ronkot over 7 years
    To wait unit container is healthy I used script while [ $(docker inspect --format "{{json .State.Health.Status }}" <container-name>) != "\"healthy\"" ]; do printf "."; sleep 1; done
  • Max
    Max over 6 years
    I found this to be the best approach as it depends only on Docker and should hence have less cross-platform issues. Only thing I encountered is that the image entrypoint spins up mysqlserver twice - once bare, and once initialized. mysqladmin ping catches the first spin up as well, which is likely what you don't want. In my case, running a dummy query with the schema you expect to be there worked best, i.e. change health command to be mysql -u root -e "use your_schema;".
  • cweiske
    cweiske about 6 years
    This does not work here; mysqladmin ping succeeds before I can actually use the database - I guess the container is still running is initialization .sql scripts.
  • VinGarcia
    VinGarcia about 6 years
    This seems the best and shortest option, however just pinging was not working for me, it still wasn't ready afterwards so I used the command: mysql -u root -proot -e 'status' &> /dev/null instead of mysqladmin ping
  • Quolonel Questions
    Quolonel Questions over 5 years
    Just because the port is available does not mean the server is ready to accept connections. mysqladmin ping is the correct answer here.
  • engin
    engin over 5 years
    I didn't have mysqladmin installed in docker image, however, wget also did the job: while ! wget mysql:3306; do sleep 1 done
  • Jesse Chisholm
    Jesse Chisholm over 4 years
    This works if you have a healthcheck for your container. But the .State.Health.Status doesn't exist if you don't. You might have to settle for .State.Status instead; but that says running too soon for this OP's needs.
  • Andrew Savinykh
    Andrew Savinykh over 4 years
    @JesseChisholm huh? What do you think --health-cmd is for?
  • Luke
    Luke almost 4 years
    If you try it on localhost and it doesn't work, try "127.0.0.1". When "localhost" is used, mysqladmin is trying to connect using a socket instead over the default TCP-port 3306.
  • am70
    am70 over 3 years
    you can use healthcheck also with docker-compose docs.docker.com/compose/compose-file/#healthcheck
  • Michael P
    Michael P over 3 years
    usually containers are as minimal as possible and mysql is not available. @QuolonelQuestions you may be right for some rare cases. The nc solution is precised enough even for a predictable docker environment. I vote for the lightweight nc.
  • YetAnotherDuck
    YetAnotherDuck about 3 years
    This is the best solution by far. I would like to add that the container needs to have acces to curl. RUN apt-get update && apt-get upgrade -y && apt-get install curl -y
  • mrodo
    mrodo over 2 years
    When and/or how should a script like this be executed? In the Dockerfile? I don't understand...
  • vincent31337
    vincent31337 about 2 years
    Most elegant solution IMO.
  • ddrake12
    ddrake12 about 2 years
    This method was working great for me in scripts until I upgraded to MacOS Monterey (I think). Sadly now it seems not to work
  • Ben Slade
    Ben Slade almost 2 years
    Re: Database ping successful I think it really means "database server ping successful". The documentation for ping says it's checking if the connection is successful. It makes no difference what database you're connected to