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 07737305depends_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_aLong syntax (オブジェクト形式)
依存先のサービス名に加えてそれぞれの条件を指定できる形式です。
たとえば次のように設定したサービスは service_a に依存することになります:
depends_on:
service_a:
condition: service_started条件の指定には condition を使用します。
採りうる選択肢は次の 3 つです:
service_startedservice_healthyservice_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 ... doneservice_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 | HiConnection refused ということで、最初の 1 回だけ service_b から service_a への wget リクエストが失敗してしまいます。
なお、 short syntax (リスト形式)で使ったときの挙動は、 long syntax (オブジェクト形式)で service_started を指定したときと同じ挙動になります。
2. service_healthy
service_healthy は依存先のサービスが起動してなおかつ healthcheck がパスすることを条件とするものです。
condition: service_healthyhealthcheck をパスするかどうかのチェックが必要になるので、 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 ... doneservice_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_healthy3. service_completed_successfully
service_completed_successfully は依存先のサービスが正常に終了したことを条件とするものです。
condition: service_completed_successfullyservice_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.mdat master ·compose-spec/compose-spec· GitHubdepends_on| Compose file version 3 reference | Docker Documentation
compose-spec の spec.md が最も正確なドキュメントのようです。