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
Mysql-clientパッケージをインストールし、mysqladminを使用してターゲットサーバーにpingを実行できます。複数のdockerコンテナを使用する場合に便利です。スリープと組み合わせて、単純な待機ループを作成します。
while ! mysqladmin ping -h"$DB_Host" --silent; do
sleep 1
done
この小さな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
これは他の回答へのコメントで多かれ少なかれ言及されましたが、それはそれ自身のエントリに値すると思います。
まず、次の方法でコンテナを実行できます。
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はそれ自体に十分な量を割り当てることができません。
ポートに関する問題は、ポートが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
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によって同様の何かが提案されたことがわかります。
次のヘルスチェックは、すべての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
私の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
私は、新しいアプローチに基づいてこの問題の新しいソリューションを開発しました。私が見つけたすべてのアプローチは、データベースへの接続を何度も試行するスクリプトに依存するか、コンテナとの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自体で使用できます。以下の例が使用法を明確にすることを願っています。
SERVICE_NAME="mysql" && \
docker-compose up -d $SERVICE_NAME && \
./wait_db.sh docker-compose --no-color $SERVICE_NAME
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
完全な例は リポジトリのテストケースで にあります。このテストケースは、新しいMySQLを起動し、ダミーデータベースを作成し、すべてが開始されるまで待機してから、selectを起動して、すべてが正常に実行されるかどうかを確認します。その後、コンテナを再起動し、開始されるのを待ってから、新しいselectを起動して、接続の準備ができているかどうかを確認します。
Mysqlコンテナを待機しているdockerコンテナがpythonイメージに基づいている場合(たとえばDjango application) 、以下のコードを使用できます。
利点は次のとおりです。
コード:
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()
使用法:
wait-for-mysql-db.py
たとえば)アプリケーションのソースコード内。startup.py
たとえば)最初に上記のコードを実行し、その後アプリケーションを起動します。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をマージできると思います。
私は次のコードを使用します。
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);
ENTRYPOINT
スクリプトで、有効なMySQL接続があるかどうかを確認する必要があります。
このソリューションでは、コンテナにMySQLクライアントをインストールする必要はありません。また、
php:7.0-fpm
runningnc
もオプションではありませんでした。インストールする必要があったためです。また、ポートが開いているかどうかをチェックすることは、必ずしもサービスが実行され、正しく公開されていることを意味しません。 [この詳細]
そのため、このソリューションでは、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接続があるまでコンテナのブートストラップロジックを本質的にブロックします。