はじめに
こんにちは。ニフティの山田です。
AWSのElastic Container Service(ECS)を使ってシステムを構築する際、機能追加や負荷の変動に応じてサービス構成を変更する必要が生じることがあります。特に多数のサブシステムからなるような構成では、フロントエンド寄りのサービスの要件によって通信先を変更することも少なくありません。
この記事ではこのような状況に柔軟に対応できる構成方法について考察します。
基本構成の課題
標準的なECSベースの構成は以下のようになるかと思います。

この構成では、長期運用を考えた場合に以下のような課題があります。
- フロントエンドだけ構成が異なる
- ECSにアクセスするためにはALBが必要なので、ALB/ECSはワンセットで考えたい
- 外部アクセスが必要な部分はPublicなALBにする必要があり、ここだけ構成が変わる
- フロントエンドの前段にシステム追加を行いたいような場合、インフラ構成に大きな変更が入ってしまう
- Terraformで管理している場合、フロントエンドだけモジュール構成を大きく変える必要が生じる場合がある
- フロントエンドの部分にプライベートAPIを生やさざるを得ない、という状況で厄介
- 一度インターネットに出る必要が生じる
- NAT GWのIPを許可するなどの微妙な対応が必要
- フロントエンドのALBだけ複数の役割を持っている
- ECSに対するALBの役割は純粋な負荷分散装置、つまりスケーリングに対応するためのもの
- フロントエンドだけはパスごとのルーティングなど、リバースプロキシとしての機能を持つ可能性がある
つまりパブリック公開部分だけがインフラ構成上、特殊な扱いをされていることになります。
これを解決するためには、以下のようにしたくなります。

常にALB/ECSを1セットにしてすべてプライベートサブネットに設置することとし、別途公開用のリバースプロキシ専用ALBを立てます。公開要件はリバースプロキシ用のALBに集約されるので、各サブシステム側は気にする必要がなくなります。
ですがALBのターゲットにALBを指定できないので、この構成を実現することはできません。また二重のALBが完全に無駄です。さてどうしましょうか。
構成案
リバースプロキシ/BFFの設置

古典的なWeb3層構成のように、リバースプロキシ(Apache/nginx)を用意します。構成によってはBFFのようなものになることもあるでしょう。
パブリック公開の責務はここに集約します。
Pros
- 公開に関する責務は分離でき、裏側の構成は自由
- Apache/nginxの機能を利用できるため、複雑なルーティングやアクセス制御があったとしても対応できる
- 構成として理解はしやすい
Cons
- リソースが増える
- リバプロ用ECSが増えてしまう
- インフラコストも運用コストも増える
- ALBの機能とリバプロの機能が重複する
適用できそうなケース
- 古典的アプリを移植してくるケース
- アクセス制御が複雑で、Apache/nginxが必要になるケース
- 静的コンテンツの配信がアプリ外で必要になるケース
CloudFront VPC Origins

CloudFrontを使用する前提となります。
CloudFront VPC Originsを使うとCloudFrontからVPC内部に直接アクセスできます。Public ALBが不要となり、ALB/ECSはすべてプライベートサブネットに置けるようになります。
Pros
- Public ALBが不要
- IPv4アドレス課金を減らせる
- IPv4アドレス直接アクセスによるスパム・不正アクセスを考えなくて良くなる
Cons
- CloudFrontをもともと使っていない場合はコスト増になってしまう
- ルーティングをCloudFrontで行うことになる
- CloudFront自体の振り分け機能はALBに比べてかなり少ない
- HTTPヘッダなどの条件を入れる場合、CloudFront Functionsで頑張ることになる
- ALB特有機能が一部使えない
- OIDC連携機能など
適用できそうなケース
- CloudFrontを既に使っているケース
- ルーティング要件が複雑でなく、CloudFront Functionsでカバーできる
- OIDC連携などが必要ない
Service Connect
サービスメッシュライクなサービスであるService ConnectによりECS間通信を行います。
Service Connectを使うと、各ECS TaskはサイドカーコンテナとしてEnvoy Proxyが自動挿入されます。各ECS ServiceはCloud Mapに登録され、Service間はCloud Map上の登録名でアクセスし、実際の通信はEnvoy Proxyによりルーティングされるようになります。
従来はBlue/Greenデプロイができないという課題がありましたが、先日発表されたECS built-in Blue/Greenデプロイで対応できるようになりました。
ECS Service間でしか通信できないため、パブリックアクセスにはALBが必要となります。
Pros
- ALBがなくなる分、ネットワークレイテンシが削減される
- ALBの分のコストが減る
Cons
- Envoy Proxyの分、追加のCPU/RAMを要求する
- 256 CPU Unit(0.25vCPU)/64MB RAMを追加することが推奨
- ECS Service間でしか通信できない
- 定時実行LambdaなどでバックエンドのECSを叩く、などは不可能
- フロントエンドの柔軟性は低いまま
- ここだけALBに縛られてしまう
適用できそうなケース
以下を両方満たすケース
- ECS Service間でしか通信しない
- Envoy Proxy分のコスト増よりも、ALB削減分が上回る
- 元々ECS Taskのスペックに余裕があり、Service Connectによるスペックアップが不要な環境でないとおそらく満たせない
VPC Lattice(ECS統合)

VPC Latticeは仮想ALBとでも呼ぶべきサービスです。複数のアカウント・VPCをまたいで「サービス」を定義し、そこに対する「リスナー」「ターゲットグループ」を設定できます。
ターゲットグループに追加するのは通常はALBなどですが、ECS統合の機能が追加されたことにより直接ECS Serviceをターゲットとして指定できるようになっています。
Pros
- ALB同様の機能、柔軟性を持つ
- VPC・アカウントをまたいで統一的なサービス管理が可能
Cons
- 高額
- 維持料金、データ転送料金ともALBより高額
適用できそうなケース
単一サービス内での適用はコスト効率が低いため、会社全体でVPC Latticeを導入し、サービス間接続をVPC Latticeに任せるケース
まとめ
複数のECSサービス構成方法を検討した結果、以下の結論に至りました。
- CloudFrontを使えるなら、ALB/ECSをすべてプライベートサブネットに配置し、CloudFront VPC Originsで接続する構成が安定的かつ柔軟性も高い
- CloudFrontを使えないなどの場合は、従来通りALBをパブリック公開する方法も有効
- Service Connectは期待できるサービスではありますが制約も多く、慎重な選択が必要となります
CloudFrontを使えるという前提にはなりますが、VPC Originsによる構成は有用な選択肢となりそうです。最終的な構成選択は、サービスの規模や要件、コスト制約によって異なりますが、新規構築などの際に検討してみてはいかがでしょうか。