☁️

TerraformでFargateを構築する

に公開

はじめに

インフラエンジニアとしてTerraform運用を行っているのですが、 TerraformやFargateもだいぶ浸透してきて、導入している企業も増えてきているように感じます。 そのようなケースのサンプルとして公開したいと思います。

ファイル構成

ファイル構成は以下としています。

├── logs
│   ├── backend.tf
│   ├── main.tf
│   ├── outputs.tf
│   ├── provider.tf
│   └── variables.tf
├── buckets
│   ├── backend.tf
│   ├── main.tf
│   ├── outputs.tf
│   ├── provider.tf
│   └── variables.tf
├── ecr
│   ├── backend.tf
│   ├── main.tf
│   ├── outputs.tf
│   ├── provider.tf
│   └── variables.tf
└── fargate
    ├── backend.tf
    ├── files
    │   └── container\_definition.json
    ├── main.tf
    ├── outputs.tf
    ├── provider.tf
    └── variables.tf

ポイントとしては、logとFargateのtfファイルの階層を分けている事です。 分ける理由としては、同階層にしてしまうとTerraform destoryした時にログまで消えてしまって後悔。。。というケースが起こるかなと思い階層を分けています。 同様の理由としてS3bucket、ECRも階層を分けています。

また、files以下にコンテナ定義のjsonファイル等を入れています。

Fargate

まずはクラスターから

resource "aws\_ecs\_cluster" "default" {
  name = local.service\_name
}

タスク定義は以下のようになっています。 定義の内容をjsonファイルに記載しておいて、ECRのARNなどはdataソースを使って記入しています。

data "template\_file" "default" {
  template = file("files/container\_definition.json")
  vars = {
    ECR\_ARN      = data.terraform\_remote\_state.ecr.outputs.default\["repository\_url"\]
    SERVICE\_NAME = local.service\_name
  }
}
resource "aws\_ecs\_task\_definition" "default" {
  family                   = local.service\_name
  container\_definitions    = data.template\_file.default.rendered
  task\_role\_arn            = aws\_iam\_role.default.arn
  network\_mode             = "awsvpc"
  execution\_role\_arn       = aws\_iam\_role.default.arn
  cpu                      = 512
  memory                   = 1024
  requires\_compatibilities = \["FARGATE"\]
}

タスク定義のjsonファイルは以下のようになっています。

\[
  {
    "name": "${SERVICE\_NAME}",
    "image": "${ECR\_ARN}",
    "essential": true,
    "portMappings": \[
      {
        "containerPort": 80,
        "hostPort": 80
      }
    \],
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
        "awslogs-region": "ap-northeast-1",
        "awslogs-group": "/ecs/${SERVICE\_NAME}",
        "awslogs-stream-prefix": "${SERVICE\_NAME}"
      }
    }
  }
\]

Ecsのサービスは以下のようになっています。

resource "aws\_ecs\_service" "default" {
  name            = local.service\_name
  cluster         = aws\_ecs\_cluster.default.id
  task\_definition = aws\_ecs\_task\_definition.default.arn
  desired\_count   = 2
  launch\_type     = "FARGATE"
  load\_balancer {
    target\_group\_arn = aws\_lb\_target\_group.default.arn
    container\_name   = local.service\_name
    container\_port   = 80
  }
  network\_configuration {
    subnets = \[
      data.terraform\_remote\_state.subnet.outputs.publib\_a\["id"\],
      data.terraform\_remote\_state.subnet.outputs.publib\_c\["id"\],
    \]
    security\_groups = \[
      aws\_security\_group.default.id
    \]
    assign\_public\_ip = true
  }
}

LB

resource "aws\_lb" "default" {
  name = local.service\_name
  internal           = false
  load\_balancer\_type = "application"
  subnets = \[
    data.terraform\_remote\_state.subnet.outputs.publib\_a\["id"\],
    data.terraform\_remote\_state.subnet.outputs.publib\_c\["id"\],
  \]
  security\_groups = \[
    aws\_security\_group.default.id,
  \]
  enable\_deletion\_protection = true
  access\_logs {
    bucket  = data.terraform\_remote\_state.buckets.outputs.default\["id"\]
    enabled = true
  }
}
resource "aws\_lb\_target\_group" "default" {
  name        = local.service\_name
  port        = 80
  protocol    = "HTTP"
  vpc\_id      = data.terraform\_remote\_state.subnet.outputs.default\["id"\],
  target\_type = "ip"
}

VPCとサブネットはバックエンドから参照しています。 また、security groupは別で作成しておいてください。 bucketについては、前述の通り階層を分けているためバックエンドから参照してください。 ターゲットグループのtarget_typeはFargateと連携するためにipに指定しておいてください。

LBへSSL証明書の適用 Certificate Managerに登録してあるSSL証明書を参照しています。

resource "aws\_lb\_listener" "default" {
  load\_balancer\_arn = aws\_lb.default.arn
  protocol          = "HTTPS"
  port              = "443"
  ssl\_policy        = "ELB\_SecurityPolicy-TLS-1-2-2017-01"
  certificate\_arn   = data.terraform\_remote\_state.certificate.outputs.default\["arn"\]
  default\_action {
    type             = "forward"
    target\_group\_arn = aws\_lb\_target\_group.default.arn
  }
}

80ポートへのアクセスはリダイレクトするようにします。

resource "aws\_lb\_listener" "redirect\_https" {
  load\_balancer\_arn = aws\_lb.default.arn
  port              = "80"
  protocol          = "HTTP"
  default\_action  {
    type = "redirect"
    redirect {
      port        = "443"
      protocol    = "HTTPS"
      status\_code = "HTTP\_301"
    }
  }
}

variables

最後に、variablesは以下のようになっています。

variable "environment" {
  default = "development"
}
locals {
  service\_name = "${var.environment}-service-name"
}

ここでlocalsを使っているのは、環境名などを変数に埋め込むためです。 特に必要ないかもしれないです。

最後に

これからTerraformを導入していきたい、Fargateを使ってみたいという方に少しでも参考になれば。。。

参考

dipテックブログ

Discussion