ハッカソン個人備忘録⑳：FastAPIを動かすためのFargate×RDS（MySQL）構成をCloudFormationで自動化してみた

## はじめに
この記事では、AWS CloudFormation テンプレートを使って、Fargate 上で動作する ECS クラスタと MySQL を利用した RDS インスタンスを構築する方法について解説します。

:::note info
**個人の備忘録程度の走り書きとなっておりますが、温かい目で見守っていただければ幸いです。**
:::

AWS のインフラをコードで管理し、自動的に一貫した構成を作成・更新できる Infrastructure as Code（IaC）の基本的な理解にも役立ちます。

## 書こうと思ったきっかけ
筆者自身が AWS を学び始めたとき、ネット上のテンプレート例は多くあるものの、実際にどのリソースが何をしているのかが掴みにくいと感じました。

このテンプレートは、自分で ECS + RDS を構築する中で整理したものであり、初心者の方でも全体像をつかめるように解説を加えました。

## 実際のテンプレート

```test.yaml
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  # VPCの作成
  VPC:
    Type: "AWS::EC2::VPC"
    Properties:
      CidrBlock: "10.0.0.0/16"
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: "Name"
          Value: "honda-vpc"

  # サブネットの作成
  PublicSubnet1:
    Type: "AWS::EC2::Subnet"
    Properties:
      VpcId: !Ref VPC
      CidrBlock: "10.0.0.0/20"
      AvailabilityZone: "ap-northeast-1a"
      Tags:
        - Key: "Name"
          Value: "honda-subnet-public1-ap-northeast-1a"

  PublicSubnet2:
    Type: "AWS::EC2::Subnet"
    Properties:
      VpcId: !Ref VPC
      CidrBlock: "10.0.16.0/20"
      AvailabilityZone: "ap-northeast-1c"
      Tags:
        - Key: "Name"
          Value: "honda-subnet-public2-ap-northeast-1c"

  # インターネットゲートウェイの設定
  InternetGateway:
    Type: "AWS::EC2::InternetGateway"
    Properties:
      Tags:
        - Key: "Name"
          Value: "honda-igw"

  AttachInternetGateway:
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

  # ルートテーブルの設定
  PublicRouteTable:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: "Name"
          Value: "honda-rtb-public"

  PublicRoute:
    Type: "AWS::EC2::Route"
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref InternetGateway

  SubnetRouteTableAssociation1:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicRouteTable

  SubnetRouteTableAssociation2:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      SubnetId: !Ref PublicSubnet2
      RouteTableId: !Ref PublicRouteTable

  # ECS用セキュリティグループ（HTTPとMySQLに対応）
  SecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "Allow HTTP and MySQL traffic"
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: "tcp"
          FromPort: 80
          ToPort: 80
          CidrIp: "0.0.0.0/0"
        - IpProtocol: "tcp"
          FromPort: 8000
          ToPort: 8000
          CidrIp: "0.0.0.0/0"
        - IpProtocol: "tcp"
          FromPort: 3306
          ToPort: 3306
          CidrIp: "0.0.0.0/0"  # 本番では特定のRDSセキュリティグループに制限推奨

  # RDSのセキュリティグループ（必要に応じて制限可能）
  RDSSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "Allow ECS to access RDS"
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          SourceSecurityGroupId: !Ref SecurityGroup

  # RDSインスタンスの作成
  MyRDS:
    Type: "AWS::RDS::DBInstance"
    Properties:
      DBInstanceIdentifier: "honda-mysql-db"
      AllocatedStorage: 20
      DBInstanceClass: db.t4g.micro
      Engine: mysql
      EngineVersion: "8.0"
      MasterUsername: "youruser"
      MasterUserPassword: "yourpassword"
      VPCSecurityGroups:
        - !Ref RDSSecurityGroup
      DBName: "yourdatabase"
      DBSubnetGroupName: !Ref DBSubnetGroup
      PubliclyAccessible: false
      BackupRetentionPeriod: 0

  # DBサブネットグループ（RDSに必要、2つのAZにまたがる）
  DBSubnetGroup:
    Type: "AWS::RDS::DBSubnetGroup"
    Properties:
      DBSubnetGroupDescription: "Subnets for RDS"
      SubnetIds:
        - !Ref PublicSubnet1
        - !Ref PublicSubnet2
      DBSubnetGroupName: "honda-db-subnet-group"

  # ECSクラスターの作成
  ECSCluster:
    Type: "AWS::ECS::Cluster"
    Properties:
      ClusterName: "my-ecs-cluster"

  # ECSタスク定義（環境変数にRDS情報を追加）
  ECSTaskDefinition:
    Type: "AWS::ECS::TaskDefinition"
    Properties:
      Family: "my-app-task"
      RequiresCompatibilities:
        - FARGATE
      NetworkMode: "awsvpc"
      Cpu: "256"
      Memory: "512"
      ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn
      ContainerDefinitions:
        - Name: "my-app-container"
          Image: "xxx.dkr.ecr.ap-northeast-1.amazonaws.com/backend-repo"
          PortMappings:
            - ContainerPort: 8000
              HostPort: 8000
              Protocol: "tcp"
          Environment:
            - Name: MYSQL_HOST
              Value: !GetAtt MyRDS.Endpoint.Address
            - Name: MYSQL_USER
              Value: "youruser"
            - Name: MYSQL_PASSWORD
              Value: "yourpassword"
            - Name: MYSQL_DATABASE
              Value: "yourdatabase"

  # ECSタスク実行ロール
  ECSTaskExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service: "ecs-tasks.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: "ecsTaskExecutionPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "ecr:GetAuthorizationToken"
                  - "ecr:GetDownloadUrlForLayer"
                  - "ecr:BatchGetImage"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                  - "s3:GetObject"
                Resource: "*"

  # ECSサービスの作成
  ECSService:
    Type: "AWS::ECS::Service"
    Properties:
      Cluster: !Ref ECSCluster
      TaskDefinition: !Ref ECSTaskDefinition
      DesiredCount: 1
      LaunchType: "FARGATE"
      NetworkConfiguration:
        AwsvpcConfiguration:
          Subnets:
            - !Ref PublicSubnet1
            - !Ref PublicSubnet2
          SecurityGroups:
            - !Ref SecurityGroup
          AssignPublicIp: "ENABLED"
```


