Compose における起動順の制御

サービスの起動順は、depends_on オプションを使って制御することができます。 Compose では必ず依存順に応じて、コンテナーの起動を行いますが、この依存順とは depends_onlinksvolumes_fromnetwork_mode:"サービス:..." によって決定します。

しかし起動時の場合、Compose はコンテナーが "準備状態" になって初めて制御を待ちます。 (これがアプリケーションにとってどのような意味になるかには無関係です。) つまり稼動していることが必要です。 これには十分な理由があります。

たとえばデータベースが準備状態になるまで待ち続けたとすると、分散システムにおいては非常に大きな問題となります。 本番環境であれば利用不能となって、すぐにホストを切り替えなければならなくなります。 アプリケーションは、このような状況に柔軟に対応できるものでなくてはなりません。

こういったことを取り扱う際には、データベースへの接続に失敗した後に、接続を再度確立するようにアプリケーションを設計しておくことが必要です。 アプリケーションが再接続を行えば、そのうちデータベースへの接続が成功します。

最適な方法は、再接続をアプリケーションコード内で行うことです。 これは起動時にも行い、さらに何らかの理由で接続が断たれた際にも行います。 もっともそれほどの柔軟性を必要としないのであれば、以下のようなラッパースクリプトを使ってこの問題を回避する方法もあります。

  • wait-for-itdockerize 、あるいはシェル互換の wait-for を利用します。 これは非常に小さなラッパースクリプトです。 これをアプリケーションイメージに含めて、指定されたホストが TCP 接続を受け入れるまでの間、指定ポートに問い合わせを行うようにすることができます。

たとえば wait-for-it.sh または wait-for を使って、サービスコマンドをラップするには以下のようにします。

version:"2"services:web:build:.ports:-"80:8000"depends_on:-"db"command:["./wait-for-it.sh","db:5432","--","python","app.py"]db:image:postgres

ちなみに

この解決方法には限界があります。 たとえば指定するサービスが、本当に準備状態であるかどうかは確認できません。 コマンドにさらに引数を追加して bashshift を利用し、ループによって対処するのが次の例です。

  • 別の方法として、独自にラッパースクリプトを用意して、アプリケーション特有のヘルスチェックを実現することも考えられます。 たとえば、Postgres が完全に準備状態になって、コマンドを受け付けるようになるまで待ちたいとするなら、以下のスクリプトを用意します。

#!/bin/bash# wait-for-postgres.shset -ehost="$1"shiftcmd="$@"until psql -h "$host" -U "postgres" -c '\l';do >&2echo"Postgres is unavailable - sleeping" sleep 1done>&2echo"Postgres is up - executing command"exec$cmd

このラッパースクリプトを先の例において利用するには、以下のように設定します。

command:["./wait-for-postgres.sh","db","python","app.py"]

Compose ドキュメント

参考

Controlling startup order in Compose

https://docs.docker.com/compose/startup-order/