Dockerコンテナが稼働するのを待つにはどうすればよいですか?


84

コンテナ内でサービスを実行する場合、たとえばmongodb、コマンド

docker run -d myimage

すぐに終了し、コンテナIDを返します。CIスクリプトでは、mongoコンテナーを実行した直後に、クライアントを実行してmongodb接続をテストします。問題は、サービスがまだ起動していないため、クライアントが接続できないことです。sleep 10スクリプトにbigを追加する以外に、コンテナーが稼働するのを待つオプションがありません。

Dockerにはwait、コンテナーが存在しないため、その場合は機能しないコマンドがあります。Dockerの制限ですか?

回答:


50

docker1.12の同様の問題でコメントされているように

HEALTHCHECKdocker / docker#23218に従って、サポートがアップストリームにマージされます。これは、次の順序で開始する前に、コンテナーが正常であるかどうかを判断するために考慮できます。

これは、docker 1.12rc3(2016-07-14)以降で使用できます。

docker-compose特定の条件を待機する機能をサポートする過程にあります。

これはlibcompose(dockerインタラクションを再構築する必要がないように)使用し、このための一連の構成コマンドを追加します。ここでそれをチェックしてください:https//github.com/dansteen/managed-compose

次のようにDockerfileで使用できます。

HEALTHCHECK --interval=5m --timeout=3s \
  CMD curl -f http://localhost/ || exit 1

公式ドキュメント:https//docs.docker.com/engine/reference/builder/#/healthcheck


私はこれが最高投票になることを期待していました。しかし、それから私はそれがごく最近答えられたことを発見しました。
Shiplu Mokaddim 2018

53

この簡単な解決策を見つけて、もっと良いものを探していましたが、運がありません...

until [ "`/usr/bin/docker inspect -f {{.State.Running}} CONTAINERNAME`"=="true" ]; do
    sleep 0.1;
done;

または、コンテナーが正常であると報告されるまで待機する場合(ヘルスチェックがある場合)

until [ "`/usr/bin/docker inspect -f {{.State.Health.Status}} CONTAINERNAME`"=="healthy" ]; do
    sleep 0.1;
done;

4
代わりにwhileループを使用する1つのライナーwhile [ "`docker inspect -f {{.State.Health.Status}} $container_id`" != "healthy" ]; do sleep 2; done
Mouath 2018

注意点として、dockerはosxの/ usr / local / bin / dockerにあります。スクリプトをクロスプラットフォームにするために$(どのDocker)を追加する価値があるでしょうか?
–con

@ con--確かに、それは改善になるでしょうが、私は「クロスプラットフォームスクリプト」の文書化を質問の範囲に対する外部の関心事と考えています。それでもなお、編集をしたい場合は、それを行ってください:)
スーパーヒーロー

これは私と一緒に働いた方法です:#!/ bin / bash until /usr/bin/docker inspect -f {{.State.Running}} local_mysql== true $ do sleep 0.1; 完了。echo "mysql is on"
M.Hefny

@ M.Hefnyそれは答えで表現されているのと同じ例です。
スーパーヒーロー

32

コンテナをリンクする予定で、テストのために複数のインスタンスを実行している可能性がある場合のように、ポートを公開したくない場合は、これが1行で行うのに適した方法であることがわかりました:)この例はElasticSearchの準備が整うのを待つことに基づく:

docker inspect --format '{{ .NetworkSettings.IPAddress }}:9200' elasticsearch | xargs wget --retry-connrefused --tries=5 -q --wait=3 --spider

これには、Ubuntuの標準であるwgetが利用可能である必要があります。接続が拒否された場合でも、5回、3秒の試行間隔で再試行し、何もダウンロードしません。


--waitretry=3代わりに使用したいと思います--wait=3
jpbochi 2015

好奇心旺盛なwgetのmanページ --wait=seconds Wait the specified number of seconds between the retrievals.--waitretry=seconds If you don't want Wget to wait between every retrieval, but only between retries of failed downloads, you can use this option. Wget will use linear backoff, waiting 1 second after the first failure on a given file, then waiting 2 seconds after the second failure on that file, up to the maximum number of seconds you specify.
目的のない2016年

