RailsアプリをRDSと連携させてECSにデプロイする

## 1.AWSアカウントIDの設定
アカウントIDを環境変数に保存し、後の操作で使用できるようにします。

```bash
export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
```

---

## 2.IAM関連
ECSタスクがCloudWatchなどの他のAWSサービスにアクセスするためのロールを作成します。

```bash
cat << EOF > trust-policy.json
{
"Version": "2012-10-17",
"Statement": [
  {
 "Effect": "Allow",
 "Principal": {
   "Service": "ecs-tasks.amazonaws.com"
 },
 "Action": "sts:AssumeRole"
  }
]
}
EOF
```

```bash
aws iam create-role --role-name hello-world-rails-rdb-task-execution-role --assume-role-policy-document file://trust-policy.json
aws iam attach-role-policy --role-name hello-world-rails-rdb-task-execution-role --policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
```

---

## 3.ネットワーク設定
VPC、サブネット、インターネットゲートウェイ、ルートテーブルなどのネットワーク構成を設定します。

### VPCの作成
```bash
aws ec2 create-vpc --cidr-block 10.0.0.0/16 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=hello-world-rails-rdb-vpc}]'
export VPC_ID=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=hello-world-rails-rdb-vpc" --query "Vpcs[0].VpcId" --output text)

aws ec2 modify-vpc-attribute --vpc-id $VPC_ID --enable-dns-support "{\"Value\":true}"
aws ec2 modify-vpc-attribute --vpc-id $VPC_ID --enable-dns-hostnames "{\"Value\":true}"
```

### サブネットの作成
```bash
aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.1.0/24 --availability-zone ap-northeast-1a \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=hello-world-rails-rdb-public-subnet-1}]'

aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.2.0/24 --availability-zone ap-northeast-1c \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=hello-world-rails-rdb-public-subnet-2}]'

export SUBNET_ID_1=$(aws ec2 describe-subnets --filters "Name=tag:Name,Values=hello-world-rails-rdb-public-subnet-1" --query "Subnets[0].SubnetId" --output text)
export SUBNET_ID_2=$(aws ec2 describe-subnets --filters "Name=tag:Name,Values=hello-world-rails-rdb-public-subnet-2" --query "Subnets[0].SubnetId" --output text)
```

### サブネットのパブリックIP自動割り当ての設定
```
aws ec2 modify-subnet-attribute --subnet-id ${SUBNET_ID_1} --map-public-ip-on-launch
aws ec2 modify-subnet-attribute --subnet-id ${SUBNET_ID_2} --map-public-ip-on-launch
```

### インターネットゲートウェイの作成とアタッチ
```bash
aws ec2 create-internet-gateway --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=hello-world-rails-rdb-igw}]'
export IGW_ID=$(aws ec2 describe-internet-gateways --filters "Name=tag:Name,Values=hello-world-rails-rdb-igw" --query "InternetGateways[0].InternetGatewayId" --output text)
aws ec2 attach-internet-gateway --vpc-id $VPC_ID --internet-gateway-id $IGW_ID
```

### ルートテーブルの作成とサブネットへの関連付け
```bash
aws ec2 create-route-table --vpc-id $VPC_ID --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=hello-world-rails-rdb-route-table}]'
export ROUTE_TABLE_ID=$(aws ec2 describe-route-tables --filters "Name=tag:Name,Values=hello-world-rails-rdb-route-table" --query "RouteTables[0].RouteTableId" --output text)
aws ec2 create-route --route-table-id $ROUTE_TABLE_ID --destination-cidr-block 0.0.0.0/0 --gateway-id $IGW_ID
aws ec2 associate-route-table --route-table-id $ROUTE_TABLE_ID --subnet-id $SUBNET_ID_1
aws ec2 associate-route-table --route-table-id $ROUTE_TABLE_ID --subnet-id $SUBNET_ID_2
```

---

## 4.セキュリティグループの作成
ALBとECSタスクに必要なセキュリティグループを作成し、各種アクセスを設定します。

### ALB用セキュリティグループ
```bash
aws ec2 create-security-group --group-name hello-world-rails-rdb-alb-sg \
  --description "ALB security group for HTTP access" --vpc-id $VPC_ID

export ALB_SG_ID=$(aws ec2 describe-security-groups --filters "Name=group-name,Values=hello-world-rails-rdb-alb-sg" --query "SecurityGroups[0].GroupId" --output text)

aws ec2 authorize-security-group-ingress --group-id $ALB_SG_ID \
  --protocol tcp --port 80 --cidr 0.0.0.0/0
```

