Docker Compose の depends_on の使い方まとめ
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 の各サービスに対して設定できる項目です。
名前が示唆するとおり depends_on
を使うとサービス間の依存関係を指定できます。
たとえば次のように書くと service_b
を service_a
に依存させることができます:
version: "3"
services:
service_a:
image: busybox
service_b:
image: busybox
# `service_b` を `service_a` に依存させる
depends_on:
- service_a
このように設定すると docker-compose
コマンドを実行したときに次のような制御が行われます:
docker-compose up
:service_a
→service_b
の順に起動するdocker-compose run
: (docker-compose up
と同じ)docker-compose stop
:serivce_b
→service_a
の順に停止する
depends_on
の指定パターン
depends_on
は次の 2 つのパターンで指定できます。
- Short syntax (リスト形式)
- Long syntax (オブジェクト形式)
Short syntax (リスト形式)
依存先のサービス名を記述するだけのシンプルな形式です。
たとえば次のように設定したサービスは service_a
という名前のサービスに依存することになります:
depends_on:
- service_a
Long syntax (オブジェクト形式)
依存先のサービス名に加えてそれぞれの条件を指定できる形式です。
たとえば次のように設定したサービスは service_a
に依存することになります:
depends_on:
service_a:
condition: service_started
条件の指定には condition
を使用します。
採りうる選択肢は次の 3 つです:
service_started
service_healthy
service_completed_successfully
condition
にはデフォルト値がありません。
Long syntax を使う場合は必ず condition
の値を指定する必要があります。
以下説明が長くなるので先にまとめですが、それぞれ次のような挙動になります:
選択肢 | 説明 |
---|---|
service_started |
依存先のサービスが起動したら起動する |
service_healthy |
依存先のサービスが起動して、なおかつ、 healthcheck が通ったら起動する |
service_completed_successfully |
依存先のサービスが正常終了したら起動する |
以下細かく見ていきます。
1. service_started
service_started
は依存先のサービスが「起動したこと」を条件とするものです。
condition: service_started
次のように書くと、 service_b
は service_a
が起動した後に起動します:
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
を使うときに注意すべきポイントは、 service_started
は依存先のサービスが「起動」しさえすれば依存元のサービスが起動される点です。
依存先のサービスのメインとなる処理の「待ち受け状態が整う」まで待つことはしてくれません。
そのため、ウェブアプリケーションとデータベースのように、依存元のサービスが正常に動作するために依存先のサービスが正しく応答することが不可欠な場合に service_started
では問題になることがあります。
たとえば次のように書くと、この挙動が問題になることが確認できます:
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_b
から service_a
への wget
リクエストが失敗してしまいます。
なお、 short syntax (リスト形式)で使ったときの挙動は、 long syntax (オブジェクト形式)で 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_started
の場合と異なり、 service_healthy
では次のようなときに対象のサービスは起動しません。
- 依存先サービスの 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_started
や service_healthy
とは少し毛色・使いどころが違います。
service_started
や service_healthy
の場合依存先のサービスは「稼働し続けるもの」である必要がありますが、 service_completed_successfully
の場合は「必要な処理を終えたら自動的に終了するもの」でなくてはなりません。
次のように書くと、 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 でも使いたい」という考えからリクエストがあり追加されたようです。
参考:
- 1.29.0 | Docker Compose release notes | Docker Documentation
- Feature Request: Init Containers for Docker Compose · Issue #6855 · docker/compose · GitHub
- Add a dependsOn condition to wait for successful completion by mikesir87 · Pull Request #134 · compose-spec/compose-spec · GitHub
- Add init container support by ojab · Pull Request #8122 · docker/compose · GitHub
ということで Docker Compose の depends_on
のまとめでした。
正確な仕様は compose-spec
を参照してください。
参考
compose-spec/spec.md
at master ·compose-spec/compose-spec
· GitHubdepends_on
| Compose file version 3 reference | Docker Documentation
compose-spec
の spec.md
が最も正確なドキュメントのようです。