マイクロサービスへ移行してから「1つのサービスがレスポンス遅延を起こしただけで、連鎖的にシステム全体が落ちた」——そんな経験を持つエンジニアは少なくありません。オンプレ時代のモノリシック構成では障害範囲が比較的予測しやすかった。しかしクラウドのマイクロサービス環境では、サービス間の依存連鎖が複雑で、1点の障害が全体に波及する「カスケード障害」が発生しやすくなります。
この記事では、カスケード障害を防ぐための設計パターンとして広く使われるサーキットブレーカーパターンを解説します。仕組みの理解から、AWSとAzureでの具体的な実装方法、コスト感覚まで体系的にカバーします。
なぜサーキットブレーカーが必要なのか
オンプレ時代のシステム障害対応は、「ハードウェアが死んだら切り離してフェイルオーバー」が基本でした。障害サーバーを隔離し、スタンバイ機に切り替える。単純明快です。
クラウドのマイクロサービス環境は様相が異なります。たとえば以下のような構成を想定してください。
・フロントエンドAPI → 注文サービス → 在庫サービス → DBサービス
在庫サービスのレスポンスが5秒かかるようになったとします。注文サービスはタイムアウトを待ちながらスレッドを保持し続け、やがてスレッドプールが枯渇します。次にフロントエンドAPIが同様のスレッド枯渇を起こし、最終的にユーザー向けAPIが完全に落ちる——これがカスケード障害です。
「サーバーを切り離す」という発想では、この問題は解決できません。障害が発生しているサービスへの呼び出しを回路遮断(ブレーカーを落とす)する仕組みが必要です。
電気回路のブレーカーをイメージしてください。過電流が流れたとき、ブレーカーが自動で遮断することで回路全体の損傷を防ぎます。これと同じ概念をソフトウェアのサービス間通信に適用したのがサーキットブレーカーパターンです。
サーキットブレーカーパターンの3つの状態
サーキットブレーカーは、以下の3つの状態を持ちます。
1. Closed(クローズド)状態
通常の動作状態です。リクエストはそのままバックエンドへ転送されます。失敗率を継続的に監視し、設定した閾値(例: 直近20回の呼び出しで50%以上が失敗)を超えると、Open状態に移行します。
2. Open(オープン)状態
障害を検知した状態です。後続のリクエストはバックエンドへ送らず、即座にエラーを返します(フォールバック処理を呼び出すこともあります)。これにより、応答待ちによるスレッド枯渇を防ぎます。一定時間(例: 30秒)が経過すると、Half-Open状態に移行します。
3. Half-Open(ハーフオープン)状態
「障害が回復したか確認する」プローブ状態です。一部のリクエストだけバックエンドへ通し、成功すればClosedに戻り、失敗すればOpenに戻ります。
この状態遷移をまとめると以下のとおりです。
| 状態 | リクエストの扱い | 遷移条件 |
|---|---|---|
| Closed | 通常通りバックエンドへ転送 | 失敗率が閾値を超えたらOpen |
| Open | 即座にエラー返却(バックエンド非呼び出し) | 一定時間経過後にHalf-Open |
| Half-Open | 一部のリクエストのみ通過 | 成功→Closed、失敗→Open |
AWSでの実装パターン
1. AWS App Mesh+EnvoyプロキシのOutlier Detectionを使う
ECSやEKSを使っている環境では、AWS App MeshとEnvoyプロキシを組み合わせることでアプリケーションコードを変更せずにサーキットブレーカーと同等の動作を実装できます。
AWS App MeshのVirtual NodeにOutlier Detection(外れ値検知)を設定します。Envoyが「一定回数以上エラーを返したホストをロードバランシング対象から除外する」機能で、サーキットブレーカーと同等の効果を持ちます。
# AWS App Mesh Virtual Node — CloudFormation YAML 設定例 # Outlier Detection によるサーキットブレーカー相当の動作 Properties: MeshName: my-mesh VirtualNodeName: inventory-service-vnode Spec: ServiceDiscovery: AWSCloudMap: NamespaceName: my-namespace ServiceName: inventory-service Listeners: - PortMapping: Port: 8080 Protocol: http OutlierDetection: # 5回連続エラーで対象ホストを隔離 MaxServerErrors: 5 # 隔離期間: 30秒 BaseEjectionDuration: Unit: s Value: 30 # 評価インターバル: 10秒 Interval: Unit: s Value: 10 # 最大50%のホストまで隔離可能 MaxEjectionPercent: 50
この設定で在庫サービスのインスタンスが5回連続エラーを返すと、30秒間そのインスタンスへのルーティングが停止します。アプリケーションコードは一切変更不要です。
2. アプリケーションレベルの実装:Resilience4j(Java/Spring Boot)
ECSやEKSのアプリケーションコード側にサーキットブレーカーを組み込む場合、JavaであればResilience4jが標準的な選択肢です。Spring Bootとの統合が容易で、設定ファイルで閾値を管理できます。
# Spring Boot application.yml — Resilience4j 設定例 resilience4j: circuitbreaker: instances: inventoryService: # 失敗率が50%を超えたらOpenへ移行 failureRateThreshold: 50 # 最小10回の呼び出しを評価サンプルとする minimumNumberOfCalls: 10 # OpenからHalf-Openへの待機時間 waitDurationInOpenState: 30s # Half-Open状態で通すテスト呼び出し数 permittedNumberOfCallsInHalfOpenState: 3 # スライディングウィンドウのサイズ slidingWindowSize: 20
Open状態のときに何を返すかをフォールバックとして定義します。たとえば「在庫確認は一時的に利用できません。後ほどお試しください」というキャッシュ値を返すなど、サービスの部分的な機能維持(グレースフルデグレード)が実現できます。
.NETの場合はPollyが同等の役割を果たします。
クラウド上のサーバーおよびコンテナ基盤となるLinuxの知識については、姉妹サイトLinuxMaster.JPでも体系的に解説しています。
Azureでの実装パターン
1. AKS+IstioのDestinationRuleで設定する
AKS(Azure Kubernetes Service)環境では、IstioサービスメッシュのOutlier Detection機能を使うのが定番です。KubernetesのカスタムリソースとしてYAMLで管理できるため、GitOpsとの相性も良好です。
# Istio DestinationRule — サーキットブレーカー設定例 apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: inventory-service-dr spec: host: inventory-service trafficPolicy: connectionPool: tcp: maxConnections: 100 http: http1MaxPendingRequests: 50 maxRequestsPerConnection: 10 outlierDetection: # 5回連続5xxエラーで対象ホストを隔離 consecutive5xxErrors: 5 # 評価インターバル: 1分 interval: 1m # 隔離基本時間: 30秒 baseEjectionTime: 30s # 最大50%のホストまで隔離可能 maxEjectionPercent: 50
2. Azure API ManagementのCircuit Breakerポリシーを使う
Azure API Managementには公式のサーキットブレーカー機能(circuit-breakerポリシー)が用意されています。バックエンドAPIへのリクエストが一定割合で失敗した場合、自動的にトラフィックを遮断します。
既存のAzure API Managementを使っている環境では、アプリケーションコードやKubernetes設定なしで導入できるメリットがあります。Premium・Standard・Basicティアで利用可能です(2026年6月時点)。
コストの考え方
実装方式によってコスト構造が大きく異なります(2026年6月時点)。
| 実装方式 | 追加コスト | 備考 |
|---|---|---|
| AWS App Mesh(Envoy) | EnvoyサイドカーのvCPU/メモリ分 | サイドカー1つあたり0.25vCPU/256MB程度が目安。ECS Fargateでは料金に直結するため事前試算が必須 |
| Resilience4j / Polly | ほぼゼロ | アプリケーション内ライブラリのため追加インフラ不要 |
| AKS+Istio | Envoyサイドカーのリソース消費 | Podあたりのオーバーヘッドをノード単位で事前試算すること |
| Azure API Management | 既存APIMティア料金内 | 新規導入の場合はConsumptionティアから試せる |
サービスメッシュ(App Mesh、Istio)を導入する場合、サイドカーコンテナのリソース消費が無視できないケースがあります。Fargateで多数のタスクを起動している環境では、サイドカー分のCPU・メモリ追加コストをあらかじめ見積もってください。Pod数が少ない段階ではライブラリベース(Resilience4j/Polly)の方がコスト効率は高くなります。
応用・実務Tips
・フォールバック設計を先に考える: サーキットブレーカーが機能するのは「Openになったときに何を返すか」を設計してこそです。キャッシュからの応答、空のリスト、デフォルト値など、サービスの特性に合わせて定義します。
・タイムアウトとセットで設定する: サーキットブレーカー単体ではなく、接続タイムアウト・読み取りタイムアウトとセットで設定するのが基本です。タイムアウトがないとOpenに移行するまでにスレッドが枯渇します。
・リトライとの組み合わせに注意する: リトライポリシーとサーキットブレーカーを同時に使う場合、「リトライ→失敗→ブレーカーが開く」という順序で動作します。リトライ回数を増やしすぎると失敗カウントが嵩むため逆効果になることがあります。
・メトリクスで状態を可視化する: サーキットブレーカーの状態遷移をAmazon CloudWatchやAzure Monitorで可視化することで、「どのサービス間で障害が頻発しているか」を把握し、根本的な改善につなげられます。Resilience4jはMicrometer経由でメトリクスをエクスポートできます。
・段階的に導入する: まず最も重要度の高いサービス間の1経路にだけ導入し、閾値の感触をつかんでから全体に展開するのが安全です。
よくあるトラブルと対処法
・「ブレーカーが思ったより開かない」: 閾値の設定が甘すぎる場合に起こります。minimumNumberOfCallsとslidingWindowSizeを小さくして、少ない呼び出し数でも判定が走るように調整します。
・「ブレーカーが開いたまま戻らない」: waitDurationInOpenStateが長すぎる、またはHalf-Open時のテスト呼び出しが失敗し続けているケースです。バックエンドの修復状況と照らし合わせて値を見直します。
・「ロードテスト中に意図せずブレーカーが開く」: 高負荷テスト中に閾値を超えてしまうことがあります。テスト環境では閾値を緩め、実測値をもとに本番値を決めるプロセスを踏むのが安全です。
・「どの呼び出しで開いたか分からない」: AWS X-RayやAzure Application InsightsなどのAPMツールと組み合わせることで、サーキットブレーカーの状態遷移をトレースに含めて可視化できます。
本記事のまとめ
サーキットブレーカーパターンは、マイクロサービスのカスケード障害を防ぐための基本設計パターンです。オンプレ時代の「フェイルオーバー」思想を、クラウドのサービス間通信に合わせて進化させたものと理解すると実践イメージが湧きやすいでしょう。
| 状況 | 推奨実装 |
|---|---|
| コード変更なしで実装したい(AWS ECS/EKS) | AWS App Mesh + Envoy の Outlier Detection |
| Javaアプリで細かく制御したい | Resilience4j(Spring Boot統合) |
| .NETアプリで細かく制御したい | Polly |
| Azure Kubernetes環境 | AKS + Istio の outlierDetection |
| Azure API Managementを既存で使っている | circuit-breakerポリシー |
まずはライブラリベース(Resilience4j/Polly)で1経路だけ試し、サービスメッシュへの移行はその後のステップとして段階的に進めるのがお勧めです。
カスケード障害を防ぐ設計、現場ではどう進める?
サーキットブレーカーを使いこなすには、フォールバック設計やタイムアウト設定との組み合わせ方を体系的に理解することが重要です。
オンプレの経験を活かしながら、現場で使えるクラウドスキルを体系的に身につけたい方へ、メルマガで実践的なクラウド活用ノウハウをお届けしています。