### ECSおよびRDS用セキュリティグループ
```bash
aws ec2 create-security-group --group-name hello-world-rails-rdb-ecs-rds-sg \
  --description "ECS and RDS security group for ALB access" --vpc-id $VPC_ID

export ECS_RDS_SG_ID=$(aws ec2 describe-security-groups --filters "Name=group-name,Values=hello-world-rails-rdb-ecs-rds-sg" --query "SecurityGroups[0].GroupId" --output text)

aws ec2 authorize-security-group-ingress --group-id $ECS_RDS_SG_ID \
  --protocol tcp --port 3000 --source-group $ALB_SG_ID

aws ec2 authorize-security-group-ingress --group-id $ECS_RDS_SG_ID \
  --protocol tcp --port 3306 --source-group $ECS_RDS_SG_ID
```

---

## 5.RDSインスタンスの作成
### サブネットグループの作成
上記の `--db-subnet-group-name` に使うため、サブネットグループを作成する必要があります。
```bash
aws rds create-db-subnet-group \
    --db-subnet-group-name hello-world-rails-rdb-subnet-group \
    --db-subnet-group-description "Subnet group for hello-world-rails-rdb" \
    --subnet-ids $SUBNET_ID_1 $SUBNET_ID_2
```

### RDSインスタンスの生成
完了後にエンドポイント情報を記録します。(数分かかる)
```
aws rds create-db-instance \
    --db-instance-identifier hello-world-rails-rdb-db \
    --db-instance-class db.t3.micro \
    --engine mysql \
    --allocated-storage 20 \
    --vpc-security-group-ids $ECS_RDS_SG_ID \
    --db-subnet-group-name hello-world-rails-rdb-subnet-group \
    --master-username db_user \
    --master-user-password password123 \
    --backup-retention-period 7 \
    --no-multi-az \
    --storage-type gp2 \
    --publicly-accessible \
    --port 3306 \
    --db-name rails_db \
    --region ap-northeast-1
```

下記のコマンドで値が返ってきたら作成完了
```
aws rds describe-db-instances --db-instance-identifier hello-world-rails-rdb-db --query "DBInstances[0].Endpoint.Address" --output text
```

### 環境変数の設定
RDSインスタンスが作成されたら、CloudShellで以下のコマンドを実行して、データベース接続に必要な環境変数を設定します。
```
export RDS_HOSTNAME=$(aws rds describe-db-instances --db-instance-identifier hello-world-rails-rdb-db --query "DBInstances[0].Endpoint.Address" --output text)

export RDS_PORT=3306
export RDS_DB_NAME="rails_db"
export RDS_USERNAME="db_user"
export RDS_PASSWORD="password123"
```

---

## 6.Railsアプリのデータベース作成
`config/database.yml`に以下の設定を追加し、RDSと接続できるようにします。環境変数を使用することで、デプロイ先の環境に応じた設定が可能です。

#### `config/database.yml` の設定例
```yaml
production:
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: <%= ENV.fetch("RDS_USERNAME") %>
  password: <%= ENV.fetch("RDS_PASSWORD") %>
  host: <%= ENV.fetch("RDS_HOSTNAME") %>
  port: <%= ENV.fetch("RDS_PORT") %>
  database: <%= ENV.fetch("RDS_DB_NAME") %>
```

---

## 7.ECRリポジトリの作成とDockerイメージのプッシュ
Dockerイメージを保存するリポジトリをECRで作成し、Railsアプリのイメージをビルドしてプッシュします。

### リポジトリの作成
```bash
aws ecr create-repository --repository-name hello-world-rails-rdb-repo
```

### Dockerイメージのビルドとプッシュ（ローカル）
```bash
export ACCOUNT_ID=<aws_account_id>
export RDS_PORT=3306
export RDS_DB_NAME="rails_db"
export RDS_USERNAME="db_user"
export RDS_PASSWORD="password123"

docker buildx build --platform linux/amd64 -t hello-world-rails-rdb:latest \
  --build-arg RDS_USERNAME=$RDS_USERNAME \
  --build-arg RDS_PASSWORD=$RDS_PASSWORD \
  --build-arg RDS_HOSTNAME=$RDS_HOSTNAME \
  --build-arg RDS_PORT=$RDS_PORT .
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com
docker tag hello-world-rails-rdb:latest ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/hello-world-rails-rdb-repo:latest
docker push ${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com/hello-world-rails-rdb-repo:latest
```

---

## 8.ロードバランサー関連
ALBおよびターゲットグループを設定し、トラフィックのルーティング設定を行います。

### ターゲットグループの作成
```bash
aws elbv2 create-target-group --name hello-world-rails-rdb-tg --protocol HTTP --port 3000 \
  --vpc-id $VPC_ID --target-type ip
export TG_ARN=$(aws elbv2 describe-target-groups --names hello-world-rails-rdb-tg --query "TargetGroups[0].TargetGroupArn" --output text)
```

