ECS on Fargate × TerraformでNginxを立てる

## はじめに
webアプリ(フロントとバックエンドを分けた)をいきなりECS on Fargate × Terraformで構築するのは難しそうなので、今回は難易度をグッと下げてNginxサーバーを立ち上げてみました。ゴールはタスクのパブリックIPからNginxサーバーの初期画面にアクセスできることです。

## 環境
MacBook Pro intel
macOS Monterey バージョン12.6
Terraform v1.4.5
Docker version 20.10.21

 VPC、サブネット、セキュリティグループ、タスク実行ロールはデフォルトを使用します。

## 手順
1. Dockerfileの作成
1. ECR リポジトリの作成
1. リポジトリにイメージをpush
1. ECS クラスターの作成
1. タスク定義の作成
1. サービスの作成

### Dockerfileの作成
NginxのDockerfileを作成します。
```dockerfile:Dockerfile
FROM nginx:latest

```

<br>

## ECR リポジトリの作成
空のプライベートリポジトリを作成し、ここで一旦applyします。
```terraform:main.tf
### ECR ###
resource "aws_ecr_repository" "nginx_repo" {
  name = "nginx-repo"
}
```

<br>

## リポジトリにイメージをpush
マネジメントコンソールのプッシュコマンドを参考に、作成したリポジトリにビルドしたDockerイメージをpushします。今回はバージョンが分かるようにタグ付けしました。
```bash
$ docker images
REPOSITORY                                                     TAG       IMAGE ID       CREATED        SIZE
123456789098.dkr.ecr.ap-northeast-1.amazonaws.com/nginx-img   v1        hog1e1b34az5   2 weeks ago    143MB
nginx-img                                                     v1        hog1e1b34az5   2 weeks ago    143MB

# リポジトリへpush
docker push 123456789098.dkr.ecr.ap-northeast-1.amazonaws.com/nginx-img:v1
```

<br>

## ECS クラスターの作成
これからECSを構築していきます。クラスターを作成します。
```terraform:main.tf
### クラスター ###
resource "aws_ecs_cluster" "cluster" {
  name = "cluster"
}
```

<br>

## タスク定義の作成
Webサーバーを立ち上げるだけなので特に設定する箇所はありませんが、container_definitionsのimageには正確なリポジトリのURIを指定します。オプションでタグ付け(-t)していない場合は、:latestを加えます。
```terraform:main.tf
### タスク定義 ###
resource "aws_ecs_task_definition" "task_def" {
  family                   = "task-def"
  cpu                      = 512
  memory                   = 1024
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  execution_role_arn       = module.ecs_task_execution_role.iam_role_arn

  container_definitions = jsonencode([
    {
      name      = "nginx-container"
      image     = "${aws_ecr_repository.nginx_repo.repository_url}:v1"
      memory    = 512
      essential = true
      portMappings = [
        {
          containerPort = 80
          hostPort      = 80
          protocol      = "tcp"
        }
      ]
    }
  ])
}

### タスク実行ロール ###
# ~省略~
```

aws_ecr_repository.リポジトリ名.repository_urlでは正確なURIを取得できません。

```bash:ターミナル

$ terraform state show aws_ecr_repository.nginx_repo
# aws_ecr_repository.nginx_repo:
resource "aws_ecr_repository" "nginx_repo" {
    arn                  = "arn:aws:ecr:ap-northeast-1:123456789098:repository/nginx-repo"
    id                   = "nginx-repo"
    image_tag_mutability = "MUTABLE"
    name                 = "nginx-repo"
    registry_id          = "123456789098"
    repository_url       = "123456789098.dkr.ecr.ap-northeast-1.amazonaws.com/nginx-repo"
    # ~省略~
}
```
<br>

## サービスの作成
最後の作業です！クラスターのサービスにタスク定義を読み込ませます。applyすればNginxにアクセスできます！
```terraform:main.tf
resource "aws_ecs_service" "service" {
  name                              = "service"
  cluster                           = aws_ecs_cluster.cluster.arn
  task_definition                   = aws_ecs_task_definition.task_def.arn
  desired_count                     = 1
  launch_type                       = "FARGATE"
  platform_version                  = "1.4.0"

  network_configuration {
    subnets          = data.aws_subnets.default.ids
    security_groups  = [data.aws_security_group.default.id]
    assign_public_ip = true
  }

    lifecycle {
    ignore_changes = [task_definition]
  }
}
```

### 

## あとがき
マネジメントコンソールを操作したのはECRのプッシュコマンドを確認した時だけです。凄いぞTerraform!(ただのタグの付け替えなので確認しなくても出来ますが笑)
今回の構成にCI/CDパイプラインを組み込むことで、アプリのソースコードを更新するたびわざわざDockerイメージをpushする手間も省けます。