Terraform(Fargate)でNginxをデプロイ

# はじめに

Terraformの学習用健忘録です。


ディレクトリはこちらになります。

https://github.com/sugiyama404/practice_terrorm_nginx

# terrformの構成

## ファイル構成
```text:ファイル構成
.
├── Dockerfile
├── nginx
│   └── index.html
└── terraform
    ├── main.tf
    ├── modules
    │   ├── bash
    │   │   └── null_resource.tf
    │   ├── ecr
    │   │   └── ecr.tf
    │   ├── ecs
    │   │   ├── ecs.tf
    │   │   ├── ecs_service.tf
    │   │   └── ecs_task_definition.tf
    │   ├── iam
    │   │   ├── aws_iam_policy.tf
    │   │   ├── aws_iam_policy_attachment.tf
    │   │   ├── aws_iam_role.tf
    │   │   └── outputs.tf
    │   └── network
    │       ├── aws_vpc.tf
    │       ├── igw.tf
    │       ├── outputs.tf
    │       ├── route_table.tf
    │       ├── security_group.tf
    │       ├── security_group_rule.tf
    │       └── subnet.tf
    ├── terraform.tfstate
    ├── terraform.tfstate.backup
    └── variables.tf
```


## ネットワーク構成

ローカルからECRにpushされたdockerimageをfargateでpullしデプロイするという手法をとります。

![fargate.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/af0b05b3-f1ee-9e62-ec6e-626dc8d5cecd.png)


## DockerfileとHTML

画面にHello world!!!!!!!!!!!と出力する簡単な構成にしています。

```dockerfile:fargate
FROM nginx:latest

COPY ./nginx /usr/share/nginx/html
```

```html:index.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
</head>

<body>
    <div style="  line-height: 200px;height: 200px;text-align: center;">
        <p style="  line-height: 1.5;display: inline-block;vertical-align: middle;">
            Hello world!!!!!!!!!!!
        </p>
    </div>
</body>

</html>

```

## main.tf

各output.tfは他のモジュールにmain.tfを経由してデータを転送するためのものですが、
説明は省きます。

```terraform:main.tf
provider "aws" {
  region      = var.region
}

# IAM
module "iam" {
  source = "./modules/iam"
}

# ECR
module "ecr" {
  source = "./modules/ecr"
  image_name = var.image_name
}

# Null Resource
module "after_ecr" {
  source = "./modules/bash"
  region = var.region
  image_name = var.image_name
}

# network
module "network" {
  source = "./modules/network"
  app_name = var.app_name
}

# ECS
module "ecs" {
  source = "./modules/ecs"
  app_name = var.app_name
  vpc_id       = module.network.security-group-id
  subnet_id    = module.network.subnet-public-1a-id
  aws_iam_role =  module.iam.aws_iam_role

}

```

## IAM構成

```terraform:aws_iam_role.tf
resource "aws_iam_role" "newworld_role" {
  name = "newworld_role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid: ""
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "ecs-tasks.amazonaws.com"
        }
      },
    ]
  })
}
```

 "ecr:*"という記述がfargateによるpullを可能にしています。
これがないとpull時に400エラーを吐きます。

```terraform:aws_iam_policy.tf
resource "aws_iam_policy" "newworld_policy" {
  name        = "test_policy"

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          "ecr:*",
        ]
        Effect   = "Allow"
        Resource = "*"
      },
    ]
  })
}
```

```terraform:aws_iam_policy_attachment.tf
resource "aws_iam_policy_attachment" "attach" {
  name       = "iam-attach"
  roles      = ["${aws_iam_role.newworld_role.name}"]
  policy_arn = "${aws_iam_policy.newworld_policy.arn}"
}
```

## ECR構成

```terraform:aws_ecr_repository.tf
variable "image_name" {}

resource "aws_ecr_repository" "repository" {
  name                 = var.image_name
  image_tag_mutability = "IMMUTABLE"
  force_delete         = true

  image_scanning_configuration {
    scan_on_push = true
  }
}
```

## Null Resource構成

ECR構築後に自動でECRにdockerimageをpushします。
以下の記述をterraformで実行しています。


>aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin AWSのアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com
docker tag hello-world-from-ecs:latest AWSのアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/hello-world-from-ecs:latest
docker push AWSのアカウントID.dkr.ecr.ap-northeast-1.amazonaws.com/hello-world-from-ecs:latest

aws_caller_identityはAWSのアカウントIDを呼び出している。

```terraform:null_resource.tf
data "aws_caller_identity" "self" { }
variable "image_name" {}
variable "region" {}

resource "null_resource" "default" {
  provisioner "local-exec" {
    command = "aws ecr get-login-password --region ${var.region} | docker login --username AWS --password-stdin ${data.aws_caller_identity.self.account_id}.dkr.ecr.ap-northeast-1.amazonaws.com"
  }

  provisioner "local-exec" {
    command = "docker tag ${var.image_name}:latest ${data.aws_caller_identity.self.account_id}.dkr.ecr.ap-northeast-1.amazonaws.com/${var.image_name}:latest"
  }

  provisioner "local-exec" {
    command = "docker push ${data.aws_caller_identity.self.account_id}.dkr.ecr.ap-northeast-1.amazonaws.com/${var.image_name}:latest"
  }
}

```

## network構成

長いので割愛します。


## ECS構成


```terraform:ecr.tf
variable "app_name" {}

resource "aws_ecs_cluster" "newworld-cluster" {
  name = "${var.app_name}-cluster"
}

resource "aws_ecs_cluster_capacity_providers" "newworld_capacity_providers" {
  cluster_name = "${var.app_name}-cluster"
  capacity_providers = ["FARGATE"]

  default_capacity_provider_strategy {
    base              = 1
    weight            = 100
    capacity_provider = "FARGATE"
  }
}
```

```terraform:aws_ecs_service.tf
variable "vpc_id" {}
variable "subnet_id" {}

resource "aws_ecs_service" "newworld-service" {
  name            = "nginx-service"
  cluster         = aws_ecs_cluster.newworld-cluster.id
  task_definition = aws_ecs_task_definition.newworld-definition.arn
  desired_count   = 1

  network_configuration {
    security_groups  = [var.vpc_id]
    subnets          = ["${var.subnet_id}"]
    assign_public_ip = true
  }

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

```terraform:aws_ecs_task_definition.tf
variable "vpc_id" {}
variable "subnet_id" {}

resource "aws_ecs_service" "newworld-service" {
  name            = "nginx-service"
  cluster         = aws_ecs_cluster.newworld-cluster.id
  task_definition = aws_ecs_task_definition.newworld-definition.arn
  desired_count   = 1

  network_configuration {
    security_groups  = [var.vpc_id]
    subnets          = ["${var.subnet_id}"]
    assign_public_ip = true
  }

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




# 結果

![Screenshot 2023-02-25 at 20.44.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/4649b229-b3f7-3178-edf6-df84cdda381c.png)