25

開始したコンテナー化されたサービスがcurlまたはwgetリクエストに必ずしも適切に応答しない場合(これは多くのサービスで発生する可能性が非常に高い)、nc代わりに使用できます。

これは、Postgresコンテナを開始し、それが利用可能になるのを待ってから続行するホストスクリプトのスニペットです。

POSTGRES_CONTAINER=`docker run -d --name postgres postgres:9.3`
# Wait for the postgres port to be available
until nc -z $(sudo docker inspect --format='{{.NetworkSettings.IPAddress}}' $POSTGRES_CONTAINER) 5432
do
    echo "waiting for postgres container..."
    sleep 0.5
done

編集-この例では、Dockerによって割り当てられたコンテナの「プライベート」IPアドレスにアクセスするため、テストしているポートを公開する必要はありません。ただし、これは、Dockerホストデーモンがループバック(127.xxx)をリッスンしている場合にのみ機能します。たとえば、Macを使用してboot2docker VMを実行している場合、Macシェルからコンテナの「プライベート」IPアドレスにルーティングできないため、この方法を使用できません。


--formatこの返信以降、オプションが変更されたと思います。現在機能しているのはdocker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' [NAME|ID...]docs.docker.com/engine/reference/commandline/inspectの例を参照)です。
カートピーク2017

16

MongoDBサーバーのホスト+ポートを知っていると仮定すると(を使用した-linkか、を挿入したため-e)、curlMongoDBサーバーが実行されて接続を受け入れているかどうかを確認するために使用できます。

次のスニペットは、成功するまで毎秒接続を試みます。

#!/bin/sh
while ! curl http://$DB_PORT_27017_TCP_ADDR:$DB_PORT_27017_TCP_PORT/
do
  echo "$(date) - still trying"
  sleep 1
done
echo "$(date) - connected successfully"

1
ただし、ホストのポートをバインドする必要があります:(
Gravis 2014年

同様の問題があります。pidfilesを使用してmonitを設定しようとして、手動でvarを挿入せずにDockerの開始/停止で詳細なイベントをトリガーできないのは面倒です。つまり、汎用ラッパーを簡単に作成することはできません。
Alex Lynham 2014

あなたが行くことができますIP=$(docker inspect -f '{{ .NetworkSettings.IPAddress }}' mysql)(「MySQLは」名またはコンテナIDである)あなたのmysqlのコンテナのIPアドレスを取得して、URLを交換します:http://$IP:3306。私のために働く!
Danyel 2014

12

私は次のようなものになってしまいました:

#!/bin/bash

attempt=0
while [ $attempt -le 59 ]; do
    attempt=$(( $attempt + 1 ))
    echo "Waiting for server to be up (attempt: $attempt)..."
    result=$(docker logs mongo)
    if grep -q 'waiting for connections on port 27017' <<< $result ; then
      echo "Mongodb is up!"
      break
    fi
    sleep 2
done

10

そこに私自身の解決策を投げる:

Dockerネットワークを使用しているため、Markのnetcatトリックが機能せず(ホストネットワークからのアクセスがない)、Erikのアイデアがpostgresコンテナーでは機能しません(postgresがまだ実行されていなくてもコンテナーは実行中としてマークされます)接続可能)。だから私はループ内のエフェメラルコンテナを介してpostgresに接続しようとしています:

#!/bin/bash

docker network create my-network
docker run -d \
    --name postgres \
    --net my-network \
    -e POSTGRES_USER=myuser \
    postgres

# wait for the database to come up
until docker run --rm --net my-network postgres psql -h postgres -U myuser; do
    echo "Waiting for postgres container..."
    sleep 0.5
done

# do stuff with the database...

それが今日私たちがしていることです。サーバーは再起動する前に一度起動するため、postgresイメージに注意してください...
Gravis 2016年

これは、いくつかの小さな変更を加えるだけでうまく機能します。1.postgresホストが解決されないので、docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' postgres代わりに使用します。2.psqlパスワードが必要ですpg_isreadyが、より適しています。3.でpg_isready必要もありません-U myuser
AlecMev18年

