Docker Compose の depends_on の使い方まとめ

Docker

Docker Compose の depends_on の使い方をかんたんにまとめました。

主に自分用のまとめですが、このあたりは公式ドキュメントがあまり充実していないので、 Docker Compose をよく使う方のお役にも立つのではないかと思います。

前提

Compose ファイル( docker-compose.yml )のバージョン 3.8 を前提としています。

動作確認には次のバージョンを使用しました:

$ docker --version
Docker version 20.10.5, build 55c4c88

$ docker-compose --version
docker-compose version 1.29.0, build 07737305

depends_on とは

depends_on は Docker Compose の各サービスに対して設定できる項目です。 名前が示唆するとおりサービス間の依存関係を指定できます。

たとえば次のように書くと service_bservice_a に依存させることができます:

version: "3"

services:
  service_a:
    image: busybox
  service_b:
    image: busybox
    # `service_b` を `service_a` に依存させる
    depends_on:
      - service_a

このように設定すると、 docker-compose updocker-compose rundocker-compose stop を実行したときに次のような制御が行われます:

  • docker-compose up: service_aservice_b の順に起動する
  • docker-compose run: ( docker-compose up と同じ)
  • docker-compose stop: serivce_bservice_a の順に停止する

depends_on の指定パターン

depends_on は次の 2 つのパターンで指定できます。

  • Short syntax (リスト)
  • Long syntax (オブジェクト)

Short syntax (リスト)

こちらはシンプルな方法で、依存先のサービス名を単純に記述するだけです。 たとえば次のように設定したサービスは service_a に依存することになります:

depends_on:
  - service_a

Long syntax (オブジェクト)

こちらは少し複雑な方法で、依存関係にあるサービスをオブジェクト形式で記述するものです。

この方法では依存先に加えて「条件」を指定することができます:

depends_on:
  service_a:
    condition: service_started

条件の指定には condition を使用します。 採りうる選択肢は次の 3 つです:

  1. service_started
  2. service_healthy
  3. service_completed_successfully

condition にデフォルト値は無いため、 long syntax を選ぶ場合は必ず指定する必要があります。

以下長いので先にまとめですが、それぞれ次のような意味合いになっています:

選択肢 説明
service_started 依存先のサービスが起動したら起動する
service_healthy 依存先のサービスが起動して、なおかつ、 healthcheck が通ったら起動する
service_completed_successfully 依存先のサービスが正常終了したら起動する

以下細かく見ていきます。

1. service_started

service_started は、依存先のサービスが「起動したこと」を条件とします。

condition: service_started

次のように書くと、 service_a の起動後に service_b が起動します:

version: "3"

services:
  service_a:
    image: busybox
  service_b:
    image: busybox
    depends_on:
      service_a:
        condition: service_started

実行結果:

$ docker-compose up -d

Creating network "PROJECT_default" with the default driver
Creating PROJECT_service_a_1 ... done
Creating PROJECT_service_b_1 ... done

service_started で注意すべきポイントは、依存先のサービスが起動したら OK であって、そのメインの処理が「待ち受け状態が整ったこと」を確約するものではない点です。 この挙動は、依存元のサービスが正常な動作をするために依存先のサービスの利用が不可欠な場合に問題になることがあります。

たとえば、次のように書くと、この挙動が問題になりうることが確認できます:

version: "3"

services:
  # service_a: わざと 5 秒待機した後にウェブサーバーを立ち上げる
  service_a:
    image: busybox
    command:
      - sh
      - -c
      - |
        set -ex
        sleep 5
        echo 'Hi' > /var/www/index.html
        httpd -f -p 80 -h /var/www
    ports:
      - "80:80"
  # service_b: service_a に対してリクエストをかける
  # 最初の 1 回だけリクエストが失敗してしまう
  service_b:
    image: busybox
    command:
      - sh
      - -c
      - |
        while true; do
          wget -O- -q http://service_a:80
          sleep 5
        done
    depends_on:
      service_a:
        condition: service_started

この設定で docker-compose up を実行すると次のようになります:

$ docker-compose up

Creating network "PROJECT_default" with the default driver
Creating PROJECT_service_a_1 ... done
Creating PROJECT_service_b_1 ... done
Attaching to PROJECT_service_a_1, PROJECT_service_b_1
service_b_1  | wget: can't connect to remote host (192.168.240.2): Connection refused
service_a_1  | + sleep 5
service_a_1  | + echo Hi
service_a_1  | + httpd -f -p 80 -h /var/www
service_b_1  | Hi
service_b_1  | Hi

Connection refused ということで、最初の 1 回だけ service_bwget のリクエストが失敗することが確認できます。

