Compose における起動、停止順の制御

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

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

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

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

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

  • wait-for-itdockerize、シェル互換の wait-forRelayAndContainers テンプレートを利用します。 これは非常に小さなラッパースクリプトです。 これをアプリケーションイメージに含めて、指定されたホストが 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/sh
    # wait-for-postgres.sh
    
    set -e
    
    host="$1"
    shift
    
    until PGPASSWORD=$POSTGRES_PASSWORD psql -h "$host" -U "postgres" -c '\q'; do
      >&2 echo "Postgres is unavailable - sleeping"
      sleep 1
    done
    
    >&2 echo "Postgres is up - executing command"
    exec "$@"
    

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

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

Compose ドキュメント

documentation, docs, docker, compose, startup, shutdown, order