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

ちなみに

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

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

#!/bin/bash
# wait-for-postgres.sh

set -e

host="$1"
shift
cmd="$@"

until psql -h "$host" -U "postgres" -c '\l'; do
  >&2 echo "Postgres is unavailable - sleeping"
  sleep 1
done

>&2 echo "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/