プライベートECSクラスターを構築する際に難しかったところまとめ

## はじめに
プライベートECSクラスターを構築する機会があったので、作業中つまったところをまとめておきます。

## 本記事で扱う内容
- ECSクラスターをプライベートに構築した際のハマりどころと、その対処方法
- API Gateway → NLB、ECR、S3、DynamoDB、OpenSearch Serverlessなどの各AWSサービスとの統合・通信周りの話

## 今回構築した構成
今回構築した構成は以下の通りです。
API Gateway から Network Load Balancer (NLB) を通し、Private Subnet 内の ECS クラスターへアクセスするアーキテクチャになっています。外部アクセスを最小化しつつ、各種AWSサービス（DynamoDB、S3、ECR、OpenSearch Serverless など）と連携するようにしています。
構築にはCloudFormationを使用しました。

 ![Screenshot 2024-12-14 at 11.38.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245686/a4a6bed9-4496-90c4-2fe1-4aec2c4930ae.png)

## 課題①: API Gateway と NLB の通信設定

### 状況
- API Gateway と NLB の間で通信できず、API Gatewayのテストで `500 Internal Server Error` が返ってくる
- - API Gatewayで `500` を返す設定にデフォルトでなっているのは以下の場合:
  - API 設定エラー
  - オーソライザー設定エラー
  - オーソライザーエラー
- 認証自体はうまくいっているが通信が確立しないので、APIGW-NLBの通信に関する設定が間違っていそう。

### 原因
- API GatewayのエンドポイントURLを **http** で指定していたが、NLB側のリスナーが **8000** ポートを待ち受ける設定になっていた。（8000ポート はアプリで使用）

### 解決策
1. **NLBのリスナーをポート80でリッスン** するように変更する。  
2. NLBのセキュリティ設定で「PrivateLink トラフィックにインバウンドルールを適用する」オプションを無効化する。（←これは念の為に） 


## 課題②: プライベートECSクラスターへのアプリデプロイがうまくいかない

### 状況
- ECSクラスタのタスクを作成してもアプリが起動しない。
- 作成したサービスのログがまったく出力されていない。

### 原因
- ECSクラスターはCloudWatch Logsの使用が必要だった
- タスク実行ロールで `CreateLogGroup` が足りていなかった。（AWS管理の`AmazonECSTaskExecutionRolePolicy`ポリシーをタスク実行ロールに関連付けていたがこの権限はなかった）
- ECSクラスタは **CloudWatch Logs** へのアクセスを必要としているが、通信経路が存在しなかった。
以下はプライベートECSクラスターに最低限必要な通信経路
com.amazonaws.region.ecr.api, com.amazonaws.region.ecr.dkr
com.amazonaws.region.s3 (ゲートウェイ型)
**com.amazonaws.region.ecr.api** (Linux 1.4.0およびWindows 1.0.0から必要)

### 解決策
1. **CloudWatch Logs用のVPCエンドポイント** を作成。  
2. タスク実行ロールに **`logs:CreateLogGroup`**の権限を付与。  
3. CloudWatch Logsのロググループを事前に作成、またはタスク作成時に自動作成されるよう設定


## 課題③: DynamoDBのVPCエンドポイント選択（Gateway型 or Interface型）

### 状況
- ECSクラスタ上のアプリから DynamoDB に通信できない
-  **Interface型エンドポイント** を作成済み
- ECSクラスタから他のサービスエンドポイントはOKだが、DynamoDB用エンドポイントのモニタリングでアクティブ接続が確認できない

### 原因
- **Interface型エンドポイント** ではプライベートDNS名の有効化ができず、アプリ側でエンドポイントURLを指定する必要がある
- **Gateway型エンドポイント** ならルートテーブルだけの設定で済み、DNS周りの追加手順が不要

### 解決策
アプリ側での変更を必要としないよう、DynamoDBのVPCエンドポイントを **Interface型** から **Gateway型** に切り替えた。


## 課題④: OpenSearch Serverlessのエンドポイント作成方法

### 状況
- OpenSearch ServerlessのVPCエンドポイントを、他のサービス同様「VPC → エンドポイント」から作ろうとしたところ、該当サービスが見つからず困った
- CloudFormationを使っても `AWS::EC2::VPCEndpoint` では作成できない

### 解決策
- **OpenSearch Serverlessのコンソール** からVPCエンドポイントを作成。  
- CloudFormationを使う場合は **`Type: AWS::OpenSearchServerless::VpcEndpoint`** を指定する。 
- 他のエンドポイントのように **`AWS::EC2::VPCEndpoint`** でServerless用エンドポイントは作成できないので注意。

## 参考リンク
- [VPC リンク経由で NLB と接続する API Gateway にリクエストを送ると Internal Server Error が返ってきます。原因を教えてください。](https://dev.classmethod.jp/articles/tsnote-api-gateway-vpclink-nlb/)
- [[アップデート] Amazon DynamoDBがPrivateLink (インターフェイス型VPCエンドポイント) をサポートしました](https://dev.classmethod.jp/articles/amazon-dynamodb-aws-privatelink/)  
- [「Amazon S3 インターフェースエンドポイント（PrivateLink）ではプライベート DNS をサポートしていません」 の意味を絵をかいて腹落ちさせてみた](https://dev.classmethod.jp/articles/s3-privatelink-diagram/)  
- [AWS::OpenSearchServerless::VpcEndpoint](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-opensearchserverless-vpcendpoint.html)