2

test/test_runner

#!/usr/bin/env ruby

$stdout.sync = true

def wait_ready(port)
  until (`netstat -ant | grep #{port}`; $?.success?) do
    sleep 1
    print '.'
  end
end

print 'Running supervisord'
system '/usr/bin/supervisord'

wait_ready(3000)

puts "It's ready :)"

$ docker run -v /tmp/mnt:/mnt myimage ruby mnt/test/test_runner

ポートがリッスンしているかどうかをこのようにテストしています。この場合、コンテナー内からテストを実行していますが、mongodbの準備ができているかどうかに関係なく、外部からテストを実行することもできます。

$ docker run -p 37017:27017 -d myimage

そして、ポート37017がホストコンテナからリッスンしているかどうかを確認します。


2

私はこれにしっかりと取り組み、アイデアを思いついた。このタスクの調査をしているときに私はここに着いたので、この投稿の将来の訪問者と私の解決策を共有したいと思いました。

Docker-composeベースのソリューション

docker-composeを使用している場合は、docker同期POCを確認できます。私は他の質問でいくつかのアイデアを組み合わせました(そのおかげで-賛成)。

基本的な考え方は、コンポジット内のすべてのコンテナーが診断サービスを公開することです。このサービスを呼び出すと、必要なポートのセットがコンテナで開いているかどうかが確認され、コンテナの全体的なステータスが返されます(POCに従ってWARMUP / RUNNING)。各コンテナには、起動時に依存サービスが稼働しているかどうかを確認するユーティリティもあります。その後、コンテナが起動します。

docker-compose環境の例では、server1server2の2つのサービスがあり、両方のサーバーが起動するのを待ってから両方にリクエストを送信して終了するクライアントサービスがあります。

POCからの抜粋

wait_for_server.sh

#!/bin/bash

server_host=$1
sleep_seconds=5

while true; do
    echo -n "Checking $server_host status... "

    output=$(echo "" | nc $server_host 7070)

    if [ "$output" == "RUNNING" ]
    then
        echo "$server_host is running and ready to process requests."
        break
    fi

    echo "$server_host is warming up. Trying again in $sleep_seconds seconds..."
    sleep $sleep_seconds
done

複数のコンテナを待っています:

trap 'kill $(jobs -p)' EXIT

for server in $DEPENDS_ON
do
    /assets/wait_for_server.sh $server &
    wait $!
done

診断サービスの基本的な実装(checkports.sh):

#!/bin/bash

for port in $SERVER_PORT; do
    nc -z localhost $port;

    rc=$?

    if [[ $rc != 0 ]]; then
        echo "WARMUP";
        exit;
    fi
done

echo "RUNNING";

診断サービスをポートに配線する:

nc -v -lk -p 7070 -e /assets/checkports.sh

興味深いアプローチ。+1
VonC 2017

1

wait-for-it、 "使用できます。これは、ホストとTCPポートの可用性を待機する純粋なbashスクリプトです。リンクされたDockerコンテナーなど、相互に依存するサービスのスピンアップを同期するのに役立ちます。純粋なbashスクリプトであり、外部依存関係はありません

ただし、このようなサービス間の相互依存を回避するようにサービスを設計する必要があります。サービスはデータベースへの再接続を試みることができますか?データベースに接続できない場合にコンテナーを停止させ、コンテナーオーケストレーター(Docker Swarmなど)に任せることはできますか?



0

Docker-composeソリューション

docker-composeの後、dockerコンテナの名前がわからないので、

docker inspect -f {{.State.Running}} $(docker-compose ps -q <CONTAINER_NAME>)

そしてtrueここのようにチェックします https://stackoverflow.com/a/33520390/7438079


0

mongoDB dockerインスタンスの場合、これを実行し、チャームのように機能します。

#!/usr/bin/env bash

until docker exec -i ${MONGO_IMAGE_NAME} mongo -u ${MONGO_INITDB_ROOT_USERNAME} -p ${MONGO_INITDB_ROOT_PASSWORD}<<EOF
exit
EOF
do
    echo "Waiting for Mongo to start..."
    sleep 0.5
done
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.