【Amazon ECS】 1タスク複数Webアプリコンテナ構成でのBlue/Greenデプロイについて考えてみた
こんにちは、コンサルティング部の神野です。
Amazon ECS(以下ECS)環境では、リソース効率や関連性の高さから、1つのタスク定義内に複数のコンテナ(例えば、Webサーバーとログ収集サイドカーなど)を同居させる構成を採用されているケースもあるかと思います。
ただ、同居構成のパターンでWebアプリコンテナが異なるポートでリッスンし、Application Load Balancer (ALB)がトラフィックを受け付けて複数のターゲットグループに振り分けているケースについてはいかがでしょうか。下記のようなイメージです。
上記のような「1タスク複数Webアプリコンテナ」構成のECSサービスに対して、Blue/Greenデプロイを適用したいと考えた場合に実現可能か気になったので調べてみました。
結論
CodeDeployやGitHub Actionsどちらを使用したとしても、下記公式ドキュメントに記載があるようローリングアップデート以外はECS Serviceに対して複数のターゲットグループをアタッチすることが難しいことが分かりました。
- サービス定義で複数のターゲット グループを指定することは、次の条件でのみサポートされます。
- サービスでは、Application Load Balancer または Network Load Balancer のいずれかを使用する必要があります。
- サービスでは、ローリング アップデート (
ECS
) デプロイメント コントローラー タイプを使用する必要があります。
そうなんですね、はい・・・
このドキュメントをもってして、「結論無理です」で終わりなのですが、妙に諦めが悪くどこがボトルネックになるのか実際に確認してみました!
また、この構成が無理なら無理で代替案としてどういった案が取れるのかも検討してみました。
CodeDeployを使った場合
デプロイの定義を記載するAppSpec
ファイルでは1つのコンテナ定義しか設定できません。
AppSpecファイルの例
version: 0.0
Resources:
- TargetService:
Type: AWS::ECS::Service
Properties:
TaskDefinition: <TASK_DEFINITION>
LoadBalancerInfo:
ContainerName: "app1" # 1コンテナのみ定義可能
ContainerPort: 80
LoadBalancerInfo
では1つのターゲットグループしか指定できないため、複数ポートの振り分けが困難です。
TerraformなどのIaCもしくはコンソール上で初回は複数設定できたとしても、CodeDeployを使ってBlue/Greenデプロイを行ったタイミングで、2つ目のWebアプリ(この例だとポート81のコンテナ)へのターゲットグループの設定ができず、追従しない形となります。具体的には下記イメージです。
GitHub Actionsを使った場合
GitHub Actionsを使用して複数のターゲットグループを紐付けないか検証しましたが、ECSのデプロイ方法External
の仕様として複数ターゲットグループは登録できないとエラーが出力されました。
こちらはそもそも最初から複数ターゲットグループの登録ができないため、2つ目のWebアプリ(今回ならポート81で稼働しているwebappb)のコンテナにはリクエストの振り分けが難しい状態となります。
エラーメッセージの例
An error occurred (InvalidParameterException) when calling the CreateTaskSet operation: CreateTaskSet cannot have more than 1 load balancer
公式ドキュメントにあるように、エラーメッセージに2つ以上のターゲットグループは登録できないと怒られますね・・・
やはり厳しかったですね・・・!無理なら無理でどう対応するか検討してみます!
解決に対する案を考えてみた
1. ECS Serviceの分割
現在1つのECS Serviceで管理している1タスク内複数Webアプリの構成を、別々のECS Serviceとして分割する方法です。
コストが増加するもののAWSの仕様としてはこちらの方が適しているように感じています。
下記記事にそれぞれのECS ServiceでBlue/Greenを独立して行うことを可能とする方法は記載があります。
記事ではホストヘッダーで分岐していますが、URLのパスベースでターゲットグループを分岐することももちろん可能です。
2つCI/CDパイプラインを用意し、トリガーをそれぞれのWebアプリケーションに対応するコードのプッシュに絞れば、 独立した形で実装できるかと思います。(レポジトリのディレクトリ構成にもよりますが・・・)
それか連続してデプロイを実施したい場合は、1つ目のデプロイが完了したタイミングでEventBridgeなどで連携して2つ目のパイプラインを実行する形になるかと思います。
簡略化したイメージとしては下記となります。
実装イメージ:トリガーをそれぞれのWebアプリケーションに対応する
実装イメージ:1つ目のパイプラインのデプロイが完了したタイミングでEventBridgeで連携して2つ目のパイプラインを実行する
2. ローリングアップデートの採用
現状の構成を維持したまま、デプロイ方式をローリングアップデートに変更する方法です。
ただ、今回Blue/Greenを使用したいといった話からこの検討を進めているので、どうしても採用したいといった背景や要件があるなら適していないかと思います。
仮に許容できるなら検討してみてもいいかもしれません。
3. リバースプロキシサーバーを前段に配置し、リクエストを振り分けする
CodeDeployを使ったBlue/GreenデプロイおよびGitHub Actionsを使用したExternal
のデプロイはターゲットグループが1つしか紐付けできないので、新規でnginx
などのリバースプロキシサーバーを立ち上げて、リバースプロキシがALBからのリクエストを受けてECS Service内部でロードバランスする方法も考えられます。
ただ、この方法にはいくつか問題点があるかと思います。まず、新規で追加する形になるので実装のコストが上がります。また、1つのタスク内に全ての機能を詰め込むことになるため、責任分解点が不明瞭になるといったデメリットがあります。
例えば、複数のWebアプリケーションを1つのタスクに詰め込むと以下のようなデメリットが考えられるので下記に対する考慮も必要かと思います。
デメリットの例
あくまでイメージで可能性となります。
- アプリケーションごとの独立したスケーリングが困難になります
- 障害の影響範囲が広がり、一部の問題が全体に波及する可能性が高まります
- デプロイリスクが増大し、1つのアプリ更新で全てに影響が出る可能性があります
- アプリケーション間の依存関係が密結合になりがちで、将来の変更が困難になります
- ログやモニタリングが複雑化し、問題の切り分けが難しくなります
マイクロサービスやコンテナ化の主要なメリットである、責任の分離や、独立したデプロイサイクルが損なわれるため、この選択肢の優先度はそこまで高くないと考えられます。
まとめ
3案をまとめるとBlue/Greenデプロイを実現したい場合はやはりWebサービス毎にECS Serviceを分割して実現するのが適しているのかなと思いました。1タスク内に複数Webアプリを同居する構成よりもコストが上がる可能性はあるかも知れませんが、AWSのサービス仕様と合わせることで運用の安定性が確保できると考えられます。
おわりに
今回調べた内容はあくまで現時点(2025年5月時点)のAWSの仕様に基づいたものです、もしかしたら将来的に複数ターゲットグループの紐付けでもBlue/Greenデプロイが実現できるようになる可能性もあるかもしれません。
ただ現状Webサービス毎にECS Serviceを分割する方法が、1タスク内に複数Webアプリを同居する構成よりもコストが上がる可能性はあるかも知れませんが、AWSのサービス仕様を考慮しつつ、また、責任分解の原則に従い、各サービスが独立してスケーリングやデプロイできるようになるメリットも大きいと思います。
もし皆さんの中でもっと良い解決策や異なるアプローチをお持ちの方がいらっしゃいましたら、ぜひコメントなどでご意見いただければ嬉しいですし、本記事が少しでも参考になったら幸いです。
最後まで読んでいただき、ありがとうございました!