バルクヘッド パターンは、障害を許容するアプリケーション設計の一種です。 バルクヘッド アーキテクチャ (セルベース アーキテクチャとも呼ばれる) では、アプリケーションの要素がプールに分離されているため、1 つの要素で障害が発生しても他の要素は機能し続けます。 これは、船体の区分された区画 (bulkhead: 隔壁) になぞらえた命名です。 船体が傷つけられた場合、水浸しになるのは破損した部分だけで、これによって船が沈むのを防ぎます。
コンテキストと問題
クラウド ベースのアプリケーションには複数のサービスが含まれ、各々のサービスは 1 つ以上のコンシューマーを持ちます。 サービスで過度の負荷やエラーがあると、そのサービスのすべてのコンシューマーに影響します。
さらに、コンシューマーは複数のサービスに同時に要求を送信し、要求ごとにリソースを使用しています。 コンシューマーが、正しく構成されていないまたは応答していないサービスに要求を送信すると、クライアントの要求で使用されるリソースが適切なタイミングで解放されない可能性があります。 このサービスへの要求が続くと、これらのリソースが不足する場合があります。 たとえば、クライアントの接続プールを使い果たすことがあります。 その時点で、このコンシューマーによる他のサービスへの要求が影響を受けます。 最終的にコンシューマーは、元々応答していないサービスだけでなく、他のサービスにも要求を送信できなくなります。
同様のリソース不足の問題が、複数のコンシューマーを伴うサービスに影響します。 1 つのクライアントから送信された大量の要求が、サービスで使用可能なリソースを使い果たす可能性があります。 他のコンシューマーがサービスを利用できなくなり、エラーが積み重なるという結果になります。
解決策
コンシューマーの負荷と可用性の要件に基づいて、サービス インスタンスを別々のグループに分割します。 この設計は障害を特定するのに役立ち、障害中でも一部のコンシューマーに対してサービス機能を維持することができます。
コンシューマーはまた、リソースを分割し、1 つのサービスの呼び出しに使用されるリソースが別のサービスの呼び出しに使用されるリソースに影響しないようにできます。 たとえば、複数のサービスを呼び出すコンシューマーに、各々のサービス用の接続プールを割り当てることができます。 サービスが失敗し始めると、そのサービスに割り当てられている接続プールのみに影響し、コンシューマーは他のサービスを引き続き使用できます。
このパターンには次のような利点があります。
- コンシューマーとサービスを、障害の連鎖から分離します。 コンシューマーまたはサービスに影響する問題は、自身のバルクヘッド内に分離でき、ソリューション全体に障害が発生するのを妨ぎます。
- サービス エラーが発生した場合、一部の機能を保持できます。 アプリケーションのその他のサービスと機能は、引き続き機能します。
- 使用するアプリケーションに別の品質のサービスを提供するサービスを展開できます。 優先度の高いサービスを使用するように、優先度の高いコンシューマー プールを構成することができます。
次の図は、個々 のサービスを呼び出す接続プールを中心に構造化したバルクヘッドを示しています。 サービス A で障害や他の問題が発生すると、接続プールが分離され、サービス A に割り当てられているスレッド プールを使用するワークロードだけが影響を受けます。 サービス B および C を使用するワークロードは影響を受けず、中断することなく機能し続けることができます。
次の図は、1 つのサービスを呼び出す複数のクライアントを示しています。 各クライアントには、別々のサービス インスタンスが割り当てられます。 クライアント 1 の要求が多すぎて、そのインスタンスを圧迫しています。 各サービス インスタンスは他から分離されているため、他のクライアントは呼び出しを続行することができます。
問題と注意事項
- アプリケーションのビジネス要件と技術的要件を中心にパーティションを定義します。
- 戦術的 DDD を使用してマイクロサービスを設計する場合は、境界付けられたコンテキストにパーティション境界を合わせる必要があります。
- サービスまたはコンシューマーをバルクヘッドに分割するときは、コスト、パフォーマンス、管理容易性の観点からのオーバーヘッドと、テクノロジによる分離レベルを考慮してください。
- より高度なエラー処理を提供するために、バルクヘッドを再試行、サーキット ブレーカー、調整パターンと組み合わせることを検討してください。
- コンシューマーをバルクヘッドに分割する場合は、プロセス、スレッド プール、およびセマフォの使用を検討します。 resilience4j や Polly などのプロジェクトが、コンシューマー バルクヘッドを作成するためのフレームワークを提供しています。
- サービスをバルクヘッドに分割する場合は、別々 の仮想マシン、コンテナー、またはプロセスに展開することを検討してください。 コンテナーは、かなり少ないオーバーヘッドで、バランスのとれたリソース分離を提供します。
- 非同期メッセージを使用して通信するサービスは、異なるセットのキューを介して分離することができます。 各キューは、そのキュー上のメッセージを処理するインスタンスの専用セット、または、デキューして処理をディスパッチするアルゴリズムを使用するインスタンスの単一グループを持つことができます。
- バルクヘッドの細分性のレベルを決定します。 たとえば、テナントをパーティションに分散する場合は、各テナントを別のパーティションに配置するか、1 つのパーティションに複数のテナントを置きます。
- 各パーティションのパフォーマンスと SLA を監視します。
このパターンを使用する状況
このパターンは次の目的で使用します。
- バックエンド サービスのセットを使用するのに使われているリソースを分離する。特に、サービスのいずれかが応答していない場合でも、アプリケーションがなんらかのレベルの機能を提供できる場合。
- 標準的なコンシューマーから重要なコンシューマーを分離する。
- エラーの連鎖からアプリケーションを保護する。
このパターンは、次の状況では適切でない可能性があります。
- プロジェクト内で、効率性の低いリソース使用は受け入れられない。
- 複雑さを追加する必要はない。
ワークロード設計
設計者は、Azure Well-Architected Framework の柱で説明されている目標と原則に対処するために、ワークロードの設計でどのようにバルクヘッド パターンを使用できるかを評価する必要があります。 次に例を示します。
重要な要素 | このパターンが柱の目標をサポートする方法 |
---|---|
信頼性設計の決定により、ワークロードが誤動作に対して復元力を持ち、障害発生後も完全に機能する状態に回復することができます。 | コンポーネント間の意図的かつ完全なセグメンテーションによって導入された障害分離戦略は、問題が発生しているバルクヘッドだけに障害を含めようとし、他のバルクヘッドへの影響を防止します。 - RE:02 クリティカルフロー - RE:07 自己保護 |
セキュリティ設計の決定により、ワークロードのデータとシステムの機密性、整合性、および可用性が確保されます。 | コンポーネント間のセグメンテーションは、セキュリティインシデントを侵害されたバルクヘッドに限定するのに役立ちます。 - SE:04 セグメンテーション |
パフォーマンスの効率化は、スケーリング、データ、コードを最適化することによって、ワークロードが効率的にニーズを満たすのに役立ちます。 | 各バルクヘッドは個別に拡張可能で、バルクヘッドにカプセル化されたタスクのニーズを効率的に満たすことができます。 - PE:02 容量計画 - PE:05 スケーリングとパーティショニング |
設計決定と同様に、このパターンで導入される可能性のある他の柱の目標とのトレードオフを考慮してください。
例
次の Kubernetes 構成ファイルでは、独自の CPU とメモリ リソースと制限で 1 つのサービスを実行する、分離されたコンテナーを作成します。
apiVersion: v1
kind: Pod
metadata:
name: drone-management
spec:
containers:
- name: drone-management-container
image: drone-service
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "1"