我如何知道我的 docker mysql 容器何时启动并且 mysql 已准备好接受查询?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25503412/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 12:59:41  来源:igfitidea点击:

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

mysqlbashshelldocker

提问by haren

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.

我正在部署几个不同的 docker 容器,mysql 是第一个。我想在数据库启动后立即运行脚本并继续构建其他容器。该脚本一直失败,因为它试图在设置 mysql(来自此官方 mysql 容器)的入口点脚本仍在运行时运行。

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.

有没有办法等待在 docker 容器内完成的入口点 mysql 安装脚本的信号?Bash sleep 似乎是一个次优的解决方案。

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.

编辑:去寻找这样的 bash 脚本。不是最优雅和最蛮力的,但就像魅力一样。也许有人会发现这很有用。

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

回答by flamemyst

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:

您可以安装 mysql-client 包并使用 mysqladmin 来 ping 目标服务器。在使用多个 docker 容器时很有用。结合 sleep 并创建一个简单的等待循环:

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

回答by Adam

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

这个小 bash 循环等待 mysql 打开,不需要安装任何额外的包:

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

回答by Andrew Savinykh

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 equivalentin the Dockerfile.

Dockerfile 中也有一个等价物

With that command your docker psand docker inspectwill show you health status of your container. For mysql in particular this method has the advantage of mysqladminbeing available inside the container, so you do not need to install it on the docker host.

与该命令你docker ps,并docker inspect会告诉你你的容器的健康状况。特别是对于 mysql,这种方法的优点mysqladmin是可以在容器内使用,因此您无需将其安装在 docker 主机上。

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.

然后您可以简单地在 bash 脚本中循环以等待状态变为健康。以下 bash 脚本由Dennis创建。

function getContainerHealth {
  docker inspect --format "{{.State.Health.Status}}" 
}

