【ITニュース解説】The 9 AM Discovery That Saved Our Production: An ECS Fargate Circuit Breaker Story
2025年09月18日に「Dev.to」が公開したITニュース「The 9 AM Discovery That Saved Our Production: An ECS Fargate Circuit Breaker Story」について初心者にもわかりやすく解説しています。
ITニュース概要
ECS Fargateでのポート設定ミスがサービス停止を招いた。ECSデプロイサーキットブレーカーを導入すれば、デプロイ失敗時に自動で以前の正常な状態へ戻し、システム停止を防げる。常にタスク定義を更新し、`latest`タグを使わないなど適切な運用が重要だ。
ITニュース解説
コンテナを使ってアプリケーションを動かす現代のシステム開発では、たとえ小さな設定ミスでも、システム全体に大きな影響を与えてしまうことがある。今回の話は、Amazon ECS Fargateというサービス上でアプリケーションを運用していたチームが、ポート設定のミスから大きな問題に直面し、そこから「デプロイメントサーキットブレーカー」という重要な安全機能の価値を学んだ経緯である。
ある日、チームのWebアプリケーション(Flask API)は、Amazon ECS Fargateという、コンテナ化したアプリケーションをサーバーの管理なしで動かせるサービス上で、問題なく稼働していた。このアプリケーションは、特定のポート番号(5001番)で通信を受け付けるように設定されており、外部からのアクセスを振り分けるロードバランサー(ALB)や、コンテナの起動方法を定義するタスク定義も、すべてこの5001番ポートに合わせていた。システムは朝8時に自動で起動し、営業時間外は停止することでコストを最適化していた。
しかし、一人の開発者がローカル環境で作業中に、他のサービスとポートが衝突したため、一時的な解決策としてアプリケーションが使用するポート番号を5001番から5201番に変更してしまった。この変更はローカルでの動作確認やコードレビュー、コンテナイメージの作成(Dockerビルド)の段階では問題なく、そのままシステムにデプロイされた。見た目上は何も問題がないように思えたのである。
翌朝、別のチームメンバーがAPIテストを実行すると、すべての呼び出しが「502 Bad Gateway」というエラーになった。これは、リクエストがWebサーバーに到達できなかったり、Webサーバーがゲートウェイから無効な応答を受け取った際に表示されるエラーである。ECSの管理画面を確認すると、Fargateタスクが「PENDING(保留中)」、「RUNNING(実行中)」、「STOPPED(停止済み)」という状態を延々と繰り返していることが判明した。タスクは起動して数分間動いた後、停止され、ECSはすぐに新しいタスクを起動しようとするが、それもまた停止するという無限ループに陥っていたのだ。
この問題の根本原因はすぐに特定された。アプリケーションは新しいポート番号5201番で通信を待ち受けているのに、ロードバランサーや他のシステム設定は古いポート番号5001番のままであったことだ。これにより、ロードバランサーはアプリケーションの健康状態をチェックできず、タスクが「不健康」と判断されて停止され、ECSは新しいタスクを起動するという「タスク死亡スパイラル」が始まった。この間、システムは計算資源を消費し続けるが、ユーザーには一切サービスを提供できない状態となっていた。
この事故の教訓から、チームは「ECS Fargateデプロイメントサーキットブレーカー」という機能の重要性に気づいた。これは、AWSが提供するECSの機能で、新しいバージョンのアプリケーションをデプロイする際に、そのデプロイがうまくいっていないことを自動で検知し、問題のあるデプロイを停止して、自動的に以前の安定したバージョンに戻してくれる安全装置である。もしこのサーキットブレーカーが有効になっていれば、今回のポート設定ミスによるデプロイは、わずか数分で異常が検知され、自動的に以前の正常な状態にロールバック(巻き戻し)されていたはずだ。実際の75分間のダウンタイムが、5~10分程度のサービス低下で済んだ可能性があったのである。
サーキットブレーカーを有効にするのは簡単で、デプロイメントの設定に「enable: true」と「rollback: true」を追加するだけで良い。しかし、この機能が正しく動作するためにはいくつかの条件があることが、その後の調査で明らかになった。一つは、「新しいタスク定義が登録された場合のみ」サーキットブレーカーが動作するという点だ。タスク定義とは、ECSがコンテナをどのように動かすかを指示する設計図のようなもので、たとえコンテナイメージの内容が更新されても、タスク定義自体が変わっていなければ、ECSはそれを新しいデプロイとみなさない場合がある。
今回の事故では、チームはコンテナイメージのタグに「latest」というものを使用していた。これは常に最新のイメージを指す便利なタグだが、タスク定義が変更されない限り、ECSは古いタスク定義のまま新しい(しかし壊れた)イメージを使ってタスクを起動しようとする。この場合、新しいデプロイとして認識されないため、サーキットブレーカーは発動せず、問題のあるイメージが静かに実行され続けてしまうという落とし穴があった。また、コスト削減のためにタスクの希望実行数(desiredCount)を1に設定している場合、異常の検知とロールバックに時間がかかることも分かった。ECSは通常、最低3つのタスクが失敗するとロールバックをトリガーするため、タスク数が少ないとこのしきい値に達するまでに時間がかかるためである。
このような実際の失敗から、チームは以下の推奨事項を導き出した。
- 「latest」タグの使用を避ける: コンテナイメージには「v1.2.3」やビルド番号のような、変更されない一意のタグを使い、常に特定のバージョンを参照するようにする。
- すべてのデプロイで新しいタスク定義を登録する: 新しいバージョンのアプリケーションをデプロイするたびに、必ず新しいタスク定義のバージョンを登録する。これにより、ECSはそれを新しいデプロイと認識し、サーキットブレーカーが正しく動作するようになる。
- CloudWatchアラームでデプロイの失敗を早期に検知する: ロードバランサーが「不健康なホスト」を検出した場合や、ECSサービスで異常なタスク停止が発生した場合に、自動でアラートを発するように設定する。
- CI/CDパイプラインでタスク定義の更新を強制する: 継続的インテグレーション/継続的デリバリー(CI/CD)の自動化されたプロセスの中で、新しいタスク定義が確実に更新されるようにチェックを組み込む。
- デプロイ中は希望実行数(desiredCount)を増やす: タスクの失敗をより迅速に検知し、ロールバックを早めるために、デプロイ中は一時的にdesiredCountを増やすことを検討する。
今回の経験を通じて、チームはデプロイメントサーキットブレーカーを単なるオプション機能ではなく、システムを安全に運用するための必須インフラと位置付けた。小さなポート設定ミスが長時間にわたるサービス停止を引き起こす可能性があったが、この事故を早期に発見し、教訓を活かすことで、チームはデプロイの安全性を大幅に向上させることができた。今では、たとえ不具合のあるデプロイが混入したとしても、ECSが自動的に問題を検知し、ロールバックしてくれるという信頼感のもと、自信を持ってデプロイできるようになっている。ユーザーに影響が及ぶ前に問題を解決できたことは、まさに「最良のプロダクションインシデントは、決して起こらないインシデントである」という言葉を体現するものであった。