FargateとRDSを接続するために、VPCエンドポイントとセキュリティグループを設定する
社内の業務効率化システムを、踏み台サーバー構成からECS on Fargateに移行した話の続きになります。
事象
社内の業務効率化システムを、踏み台サーバー構成からECS on Fargateに移行する際に、FargateとRDS(データベース)を異なるVPC(=家の敷地みたいなもの)に置いてしまい、RDSへの接続エラーが出ていました。
同じVPC内に移せばいいってものじゃなかった
エラーを解消するため、FargateをRDSと同じVPC内に移す、つまりFargateとRDSを同じ家の敷地内に移せばいいのかと思ったのですが、そうは問屋が卸しませんでした。
なぜ接続できないのかを理解するには、VPCにおける「サブネット」について理解する必要があります。
VPCは家の敷地ないしはマンションのようなものなのですが、その中は「サブネット」という部屋で分かれています。サブネットにも3種類存在し、インターネットとの通信が発生するPublic Subnet、一部のアクセスのみが許可されるProtected Subnet、インターネットから直接アクセスできないPrivate Subnetがあります。
はじめにFargateをRDSと同じVPC内に移動する時、以下のように、FargateをPrivate Subnet、RDSをProtected Subnetに配置するアーキテクチャにしました。
( Internet / AWS Managed Area )
│
┌──────────▼──────────┐
│ AWS ECR 📦 │
│ (Image Registry) │
└─────────────────────┘
│
===========│===================================
【 VPC 】 │ ❌ 接続できない
│
┌──────────▼──────────┐
│ Private Subnet │
│ │
│ ┌─────────────┐ │
│ │ Fargate │ │
│ └──────┬──────┘ │
└──────────┼──────────┘
│
│ ❌ 接続できない
▼
┌─────────────────────┐
│ Protected Subnet │
│ ┌─────────────┐ │
│ │ RDS │ │
│ │ (Database) │ │
│ └─────────────┘ │
└─────────────────────┘
でもこれだけでは接続できません。
Dockerイメージが保管してあるECRや、ログを保存するCloudWatch LogsはVPCの外にあるため、これらにアクセスするためにはインターネットを経由しなくてはいけません。
なので、FargateがECRという倉庫からイメージを取ってくる手段と、RDSにリクエストを送る手段を作ってあげる必要があります。
VPCエンドポイントの導入
まず、FargateがECRからイメージを取得できるようにするため、VPCエンドポイントを導入しました。
VPCエンドポイントはAWSサービスへの直通トンネルのようなもので、Private Subnetに置いてあるFargateがECRやCloudWatch Logsと接続することを可能にします。
今回導入したのは以下の4つのエンドポイントです。
| エンドポイント | 用途 |
|---|---|
| ECR API | ECRの認証やAPI操作用 |
| ECR DKR | Dockerイメージのダウンロード用 |
| S3 | イメージの実体(レイヤー)がS3にあるため必須 |
| CloudWatch Logs | CloudWatch Logsへのログ送信用 |
これで無事にコンテナが起動し、FargateがECRからイメージを取得・起動できるようになりました。
しかし、アプリケーションのログを見るとDB接続エラーが発生していました。
Connection timed out: could not connect to server: Connection refused
Is the server running on host "rds-endpoint" and accepting TCP/IP connections on port 5432?
同じVPCの中にいて、ネットワークも繋がっているはずなのに、RDSに拒否されてしまいます。
セキュリティグループを設定する
これについては、セキュリティグループを設定することで解決しました。
セキュリティグループというのは、VPCというマンションのインターフォンのようなものです。
セキュリティグループは、デフォルトで「すべての受信を拒否」となっており、いくら同じ敷地内(VPC)にいるFargateさんであっても、セキュリティグループという入館許可リストに名前がなければ、かなしいかな、門前払いです。
というわけで、セキュリティグループIDを使って許可設定を行いました。
(ちなみに、FargateのIPアドレスを許可することを最初考えたのですが、Fargateは再起動のたびにIPアドレスが変わるので断念しました)
設定手順としては、
- Fargate用のセキュリティグループを作成(例: hogehoge)
- RDSのセキュリティグループのインバウンドルールを編集
- タイプ: PostgreSQL (5432)
- ソース: hogehoge (IPアドレスではなく、SGのIDを選択)
# イメージ:AWS CLIで書くとこんな感じ
aws ec2 authorize-security-group-ingress \
--group-id [RDS_SG_ID] \
--protocol tcp \
--port 5432 \
--source-group [FARGATE_SG_ID]
これで、「このIDの名札(hogehoge)を付けている人なら、IPが何であろうと通してOK」 という柔軟な設定が可能になります。
同様に、Dify(EC2)からFargateへのアクセス(Port 8000)も、DifyのセキュリティグループIDをソースに指定して許可しました。
FargateとRDSを接続する構成
VPCエンドポイントという直通トンネルでVPCという屋敷の壁を抜け、セキュリティグループの設定でRDSの扉を開く通行手形を発行したことで、FargateとRDSがようやく接続できるようになりました。
最終的な構成は次のようになりました。
( Internet / AWS Managed Area )
│
┌──────────▼──────────┐
│ AWS ECR / S3 📦 │
│ (Image & Layers) │
└──────────▲──────────┘
│
===========│===================================
【 VPC 】 │ ✅ トンネル開通 (VPC Endpoint)
│
┌──────────┴──────────┐
│ Private Subnet │
│ │
│ ┌─────────────┐ │ 🏷️ hogehoge (通行手形)
│ │ Fargate │───┼──────────┐
│ │ (Running!) │ │ │
│ └──────┬──────┘ │ │
│ │ │ 🚇 VPC Endpoint
│ │ │ (Interface/Gateway)
└──────────┼──────────┘
│
│ ✅ 許可 (Allow hogehoge)
▼
┌─────────────────────┐
│ Protected Subnet │
│ ┌─────────────┐ │
│ │ RDS │ │
│ │ (Database) │◀──┘ "hogehoge" なら
│ └─────────────┘ 通してよし!
└─────────────────────┘
学び
- プライベートサブネットはデフォルトではインターネットに接続できない。ECRといったVPC外にあるAWSサービスを使うにはVPCエンドポイントを設定する必要がある
- 同じVPC内であっても、セキュリティグループの設定がなければ接続することができない。接続元が変わるコンテナ環境では、IP指定ではなく、セキュリティグループIDで接続許可する方が🙆♀️
Discussion