Docker Composeのデペンデンシー管理とオーケストレーション
Docker Compose は、マルチコンテナアプリケーションの定義と管理において非常に強力であり、サービス間のデペンデンシー(依存関係)を宣言し、それらの起動とシャットダウンをオーケストレーションするメカニズムを提供します。本章では、Docker Compose がアプリケーションのデペンデンシーをどのように処理するか、また、各サービスが正しい順序で起動し、効率的に連携して動作することを保証するために提供される豊富なオーケストレーション機能について深く掘り下げます。
1. サービスデペンデンシーの定義
Docker Compose を使用すると、サービス間のデペンデンシーを明示的に定義でき、サービスが特定の順序で作成および起動されることを保証できます。これは、データベースの準備が整うまで待機する必要がある Web アプリケーションなど、特定のコンポーネントが実行されないと正常に動作しないアプリケーションにとって不可欠です。
docker-compose.yml ファイルでデペンデンシーを定義するには、主に2つのアプローチがあります。単独で depends_on を使用する方法と、healthcheck (ヘルスチェック)を depends_on と組み合わせて使用する方法です。
1.1 depends_on を使用した起動順序の制御
depends_on キーは、サービスの起動順序を決定するためのデペンデンシーを確立します。depends_on を使用すると、Compose は、デペンデンシーを宣言しているサービスよりも前に、依存されている側のサービスが起動することを保証します。同時に、サービスを停止または削除する際には、逆の順序で処理されることも保証します。
フロントエンド Web サーバー(Nginx など)、バックエンド API(Python Flask アプリなど)、およびデータベース(PostgreSQL など)を含むシンプルな Web アプリケーションを想像してください。バックエンド API はデータの読み書きのためにデータベースの実行を必要とし、Nginx フロントエンドはリクエストをプロキシするためにバックエンド API を必要とします。
以下は、depends_on をデモンストレーションする docker-compose.yml の例です:
version: '3.8'
services:
db:
image: postgres:13
environment:
POSTGRES_DB: mydatabase
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:
- db_data:/var/lib/postgresql/data
networks:
- app_network
backend:
build: ./backend # 'backend' ディレクトリに Dockerfile があると仮定
ports:
- "8000:8000"
environment:
DATABASE_URL: postgres://user:password@db:5432/mydatabase
depends_on:
- db # backend サービスは db サービスに依存する
networks:
- app_network
frontend:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- backend # frontend サービスは backend サービスに依存する
networks:
- app_network
networks:
app_network:
driver: bridge
volumes:
db_data:このコンフィギュレーションでは:
backendは、dbが起動するまで待機してから起動を開始します。frontendは、backendが起動するまで待機してから起動を開始します。
重要な注意事項: 単純な depends_on は、依存されているサービスのコンテナが「起動した」ことのみを保証します。コンテナ内部のアプリケーションの準備が整う、あるいは healthy な状態になるまで待機するわけではありません。例えば、db コンテナが稼働し始めると、db 内部の PostgreSQL サーバーがまだ初期化中で接続を受け付けられない状態であっても、backend は即座に起動してしまいます。これにより、バックエンドがデータベースの準備完了前に接続を試みてしまう「レースコンディション(Race Conditions)」が発生し、最終的に接続エラーやアプリケーションのクラッシュを引き起こす可能性があります。
1.2 condition と healthcheck を組み合わせたサービス状態の確証
上記のレースコンディション問題を解決するために、Compose はバージョン 3.4 から depends_on に condition オプションを導入しました。これにより、依存されているサービスが満たすべき条件を指定でき、その条件が満たされて初めて現在のサービスが起動するようになります。利用可能な条件には、service_started(起動済み、デフォルト値)、service_healthy(ヘルスチェック通過済み)、および service_completed_successfully(正常に完了)が含まれます。
service_healthy 条件は、healthcheck 定義と組み合わせて使用すると特に効果的です。healthcheck を使用するとコマンドを定義でき、Docker はコンテナ内部で定期的にそのコマンドを実行して、サービスが正常に稼働し続けているかどうかを判断します。
healthcheck と depends_on: condition: service_healthy を利用して、先ほどの例を最適化してみましょう:
version: '3.8'
services:
db:
image: postgres:13
environment:
POSTGRES_DB: mydatabase
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:
- db_data:/var/lib/postgresql/data
networks:
- app_network
healthcheck: # データベースサービス用のヘルスチェックを定義
test: ["CMD-SHELL", "pg_isready -U user -d mydatabase"]
interval: 5s
timeout: 5s
retries: 5
backend:
build: ./backend
ports:
- "8000:8000"
environment:
DATABASE_URL: postgres://user:password@db:5432/mydatabase
depends_on:
db:
condition: service_healthy # バックエンドは、db が healthy ステータスを報告するまで待機する
networks:
- app_network
healthcheck: # バックエンドサービス用のヘルスチェックを定義
test: ["CMD", "curl", "-f", "http://localhost:8000/health"] # ヘルスチェック用 API エンドポイントがあると仮定
interval: 10s
timeout: 5s
retries: 3
frontend:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
backend:
condition: service_healthy # フロントエンドは、バックエンドが healthy ステータスを報告するまで待機する
networks:
- app_network
networks:
app_network:
driver: bridge
volumes:
db_data:この更新された例では:
dbサービスにはhealthcheckがあり、pg_isreadyコマンドを使用して PostgreSQL が接続を受け付ける準備ができているかを確認します。backendサービスは、db サービスのヘルスチェックがhealthyステータスを報告するまで起動しません。backendサービスにも独自のhealthcheckがあり、frontendはそれを待機します。frontendサービスは、backendサービスがhealthyを報告するまで起動しません。
このアプローチにより、起動プロセスが極めて堅牢になり、サービスがまだ利用できない依存リソースに接続しにいくことを防ぎます。
1.3 仮定のシナリオ:マイクロサービス決済システム
決済処理システム用のマイクロサービスアーキテクチャを想像してみてください:
- payment-gateway (ペイメントゲートウェイ): 外部からの決済リクエストを処理し、トランザクションの詳細を保存する必要があります。
- transaction-database (トランザクションデータベース): すべてのトランザクション記録を保存します。
- fraud-detection-service (不正検知サービス): トランザクションに不審なアクティビティがないか分析します。
transaction-databaseのデータを必要とします。 - notification-service (通知サービス): 決済の成功または失敗のメール/SMS アラートを送信します。通知をトリガーするために
payment-gatewayに依存します。
version: '3.8'
services:
transaction-database:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: root_password
MYSQL_DATABASE: transactions
volumes:
- transaction_db_data:/var/lib/mysql
networks:
- payment_network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-proot_password"]
interval: 10s
timeout: 5s
retries: 5
fraud-detection-service:
build: ./fraud-detection
environment:
DB_HOST: transaction-database
DB_NAME: transactions
DB_USER: root
DB_PASSWORD: root_password
depends_on:
transaction-database:
condition: service_healthy
networks:
- payment_network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5001/health"] # 不正検知サービスにヘルスエンドポイントがあると仮定
interval: 15s
timeout: 5s
retries: 3
payment-gateway:
build: ./payment-gateway
ports:
- "8080:8080"
environment:
TRANSACTION_SERVICE_URL: http://fraud-detection-service:5001 # ゲートウェイは不正検知サービスと通信する
depends_on:
fraud-detection-service:
condition: service_healthy # ペイメントゲートウェイは不正検知サービスが healthy になるまで待機する
networks:
- payment_network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/status"] # ゲートウェイにステータスエンドポイントがあると仮定
interval: 10s
timeout: 5s
retries: 3
notification-service:
build: ./notification-service
environment:
GATEWAY_URL: http://payment-gateway:8080 # 通知サービスはゲートウェイのイベントをサブスクライブする可能性がある
depends_on:
payment-gateway:
condition: service_healthy # 通知サービスはイベントを受信するためにゲートウェイの稼働を必要とする
networks:
- payment_network
networks:
payment_network:
driver: bridge
volumes:
transaction_db_data:これにより、堅牢な起動シーケンスが確保されます:データベースが healthy になった後に不正検知サービスが起動し、不正検知サービスが healthy になった後にペイメントゲートウェイがリクエストの受付を開始し、ペイメントゲートウェイが正常に稼働した後に通知サービスが準備完了となります。
2. オーケストレーション機能の拡張
シンプルな起動順序の制御に加えて、Docker Compose はマルチコンテナアプリケーションを効果的に管理するためのいくつかの追加オーケストレーション機能を提供しています。
2.1 コンテナリスタートポリシー (Restart Policies)
リスタートポリシーは、コンテナが停止した際に Docker がどのように反応すべきかを規定します。これは、アプリケーションの可用性を維持し、障害から自動的にリカバリするために不可欠です。各サービスに対して restart キーを使用してリスタートポリシーを定義できます。
一般的な restart ポリシーの値:
no: コンテナを自動的に再起動しません(デフォルト値)。on-failure: コンテナが非ゼロのエグジットコード(エラーの発生を示す)で終了した場合にのみ再起動します。always: エグジットコードに関係なく、コンテナが停止した場合は常に再起動します。Docker デーモンが起動した際、デーモン停止時に実行中だったalways設定のコンテナも再起動されます。unless-stopped: 明示的に停止されない限り(例:docker stop <コンテナ名>経由)、常にコンテナを再起動します。Docker デーモンが再起動した場合、このコンテナも追従して再起動されます。
リスタートポリシーを伴う例:
version: '3.8'
services:
db:
image: postgres:13
# ... (環境変数やデータボリューム等のコンフィギュレーションは省略)
restart: unless-stopped # データベースは手動で停止されない限り常に実行し続けるべき
backend:
build: ./backend
# ...
depends_on:
db:
condition: service_healthy
restart: on-failure # バックエンドがクラッシュした場合は再起動すべき
frontend:
image: nginx:latest
# ...
depends_on:
backend:
condition: service_healthy
restart: always # フロントエンド Web サーバーは常に利用可能状態を維持しようと試みるべきこの例では:
dbサービスは、docker stopで手動で停止されない限り、自動的に再起動を続けます。backendサービスは、エラーに遭遇して非ゼロのステータスコードで終了した場合にのみ再起動します。frontendサービスは、停止した場合は常に再起動を試み、Web インターフェースが継続的に利用可能であることを保証します。
2.2 サービススケーリング (Service Scaling)
Docker Compose を使用するとサービスを「スケール」させることができます。これは、増加する負荷に対応したりデータのリダンダンシ(冗長性)を提供したりするために、特定のサービスの同一のインスタンス(レプリカ)を複数実行できることを意味します。Compose 自体は Docker Swarm や Kubernetes のように動的かつ自動的なスケーリングを提供するわけではありませんが、docker compose up --scale コマンドを使用して手動でサービスをスケールさせることができます。
例えば、backend サービスのインスタンスを 3 つ実行するには:
docker compose up -d --scale backend=3Compose は backend サービス用に 3 つの独立したコンテナを作成し、各コンテナには一意の名前(例:myapp-backend-1, myapp-backend-2, myapp-backend-3)が付けられます。これにより負荷が分散され、一定のフォールトトレランスがもたらされます。1つのバックエンドインスタンスがダウンしても、他のインスタンスがリクエストの処理を継続できます(今回の例で言えば、Nginx のようなロードバランサーがトラフィックを各インスタンスへディスパッチするように設定されていることが前提となります)。
2.3 実践アプリケーション:ECプラットフォームアーキテクチャ
いくつかの重要なサービスを含む EC プラットフォームを検討してみましょう:
- Product Catalog Service (プロダクトカタログサービス): 商品情報、画像、価格を提供します。(データベースから読み取り)
- Order Processing Service (オーダープロセッシングサービス): ユーザーの注文を処理し、在庫を更新します。(データベースへ書き込み、ペイメントゲートウェイとインタラクション)
- User Authentication Service (ユーザー認証サービス): ユーザーのログインと登録を管理します。(データベースの読み書き)
- Redis Cache (Redis キャッシュ): セッション管理と高速なデータ検索のために複数のサービスで使用されます。
- PostgreSQL Database (PostgreSQL データベース): すべてのサービスの永続化ストレージ。
- Load Balancer (Nginx ロードバランサー): トラフィックをプロダクトカタログ、オーダープロセッシング、およびユーザー認証サービスにルーティングします。
堅牢な Compose コンフィギュレーションでは、デペンデンシーとオーケストレーション機能をフル活用します:
version: '3.8'
services:
database:
image: postgres:13
environment:
POSTGRES_DB: ecommerce_db
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:
- db_data:/var/lib/postgresql/data
networks:
- ecommerce_network
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d ecommerce_db"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:6-alpine
networks:
- ecommerce_network
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 1s
timeout: 3s
retries: 5
product-catalog:
build: ./product-catalog
environment:
DB_HOST: database
REDIS_HOST: redis
depends_on:
database:
condition: service_healthy
redis:
condition: service_healthy
networks:
- ecommerce_network
restart: on-failure
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8001/health"]
interval: 10s
timeout: 5s
retries: 3
user-auth:
build: ./user-auth
environment:
DB_HOST: database
REDIS_HOST: redis
depends_on:
database:
condition: service_healthy
redis:
condition: service_healthy
networks:
- ecommerce_network
restart: on-failure
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8002/health"]
interval: 10s
timeout: 5s
retries: 3
order-processing:
build: ./order-processing
environment:
DB_HOST: database
PRODUCT_CATALOG_URL: http://product-catalog:8001
depends_on:
database:
condition: service_healthy
product-catalog:
condition: service_healthy
networks:
- ecommerce_network
restart: on-failure
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8003/health"]
interval: 10s
timeout: 5s
retries: 3
nginx:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
product-catalog:
condition: service_healthy
user-auth:
condition: service_healthy
order-processing:
condition: service_healthy
networks:
- ecommerce_network
restart: always
networks:
ecommerce_network:
driver: bridge
volumes:
db_data:このコンフィギュレーションにより以下が保証されます:
- どのアプリケーションサービスが接続を試みるよりも前に、Database と Redis が healthy であること。
- Nginx ロードバランサーがトラフィックのルーティングを開始する前に、すべてのアプリケーションサービス(プロダクトカタログ、ユーザー認証、オーダープロセッシング)が healthy であること。
- すべてのコアインフラストラクチャ(database, redis, nginx)に
restart: unless-stoppedまたはalwaysが設定され、高可用性が確保されていること。 - アプリケーションサービスに
restart: on-failureが設定され、内部エラーからのリカバリが可能であること。