web-dev-qa-db-ja.com

Docker mysqlコンテナが起動し、mysqlがクエリを受け取る準備ができたことを知るにはどうすればよいですか?

Mysqlが最初のコンテナーであるいくつかの異なるdockerコンテナーをデプロイしています。データベースが起動したらすぐにスクリプトを実行し、他のコンテナの構築に進みたいと思います。 mysqlをセットアップするエントリポイントスクリプト( this official mysql container から)がまだ実行されていたときに実行しようとしていたため、スクリプトは失敗していました。

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

Dockerコンテナー内でentrypoiny mysqlセットアップスクリプトが終了するシグナルを待つ方法はありますか? Bashスリープは、次善のソリューションのようです。

編集:このような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
49
haren

Mysql-clientパッケージをインストールし、mysqladminを使用してターゲットサーバーにpingを実行できます。複数のdockerコンテナを使用する場合に便利です。スリープと組み合わせて、単純な待機ループを作成します。

while ! mysqladmin ping -h"$DB_Host" --silent; do
    sleep 1
done
43
flamemyst

この小さな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
31
Adam

これは他の回答へのコメントで多かれ少なかれ言及されましたが、それはそれ自身のエントリに値すると思います。

まず、次の方法でコンテナを実行できます。

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

Dockerfileには 同等 もあります。

そのコマンドを使用すると、docker psおよびdocker inspectは、コンテナのヘルスステータスを表示します。特にmysqlの場合、このメソッドにはmysqladminがコンテナー内で利用可能であるという利点があるため、インストールする必要はありません。ドッカーホスト。

その後、bashスクリプトをループするだけで、ステータスが正常になるのを待つことができます。次のbashスクリプトは Dennis によって作成されます。

function getContainerHealth {
  docker inspect --format "{{json .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"
}

これで、スクリプトでこれを実行できます。

waitContainer mysql

コンテナが起動して実行されるまでスクリプトは待機します。コンテナが異常になった場合、たとえば、Docker Hostのメモリが不足している可能性がある場合、スクリプトは終了します。そのため、mysqlはそれ自体に十分な量を割り当てることができません。

23
Andrew Savinykh

ポートに関する問題は、ポートがopenである可能性があるが、データベースの準備がまだ整っていないことです。

他のソリューションでは、ホストマシンにmysql oa mysql clientをインストールする必要がありますが、実際には既に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
8
alejandropg

mysqladmin pingアプローチを使用することは、特に新しいDBを立ち上げる場合は特に、必ずしも信頼できるとは限りません。その場合、サーバーにpingを実行できたとしても、ユーザー/特権テーブルがまだ初期化されていると接続できない場合があります。代わりに、次のようなことをします。

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

これまでのところ、この方法で問題は発生していません。 mysqladmin ping回答の1つに対するコメントの中で、VinGarciaによって同様の何かが提案されたことがわかります。

5
Matt Kramer

次のヘルスチェックは、すべての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
4
developer10214

私のDjangoコンテナが起動直後にmysqlコンテナに接続しようとしたときに同じ問題が発生しました。vishnubobの wait-for.it.sh スクリプトを使用して解決しました。続行する前にIPとホストの準備が整うまで待機するシェルスクリプトです。

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

このスクリプトでは、ポート3306(デフォルトのmysqlポート)およびMYSQL_CONTAINER_NAMEのdockerによって割り当てられたホストで、最大90秒待機するようにmysqlコンテナーに要求しています(準備ができたら正常に実行されます)。スクリプトにはさらに多くの変数がありますが、MWではこれら3つで機能しました。

だから誰かがこれを投稿したかどうかはわかりません。誰も持っていないように見えるので、... 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

重要な部分はmysqladmin ping -h 127.0.0.1 -u root --password=MY_ROOT_PASS --wait=30 -v とともに --waitは、接続が成功するまで待機するフラグであり、数値は再試行の試行回数です。

理想的には、そのコマンドをdockerコンテナー内から実行しますが、元のポスターコマンドをあまり変更したくありませんでした。

初期化のためにメイクファイルで使用する場合

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
2
ctatro85

私は、新しいアプローチに基づいてこの問題の新しいソリューションを開発しました。私が見つけたすべてのアプローチは、データベースへの接続を何度も試行するスクリプトに依存するか、コンテナとのTCP接続を確立しようとします。完全な詳細は waitdb repository、しかし、私の解決策は、コンテナから取得したログに依存することです。スクリプトは、ログが接続の準備ができたメッセージまで待機します。スクリプトは、コンテナが初めて起動するかどうかを識別できますこの場合、スクリプトは、最初のデータベーススクリプトが実行され、データベースが再起動されるまで待機し、新しいを再度待機しますconnectionsメッセージの準備ができました。このソリューションをMySQL 5.7およびMySQL 8.0でテストしました。

スクリプト自体(wait_db.sh):

#!/bin/bash

STRING_CONNECT="mysqld: ready for connections"

findString() {
    ($1 logs -f $4 $5 $6 $7 $8 $9 2>&1 | grep -m $3 "$2" &) | grep -m $3 "$2" > /dev/null
}

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

このスクリプトは、Docker ComposeまたはDocker自体で使用できます。以下の例が使用法を明確にすることを願っています。

