Docker-mysql接続の準備ができているかどうかのcomposeチェック


95

dbコンテナーが開始され、接続を受け入れる準備ができるまで、アプリコンテナーが移行/開始を実行しないようにしようとしています。

そこで、ヘルスチェックを使用することにし、dockercomposeファイルv2のオプションに依存します。

アプリでは、私は以下を持っています

app:
    ...
    depends_on:
      db:
      condition: service_healthy

一方、dbには次のヘルスチェックがあります

db:
  ...
  healthcheck:
    test: TEST_GOES_HERE
    timeout: 20s
    retries: 10

私は次のようないくつかのアプローチを試しました:

  1. dbDIRが作成されていることを確認する test: ["CMD", "test -f var/lib/mysql/db"]
  2. mysqlバージョンの取得: test: ["CMD", "echo 'SELECT version();'| mysql"]
  3. 管理者にpingを実行します(dbコンテナーを正常としてマークしますが、有効なテストではないようです) test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]

誰かがこれに対する解決策を持っていますか?


DB用のDockerを作成しましたか?アプリケーションの健全性のために、データはこのコンテナの外にあることを教えてください
Jorge Campos 2017年

または、少なくともこれはテストコンテナです。
ホルヘカンポス2017年

これは、実際には開発/テストのみを目的としています。
John Kariuki 2017年

2
私はあなたが接続してMySQLでクエリを実行するためにコマンドを使用する必要があると思いますが、サンプルのどれもこれを行うません:ような何かを:mysql -u USER -p PASSWORD -h MYSQLSERVERNAME -e 'select * from foo...' database-name
ホルヘ・カンポス

1
@JorgeCamposわかりました。ありがとうございます。通常、dbコンテナーがありますが、データディレクトリのボリュームをマップします。そのため、コンテナがダウンした場合、データは次のインスタンス化まで保持されます。
S ..

回答:


85
version: "2.1"
services:
    api:
        build: .
        container_name: api
        ports:
            - "8080:8080"
        depends_on:
            db:
                condition: service_healthy
    db:
        container_name: db
        image: mysql
        ports:
            - "3306"
        environment:
            MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
            MYSQL_USER: "user"
            MYSQL_PASSWORD: "password"
            MYSQL_DATABASE: "database"
        healthcheck:
            test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
            timeout: 20s
            retries: 10

apiコンテナーは、dbコンテナーが正常になるまで(基本的にはmysqladminが起動して接続を受け入れるまで)開始されません。


12
mysqladmin pingサーバーは実行されているが、まだ接続を受け入れていない場合、誤検知を返します。
halfpastfour.am 2017年

55
2017年の人々への参考までに:バージョン3以降conditiondepends_onはアンダーはサポートされていません
Mint

@BobKruithof私は同じ問題に直面しています...スリープや再試行の終了ステータスなどの回避策はありますか
Mukesh Agarwal 2017

1
@dKenはstackoverflow.com/a/45058879/279272の下の私の答えを参照してください、私はそれがあなたのためにも働くことを願っています。
ムケシュアガワル2017