function waitContainer {
  while STATUS=$(getContainerHealth ); [ $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.

并且您的脚本将等到容器启动并运行。如果容器变得不健康,脚本将退出,这是可能的,例如 docker 主机内存不足,因此 mysql 无法为自己分配足够的内存。

回答by alejandropg

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 mysqlo a mysql clientin your host machine, but really you already have it inside the Docker container, so I prefer to use something like this:

其他解决方案要求您在主机中安装mysqloa mysql 客户端,但实际上您已经在 Docker 容器中安装了它,所以我更喜欢使用这样的东西:

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

回答by Matt Kramer

I've found that using the mysqladmin pingapproach 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:

我发现使用这种mysqladmin ping方法并不总是可靠的,尤其是当你要建立一个新的数据库时。在这种情况下,即使您能够 ping 服务器,如果用户/权限表仍在初始化,您也可能无法连接。相反,我执行以下操作:

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 pinganswers.

到目前为止,我还没有遇到这种方法的任何问题。我看到 VinGarcia 在对其中一个mysqladmin ping答案的评论中提出了类似的建议。

回答by ctatro85

So I am not sure if any one has posted this. It doesn't look like any one has, so... there is a command in mysqladmin that features a wait, it handles testing of the connection, then retries internally and returns a success upon completion.

所以我不确定是否有人发布过这个。它看起来不像任何人,所以...... mysqladmin 中有一个命令具有等待功能,它处理连接测试,然后在内部重试并在完成后返回成功。

sudo docker run --name mysql -e MYSQL_ROOT_PASSWORD=MY_ROOT_PASS -p 3306:3306 -d mysql
mysqladmin ping -h 127.0.0.1 -u root --password=MY_ROOT_PASS --wait=30 && mysql -h 127.0.0.1 -P 3306 -u root --password=MY_ROOT_PASS < MY_SQL_SCRIPT.sql

The important piece is mysqladmin ping -h 127.0.0.1 -u root --password=MY_ROOT_PASS --wait=30 -vwith the --waitbeing the flag to wait until the connection is successful and the number being the amount of attempts to retry.

的重要的部分是mysqladmin ping -h 127.0.0.1 -u root --password=MY_ROOT_PASS --wait=30 -v--wait作为标记等到连接成功和数为尝试重试的量。

Ideally you would run that command from inside the docker container, but I didn't want to modify the original posters command too much.

理想情况下,您应该从 docker 容器内部运行该命令,但我不想过多地修改原始海报命令。

When used in my make file for initialization

在我的 make 文件中用于初始化时

db.initialize: db.wait db.initialize


db.wait:
  docker-compose exec -T db mysqladmin ping -u $(DATABASE_USERNAME) -p$(DATABASE_PASSWORD) --wait=30 --silent

db.initialize:
  docker-compose exec -T db mysql -u $(DATABASE_USERNAME) -p$(DATABASE_PASSWORD) $(DATABASE_NAME) < dev/sql/base_instance.sql

回答by developer10214

The following health-check works for all my mysql containers:

以下运行状况检查适用于我的所有 mysql 容器:

db:
    image: mysql:5.7.16
    healthcheck:
      test: ["CMD-SHELL", 'mysql --database=$$MYSQL_DATABASE --password=$$MYSQL_ROOT_PASSWORD --execute="SELECT count(table_name) > 0 FROM information_schema.tables;" --skip-column-names -B']
      interval: 30s
      timeout: 10s
      retries: 4
    extends:
        file: docker-compose-common-config.yml
        service: common_service

回答by Fabian Andres Merchan Jimenez

I had the same issue when my Django container tried to connect the mysql container just after it started. I solved it using the vishnubob's wait-for.it.shscript. Its a shell script which waits for an IP and a host to be ready before continuing. Here is the example I use for my applicaction.

当我的 Django 容器在启动后尝试连接 mysql 容器时,我遇到了同样的问题。我使用 vishnubob 的wait-for.it.sh脚本解决了它。它是一个 shell 脚本,它在继续之前等待 IP 和主机准备就绪。这是我用于我的应用程序的示例。

./wait-for-it.sh \
    -h $(docker inspect --format '{{ .NetworkSettings.IPAddress }}' $MYSQL_CONTAINER_NAME) \
    -p 3306 \
    -t 90

In that script I'm asking to the mysql container to wait maximum 90 seconds (it will run normally when ready) in the port 3306 (default mysql port) and the host asigned by docker for my MYSQL_CONTAINER_NAME. The script have more variables but for mw worked with these three.

在该脚本中,我要求 mysql 容器在端口 3306(默认 mysql 端口)和 docker 为我的 MYSQL_CONTAINER_NAME 分配的主机中等待最多 90 秒(准备好后将正常运行)。该脚本有更多变量,但 mw 使用了这三个变量。

回答by Marcelo Barros

I developed a new solution for this issue based on a new approach. All approaches I found rely on a script that tries over and over to connect to the database, or try to establish a TCP connection with the container. The full details can be found on the waitdbrepository, but, my solution is to rely on the retrieved log from the container. The script waits until the log fires the message ready for connections. The script can identify if the container is starting for the first time. In this case the script waits until the initial database script is executed and the database is restarted, waiting again for a new ready for connectionsmessage. I tested this solution on MySQL 5.7 and MySQL 8.0.

我基于一种新方法为这个问题开发了一个新的解决方案。我发现的所有方法都依赖于反复尝试连接到数据库或尝试与容器建立 TCP 连接的脚本。可以在waitdb存储库中找到完整的详细信息,但是,我的解决方案是依赖从容器中检索到的日志。脚本等待,直到日志触发消息准备好连接。该脚本可以识别容器是否是第一次启动。在这种情况下,脚本会一直等到初始数据库脚本执行完毕并重新启动数据库,再次等待新的连接就绪消息。我在 MySQL 5.7 和 MySQL 8.0 上测试了这个解决方案。

The script itself (wait_db.sh):

脚本本身(wait_db.sh):

#!/bin/bash

STRING_CONNECT="mysqld: ready for connections"

findString() {
    ( logs -f       2>&1 | grep -m  "" &) | grep -m  "" > /dev/null
}

echo "Waiting startup..."
findString  "$STRING_CONNECT" 1      
 logs     2>&1 | grep -q "Initializing database"
if [ $? -eq 0 ] ; then
    echo "Almost there..."
    findString  "$STRING_CONNECT" 2      
fi
echo "Server is up!"

The script can be used in Docker Compose or in Docker itself. I hope the examples bellow make the the usage clear:

该脚本可用于 Docker Compose 或 Docker 本身。我希望下面的示例使用法清晰:

Example 01: Using with Docker Compose

示例 01:与 Docker Compose 一起使用

SERVICE_NAME="mysql" && \
docker-compose up -d $SERVICE_NAME && \
./wait_db.sh docker-compose --no-color $SERVICE_NAME

Example 02: Using with Docker

示例 02:与 Docker 一起使用

CONTAINER_NAME="wait-db-test" && \
ISO_NOW=$(date -uIs) && \
  docker run --rm --name $CONTAINER_NAME \
    -e MYSQL_ROOT_PASSWORD=$ROOT_PASSWORD \
    -d mysql:5.7 && \
./wait_db.sh docker --since "$ISO_NOW" $CONTAINER_NAME

Example 3: A full example (the test-case)

示例 3:完整示例(测试用例)

A full example can be found on the test case of the repository. This test-case will startup a new MySQL, create a dummy database, wait until everything is started and then fires a selectto check if everything goes fine. After that it'll going restart the container and wait it to be started and then fires a new selectto check if it's ready for connection.

可以在存储库的测试用例中找到完整示例。这个测试用例将启动一个新的 MySQL,创建一个虚拟数据库,等待一切启动,然后触发一个选择来检查一切是否正常。之后它将重新启动容器并等待它启动,然后触发一个新的选择以检查它是否准备好连接。

回答by Erdin? ?orbac?

I use the following code ;

我使用以下代码;

export COMPOSE_PROJECT_NAME=web;

导出 COMPOSE_PROJECT_NAME=web;

export IS_DATA_CONTAINER_EXISTS=$(docker volume ls | grep ${COMPOSE_PROJECT_NAME}_sqldata);

export IS_DATA_CONTAINER_EXISTS=$(docker volume ls | grep ${COMPOSE_PROJECT_NAME}_sqldata);

docker-compose up -d;
docker-compose ps;

export NETWORK_GATEWAY=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.Gateway}}{{end}}' ${COMPOSE_PROJECT_NAME}_webserver1_con);