例01:Docker Composeで使用する

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

例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

例3:完全な例(テストケース)

完全な例は リポジトリのテストケースで にあります。このテストケースは、新しいMySQLを起動し、ダミーデータベースを作成し、すべてが開始されるまで待機してから、selectを起動して、すべてが正常に実行されるかどうかを確認します。その後、コンテナを再起動し、開始されるのを待ってから、新しいselectを起動して、接続の準備ができているかどうかを確認します。

1
Marcelo Barros

Mysqlコンテナを待機しているdockerコンテナがpythonイメージに基づいている場合(たとえばDjango application) 、以下のコードを使用できます。

利点は次のとおりです。

  • wait-for-it.sh に基づいていないため、mysqlのIPとポートの準備が完了するまで待機しますが、これはmysqlの初期化が終了したことを自動的に意味するものではありません。
  • コンテナに存在する必要があるmysqlまたはmysqladmin実行可能ファイルに基づくシェルスクリプトではありません。コンテナはpythonイメージに基づいているため、そのイメージの上にmysqlをインストールする必要があります。以下のソリューションでは、コンテナに既に存在するテクノロジーである純粋なPythonを使用します。

コード:

import time

import pymysql


def database_not_ready_yet(error, checking_interval_seconds):
    print('Database initialization has not yet finished. Retrying over {0} second(s). The encountered error was: {1}.'
          .format(checking_interval_seconds,
                  repr(error)))
    time.sleep(checking_interval_seconds)


def wait_for_database(Host, port, db, user, password, checking_interval_seconds):
    """
    Wait until the database is ready to handle connections.

    This is necessary to ensure that the application docker container
    only starts working after the MySQL database container has finished initializing.

    More info: https://docs.docker.com/compose/startup-order/ and https://docs.docker.com/compose/compose-file/#depends_on .
    """
    print('Waiting until the database is ready to handle connections....')
    database_ready = False
    while not database_ready:
        db_connection = None
        try:
            db_connection = pymysql.connect(Host=host,
                                            port=port,
                                            db=db,
                                            user=user,
                                            password=password,
                                            charset='utf8mb4',
                                            connect_timeout=5)
            print('Database connection made.')
            db_connection.ping()
            print('Database ping successful.')
            database_ready = True
            print('The database is ready for handling incoming connections.')
        except pymysql.err.OperationalError as err:
            database_not_ready_yet(err, checking_interval_seconds)
        except pymysql.err.MySQLError as err:
            database_not_ready_yet(err, checking_interval_seconds)
        except Exception as err:
            database_not_ready_yet(err, checking_interval_seconds)
        finally:
            if db_connection is not None and db_connection.open:
                db_connection.close()

使用法:

  1. このコードをpythonファイル(wait-for-mysql-db.pyたとえば)アプリケーションのソースコード内。
  2. 別のpython script(startup.pyたとえば)最初に上記のコードを実行し、その後アプリケーションを起動します。
  3. アプリケーションコンテナのDockerfileがこれら2つのpythonスクリプトとアプリケーションのソースコードをDockerイメージにパックしていることを確認してください。
  4. Docker-composeファイルで、次のようにアプリケーションコンテナを設定します:command: ["python3", "startup.py"]

このソリューションは、MySQLデータベース用に作成されていることに注意してください。別のデータベースに少し適合させる必要があります。

https://github.com/docker-library/mysql/blob/master/5.7/docker-entrypoint.sh docker-entrypoint.shは、カスタマイズされた.sqlのマージをまだサポートしていません。

Mysqlインスタンスの準備ができたら実行できるように、docker-entrypoint.shを変更してSQLをマージできると思います。

0
kmsheng

私は次のコードを使用します。

export COMPOSE_PROJECT_NAME = web;

export IS_DATA_CONTAINER_EXISTS = $(ドッカーボリューム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);
0

ENTRYPOINTスクリプトで、有効なMySQL接続があるかどうかを確認する必要があります。

このソリューションでは、コンテナにMySQLクライアントをインストールする必要はありません。また、php:7.0-fpm running ncもオプションではありませんでした。インストールする必要があったためです。また、ポートが開いているかどうかをチェックすることは、必ずしもサービスが実行され、正しく公開されていることを意味しません。 [この詳細]

そのため、このソリューションでは、PHPスクリプトを実行して、MySQLコンテナが接続を取得できるかどうかを確認する方法を示します。私のコメント ここ

ファイルentrypoint.sh

#!/bin/bash
cat << EOF > /tmp/wait_for_mysql.php
<?php
\$connected = false;
while(!\$connected) {
    try{
        \$dbh = new pdo( 
            'mysql:Host=mysql:3306;dbname=db_name', 'db_user', 'db_pass',
            array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)
        );
        \$connected = true;
    }
    catch(PDOException \$ex){
        error_log("Could not connect to MySQL");
        error_log(\$ex->getMessage());
        error_log("Waiting for MySQL Connection.");
        sleep(5);
    }
}
EOF
php /tmp/wait_for_mysql.php
# Rest of entry point bootstrapping

これを実行することにより、有効なMySQL接続があるまでコンテナのブートストラップロジックを本質的にブロックします。

0
Starx