1
パスワードを使用してこれを確認するには:test: ["CMD", 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', '-p$$MYSQL_ROOT_PASSWORD' ]-セクションで定義MYSQL_ROOT_PASSWORDした場合environments
ライミソン

23

あなたがドッキングウィンドウ-コンを使用している場合はV3 +をconditionの選択肢としてdepends_onされています削除

推奨パスではなく使用することがあるwait-for-itdockerizeまたはwait-for。あなたにはdocker-compose.yml、ファイル、であるためにあなたのコマンドを変更します。

command: sh -c 'bin/wait-for db:3306 -- bundle exec rails s'

wait-forアルパインコンテナで実行できるので、個人的には好みます(sh互換性があり、依存しませんbash)。欠点は、に依存netcatすることです。したがって、使用する場合はnetcat、コンテナにインストールされていることを確認するか、次のようにDockerfileにインストールしてください。

RUN apt-get -q update && apt-get -qy install netcat

またwait-forプロジェクトをフォークして、正常なHTTPステータスを確認できるようにしました(を使用しますwget)。次に、次のようなことを行うことができます。

command: sh -c 'bin/wait-for http://api/ping -- jest test'

PS:PRもマージして、その容量をwait-forプロジェクトに追加する準備ができています。


15

これで十分です

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

2
ダブル$は何ですか?
InsOp

5
環境変数をエスケープするためにヘルスチェックテストコマンドで使用する必要がある@InsOpの特別な構文は、$で始まります。つまり、$$ MYSQL_PASSWORDは$ MYSQL_PASSWORDになり、この具体的な例では、それ自体がmypasswordになります
Maksim Kostromin

それで、このimでコンテナ内のenv変数にアクセスしますか?単一の$Imがホストからenv変数にアクセスしているとしたら、私は思いますか?よろしくお願いします!
InsOp

10

mysqlの準備ができるのを待つようにコンテナを変更できる場合は、それを実行します。

データベースを接続するコンテナーを制御できない場合は、特定のポートを待機することができます。

そのために、小さなスクリプトを使用して、別のコンテナーによって公開されている特定のポートを待機しています。

この例では、myservermydbコンテナのポート3306に到達できるようになるのを待ちます。

# 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

スクリプトの待機ドキュメントはここにあります


wait-for-it.sh 以前に使用してみましたが、デフォルトのDockerfileを上書きしますか?entrypoint.shはどのように見えますか?
John Kariuki 2017

エントリポイントは画像によって異なります。docker inspect <imageid>で確認できます。これは、サービスが利用可能になるのを待って、エントリポイントを呼び出す必要があります。
nono 2017

大丈夫ですか ?わかりますか?
nono 2017年

理にかなっています。うん。
John Kariuki 2017年

6
警告:MySQL 5.5(おそらく新しいバージョンも)は初期化中に応答できます。
ブレイズ2018

8

docker-compose v2.1を使用した簡単なヘルスチェックのためにこんにちは、私は使用しました:

/usr/bin/mysql --user=root --password=rootpasswd --execute \"SHOW DATABASES;\"

基本的に は、データベースにパスワードを持つユーザーを例として使用して、簡単なmysqlコマンドSHOW DATABASES;を実行します。rootrootpasswd

コマンドが成功すると、データベースが起動して準備が整うため、ヘルスチェックパスが実行されます。interval間隔を置いてテストするために使用できます。

可視性のために他のフィールドを削除すると、次のようになります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

1
警告:作成ファイルの「バージョン3」では、「条件」のサポートは利用できなくなりました。docs.docker.com/compose/compose-file/#depends_on
BartoszK

1
コマンド機能は、wait-for-it.shスクリプトと一緒に使用する必要があります。私はこのようにしています:command: ["/home/app/jswebservice/wait-for-it.sh", "maria:3306", "--", "node", "webservice.js"]
BartoszK 2018年

@BartoszKIはそれを理解していません。詳細を含む完全な回答を追加していただけますか?私はまったく同じ問題に直面していますが、それを機能させることはできません。
タデウアントニオフェレイラメロ

v2.1を使用していることを確認してください。それ以外の場合は、v3.0以降の新しいガイドラインに従ってください。
Sylhare

1
--execute \"SHOW DATABASES;\"データベースがアクセスするアプリケーションのために利用可能になるまで、それは私のために待って作ったものである
tsuz

6

docker-compose.yml次の例のようにを変更しましたが、機能しました。

  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

私の場合../schemaAndSeedData、複数のスキーマとデータシードSQLファイルが含まれています。Design your own check script次のようになりますselect * from LastSchema.LastDBInsert

Web依存のコンテナコードは

depends_on:
  mysql:
    condition: service_healthy

これはあなたのために働くかもしれませんが、これがすべてのMySQLエンジンでサポートされているかどうかはわかりません。
halfpastfour.am 2017

InnoDB、MyISAMなどのデータベースエンジンについて話していますLastSchema.LastDBInsert。MySQLのデフォルトまたはデータベースエンジン固有ですか?
halfpastfour.am 2017

いいえ、mysqlのデフォルトでもありません。それは単なるサンプルでした。ダミークエリ。
ムケシュアガワル2017

5
警告:作成ファイルの「バージョン3」では、「条件」のサポートは利用できなくなりました。docs.docker.com/compose/compose-file/#depends_on
BartoszK

4

ヘルスチェックアプローチの更新されたソリューションを追加します。単純なスニペット:

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

説明mysqladmin ping誤検知を返すので(特に間違ったパスワードの場合)、出力を一時変数に保存してから、を使用grepして期待される出力を見つけます(mysqld is alive)。見つかった場合は、0エラーコードを返します。見つからない場合は、メッセージ全体を出力し、1つのエラーコードを返します。

拡張スニペット:

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

説明:env変数の代わりにdockerシークレットを使用しています(ただし、これは通常のenv変数でも実現できます)。の使用は、コンテナに渡されるときに削除される$$リテラル$記号用です。

docker inspect --format "{{json .State.Health }}" db | jqさまざまな場面からの出力:

すべて大丈夫:

{
  "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は(まだ)稼働していません:

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

間違ったパスワード:

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

3

同じ問題がありました。この目的のために外部bashスクリプトを作成しました(マキシムの回答に触発されています)。mysql-container-nameMySQLコンテナの名前に置き換えます。また、パスワード/ユーザーが必要です。

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

MakeFileでは、docker-composeアップコールの直後にこのスクリプトを呼び出します。

wait-for-mysql: ## Wait for MySQL to be ready
    bin/wait-for-mysql.sh

run: up wait-for-mysql reload serve ## Start everything...

次に、エラーなしで他のコマンドを呼び出すことができます。

ドライバで例外が発生しました:SQLSTATE [HY000] [2006] MySQLサーバーがなくなりました

出力例:

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.

このアプローチでは役に立たなくなったため、ヘルスチェックを削除しました。


2

再起動-失敗

v3condition: service_healthyは使用できなくなったため。開発者は、アプリ自体にクラッシュリカバリのメカニズムを実装する必要があるという考え方です。ただし、単純なユースケースの場合、この問題を解決する簡単な方法はrestartオプションを使用することです。

mysqlサービスステータスが原因でアプリケーションが発生するexited with code 1場合は、使用restart可能なポリシーオプションの1つを使用できます。例えば、on-failure

version: "3"

services:

    app:
      ...
      depends_on:
        - db:
      restart: on-failure
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.