## 1. VPCとサブネットの作成

### VPC
- CIDR ブロック `10.0.0.0/16` を持つ仮想ネットワークを作成します。
- DNS サポートと DNS ホスト名の利用を有効化。
- タグ `Name: honda-vpc` が付与されます。

### サブネット
- `ap-northeast-1a` と `ap-northeast-1c` にまたがる2つのパブリックサブネットを作成。
- それぞれ `10.0.0.0/20` と `10.0.16.0/20` のCIDRブロック。

## 2. インターネット接続の構成

### インターネットゲートウェイ
- VPCと接続することで外部アクセスを可能にするためのゲートウェイです。

### ルートテーブル
- `0.0.0.0/0`（全ての宛先）へのルートをインターネットゲートウェイに設定。
- 作成したサブネットと関連付けられます。

## 3. セキュリティグループ

### ECS 用セキュリティグループ
- HTTP（ポート80）とアプリケーション用（ポート8000）、MySQL（ポート3306）へのアクセスを許可。
- 本番環境では IP 制限や他のセキュリティグループとの関連付けを推奨。

### RDS 用セキュリティグループ
- ECS セキュリティグループからの 3306 ポートアクセスのみを許可。

## 4. RDS（MySQL）構成

- インスタンス識別子 `honda-mysql-db`、インスタンスクラス `db.t4g.micro`。
- エンジン: MySQL 8.0。
- パスワードやユーザー名はベタ書きされており、Parameter Storeなどを使って安全に管理すべきです。
- DBサブネットグループは、異なるAZにあるサブネット2つを含む必要があります。

## 5. ECS クラスタとサービス

### ECS クラスタ
- 名前付き ECS クラスタを作成。

### ECS タスク定義
- `FARGATE` 対応。
- 環境変数に RDS 接続情報を設定。
- コンテナのポートは 8000 に設定。

### タスク実行ロール
- ECS タスクが ECR（コンテナレジストリ）やログサービス（CloudWatch Logs）にアクセスするための IAM ロール。

### ECS サービス
- 1つのタスクを常に稼働させる構成。
- パブリックサブネット上で稼働。
- セキュリティグループを指定し、パブリックIPを割り当てる設定（AssignPublicIp: ENABLED）。

## まとめ
このテンプレートでは、VPCからECS、RDSまで、Webアプリケーションを構成するための基本的なインフラ構成が整っています。

:::note info
***セキュリティやスケーラビリティを高めるためには、以下の点を追加・改善するとより実践的になります。***
:::

- Secrets Manager で DB パスワードの管理
- ALB（Application Load Balancer）の追加
- Auto Scaling の設定
- CloudWatch メトリクスやログのより詳細な設定

この構成は学習用途としても、小規模なプロジェクトのベースとしても適しています。テンプレートの全体像を理解し、インフラ構築の第一歩として活用いただければ幸いです！