### ALBの作成と設定
```bash
aws elbv2 create-load-balancer --name hello-world-rails-rdb-alb \
  --subnets $SUBNET_ID_1 $SUBNET_ID_2 \
  --security-groups $ALB_SG_ID \
  --scheme internet-facing \
  --type application \
  --ip-address-type ipv4
export ALB_ARN=$(aws elbv2 describe-load-balancers --names hello-world-rails-rdb-alb --query "LoadBalancers[0].LoadBalancerArn" --output text)
```

### ALBリスナーの作成
```bash
aws elbv2 create-listener --load-balancer-arn $ALB_ARN \
  --protocol HTTP --port 80 \
  --default-actions Type=forward,TargetGroupArn=$TG_ARN
```

---

## 9.ECSクラスターとタスク定義
クラスターを作成し、タスク定義を登録します。

### ECSクラスターの作成
```bash
aws ecs create-cluster --cluster-name hello-world-rails-rdb-cluster
```

### ECSタスク定義の作成
```bash
aws ecs register-task-definition --family hello-world-rails-rdb-task \
  --network-mode awsvpc --requires-compatibilities FARGATE \
  --execution-role-arn arn:aws:iam::$ACCOUNT_ID:role/hello-world-rails-rdb-task-execution-role \
  --cpu "256" --memory "512" \
  --container-definitions '[
    {
      "name": "hello-world-rails-rdb-container",
      "image": "'$ACCOUNT_ID'.dkr.ecr.ap-northeast-1.amazonaws.com/hello-world-rails-rdb-repo:latest",
      "portMappings": [
        {
          "containerPort": 3000,
          "protocol": "tcp"
        }
      ]
    },
    {
      "name": "hello-world-rails-rdb-migration",
      "image": "'$ACCOUNT_ID'.dkr.ecr.ap-northeast-1.amazonaws.com/hello-world-rails-rdb-repo:latest",
      "essential": false,
      "command": ["rails", "db:migrate"]
    }
  ]'
```

---

## 10.ECSサービスの作成
ECSサービスを作成し、ALBと連携してトラフィックの処理を管理します。

```bash
aws ecs create-service --cluster hello-world-rails-rdb-cluster --service-name hello-world-rails-rdb-service \
  --task-definition hello-world-rails-rdb-task --desired-count 1 --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=['$SUBNET_ID_1','$SUBNET_ID_2'],securityGroups=['$ECS_RDS_SG_ID'],assignPublicIp='ENABLED'}" \
  --load-balancers "targetGroupArn=$TG_ARN,containerName=hello-world-rails-rdb-container,containerPort=3000"
```

---

## 11.CloudWatchロググループの作成
ECSタスクのログを保存するCloudWatchロググループを作成します。

```bash
aws logs create-log-group --log-group-name /ecs/hello-world-rails-rdb-task --region ap-northeast-1
```

---

## 12.ALBのDNS名を取得してアクセス
```bash
aws elbv2 describe-load-balancers --names hello-world-rails-rdb-alb --query "LoadBalancers[0].DNSName" --output text
```

---

## 13. ECSの更新手順
Dockerイメージの再ビルドとプッシュ
```bash
docker buildx build --platform linux/amd64 -t hello-world-rails-rdb:latest .
docker tag hello-world-rails-rdb:latest $ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/hello-world-rails-rdb-repo:latest
docker push $ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/hello-world-rails-rdb-repo:latest
```

タスク定義の更新
```bash
aws ecs register-task-definition --family hello-world-rails-rdb-task \
  --network-mode awsvpc --requires-compatibilities FARGATE \
  --execution-role-arn arn:aws:iam::$ACCOUNT_ID:role/hello-world-rails-rdb-task-execution-role \
  --cpu "256" --memory "512" \
  --container-definitions '[
    {
      "name": "hello-world-rails-rdb-container",
      "image": "'$ACCOUNT_ID'.dkr.ecr.ap-northeast-1.amazonaws.com/hello-world-rails-rdb-repo:latest",
      "portMappings": [
        {
          "containerPort": 3000,
          "protocol": "tcp"
        }
      ]
    },
    {
      "name": "hello-world-rails-rdb-migration",
      "image": "'$ACCOUNT_ID'.dkr.ecr.ap-northeast-1.amazonaws.com/hello-world-rails-rdb-repo:latest",
      "essential": false,
      "command": ["rails", "db:migrate"]
    }
  ]'
```

サービスの更新
```bash
aws ecs update-service --cluster hello-world-rails-rdb-cluster --service hello-world-rails-rdb-service --force-new-deployment
```

---