なお、 depends_on をリスト形式で指定した場合の挙動は、この service_started を選んだ場合と同じになります。

2. service_healthy

service_healthy は依存先のサービスが起動してなおかつ healthcheck が通ることを条件とするものです。

condition: service_healthy

healthcheck をパスすることが条件なので、 service_healthy を利用する場合は依存先のサービスで healthcheck の設定をしておく必要があります。

参考: healthcheck | Compose file version 3 reference | Docker Documentation

次のように書くと、 service_a の healthcheck が通った後に service_b が起動します:

version: "3"

services:
  service_a:
    image: busybox
    # かんたんに状態を Up に保つためのに httpd を使用する
    command: httpd -f
    healthcheck:
      test: exit 0
      # すぐに結果が出るようにタイミングを調整する
      interval: 1s
      timeout: 1s
      retries: 3
      start_period: 1s
  service_b:
    image: busybox
    depends_on:
      service_a:
        condition: service_healthy

実行結果:

$ docker-compose up -d

Creating network "PROJECT_default" with the default driver
Creating PROJECT_service_a_1 ... done
Creating PROJECT_service_b_1 ... done

service_healthy では service_started の場合とは異なり、次のような場合には対象のサービスは起動しません。

  • healthcheck が通らなかった場合
  • 依存先サービスの処理が終了してコンテナが停止した場合

たとえば次のように書くと、 service_a は正常に動いていますが healthcheck が通らないので service_b は起動しません:

version: "3"

services:
  service_a:
    image: busybox
    # かんたんに状態を Up に保つためのに httpd を使用する
    command: httpd -f
    # healthckeck が通らないようにあえて exit 1 を実行する
    healthcheck:
      test: exit 1
      # すぐに結果が出るようにタイミングを調整する
      interval: 1s
      timeout: 1s
      retries: 3
      start_period: 1s
  service_b:
    image: busybox
    depends_on:
      service_a:
        condition: service_healthy

この設定で docker-compose up -d を実行した後に docker-compose ps を実行すると、 service_a の state 判定が Up (unhealthy) となっていること、そして、 service_b が起動していなかったことが確認できます:

$ docker-compose ps

       Name          Command        State        Ports
------------------------------------------------------
PROJECT_service_a_1   httpd -f   Up (unhealthy)

また、次のように書いた場合は、 service_a が( sh を実行して)すぐに停止してしまうので、こちらも service_b は起動しません:

version: "3"

services:
  service_a:
    image: busybox
    healthcheck:
      test: exit 0
      # すぐに結果が出るようにタイミングを調整する
      interval: 1s
      timeout: 1s
      retries: 3
      start_period: 1s
  service_b:
    image: busybox
    depends_on:
      service_a:
        condition: service_healthy

3. service_completed_successfully

service_completed_successfully は依存先のサービスが正常に終了したことを条件とするものです。

condition: service_completed_successfully

service_completed_successfully の場合は依存先のサービスが「終了すること」が必要なので service_startedservice_healthy とは毛色・使いどころがかなり異なります。

次のように書くと、 service_a が起動してすぐに停止した後に service_b が起動します:

version: "3"

services:
  # service_a: すぐに正常終了させる
  service_a:
    image: busybox
    command: ["sh", "-c", "exit 0"]
  service_b:
    image: busybox
    depends_on:
      service_a:
        condition: service_completed_successfully

次のように書くと、 service_a が異常終了するので service_b は起動しません:

version: "3"

services:
  # service_a: 異常終了させる
  service_a:
    image: busybox
    command: ["sh", "-c", "exit 1"]
  service_b:
    image: busybox
    depends_on:
      service_a:
        condition: service_completed_successfully

また、次のように書くと、 service_a が終了しないのでいくら待っても service_b は起動してきません:

version: "3"

services:
  # `service_a` を終了させない
  service_a:
    image: busybox
    command: ["sh", "-c", "while true; do echo Hello; sleep 5; done"]
  service_b:
    image: busybox
    depends_on:
      service_a:
        condition: service_completed_successfully

ちなみに、この service_completed_successfully は 2021 年 2 月に仕様として追加 & 実装された比較的新しい機能です。 docker-compose ではバージョン 1.29.0 ( 2021/04/06 リリース)以降で使えるようになっています。 「 Kubernetes の Init Containers に似た機能を Docker Compose でも使いたい」という考えからリクエストがあり追加されたようです。

参考:

ということで Docker Compose の depends_on のまとめでした。 正確な仕様は compose-spec を参照してください。

参考

compose-specspec.md が最も正確なドキュメントのようです。


アバター
後藤隼人 ( ごとうはやと )GitHubPython

ウェブ制作・開発やマーケティング、プロジェクト支援などをしています。