AWS FargateをTerraformで構築する

概要

夏頃、AWS FargateとEKS祭りに参加してきた。
Fargateが東京リージョンにもやってきたので、そのためだ。
会社のチーム内でもAWS Fargateの機運が高まり、頑張って検証をしたので、まずはTerraformで構築する簡単なものを記録しておく。

なお、基本的には下記Terraformのドキュメントを参考にしている。

https://www.terraform.io/docs/providers/aws/r/ecr_repository.html
https://www.terraform.io/docs/providers/aws/r/ecs_cluster.html
https://www.terraform.io/docs/providers/aws/r/ecs_service.html
https://www.terraform.io/docs/providers/aws/r/ecs_task_definition.html

構成

PrivateサブネットにFargateを設置する。
コンテナにグローバルIPを付与することもできるが、EIPをつけずALBにぶら下げる場合はNAT Gateway経由でインターネットに接続するようなサブネット構成にすることになると思う。でないとイメージをpullできない!(ここでまずハマった)
今回はALBにぶら下げるようにする。

ALB

先にコンテナをぶら下げるALBを作成しておく。(グローバルIPを振る場合は不要)
SSL証明書の設定(ACM)等は自分の環境にあわせて修正する。
下記ではACMで取得した証明書を指定して、443へリダイレクトするようにしている。

resource "aws_lb" "staging-inamuu" {
  name                       = "staging-inamuu"
  internal                   = false
  load_balancer_type         = "application"
  security_groups            = ["${aws_security_group.staging-inamuu-alb.id}"]
  subnets                    = ["${aws_subnet.staging-public-1a.id}"]
  enable_deletion_protection = true

  tags {
    Env = "staging"
  }
}

resource "aws_lb_target_group" "staging-inamuu" {
  name                 = "staging-inamuu-lb-tg"
  port                 = 80
  protocol             = "HTTP"
  vpc_id               = "${aws_vpc.staging-inamuu-vpc.id}"
  target_type          = "ip"
  deregistration_delay = "10"
}

resource "aws_alb_listener" "staging-inamuu" {
  load_balancer_arn = "${aws_lb.staging-inamuu.arn}"
  port              = "80"
  protocol          = "HTTP"

  default_action {
    type = "redirect"

    redirect {
      port        = "443"
      protocol    = "HTTPS"
      status_code = "HTTP_301"
    }
  }
}

resource "aws_alb_listener" "staging-inamuu-https" {
  load_balancer_arn = "${aws_lb.staging-inamuu.arn}"
  port              = "443"
  protocol          = "HTTPS"
  ssl_policy        = "ELBSecurityPolicy-2015-05"
  certificate_arn   = "${aws_acm_certificate.staging-inamuu-com.arn}"

  default_action {
    target_group_arn = "${aws_lb_target_group.staging-inamuu.arn}"
    type             = "forward"
  }
}

ECR

次にECRのリポジトリを作る。
ここにアップロードしたDockerイメージがFargateで稼働する。
ECRにリポジトリを作ると、ログイン手順やpush手順等が表示されるのでそれに則って、Dockerイメージをpushする。(Fargate構築する前にpushしておくと吉)
このあたりからイメージのタグをどういった値(latest以外)にするかなどを検討しはじめた方が良さそう。

resource "aws_ecr_repository" "staging-inamuu-app" {
  name = "staging-inamuu-app"
}

クラスター

次にFargateの母体となるクラスターを構築する。

## Cluster
resource "aws_ecs_cluster" "staging-inamuu" {
  name = "staging-inamuu"
}

タスク定義(task definition)

実行するコンテナのスペックや、ログ設定、イメージ元を定義する。
これを元にコンテナが作成される。
注意点として、awslogsで指定したlogグループを事前にcloudwatchlogsで作成しておかないと、構築時にエラーになってしまう。地味にハマる。

$ cat task-definition/staging-inamuu-app.json
[
  {
    "image": "XXXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/staging-inamuu-app",
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
        "awslogs-group": "/ecs/staging-inamuu-app",
        "awslogs-region": "ap-northeast-1",
        "awslogs-stream-prefix": "ecs"
      }
    },
    "cpu": 256,
    "memory": 512,
    "networkMode": "awsvpc",
    "name": "staging-inamuu-app",
    "portMappings": [
      {
        "containerPort": 80,
        "protocol": "tcp"
      }
    ]
  }
]

上記でタスク定義の元を指定したら、下記でタスク定義を作成する。

## Task Definition
resource "aws_ecs_task_definition" "staging-inamuu-task" {
  family                   = "staging-inamuu"
  requires_compatibilities = ["FARGATE"]
  network_mode             = "awsvpc"
  task_role_arn            = "arn:aws:iam::${var.aws_account_id}:role/ecsTaskExecutionRole"
  execution_role_arn       = "arn:aws:iam::${var.aws_account_id}:role/ecsTaskExecutionRole"
  cpu                      = 256
  memory                   = 512
  container_definitions    = "${file("files/task-definitions/staging-inamuu-app.json")}"
}

サービス

最後に、サービスを設定する。
ここではFARGATEで起動することや、セキュリティーグループなどネットワーク周りを定義する。

## Service
resource "aws_ecs_service" "staging-inamuu-service" {
  cluster                            = "${aws_ecs_cluster.staging-inamuu.id}"
  deployment_minimum_healthy_percent = 50
  desired_count                      = "${var.aws_ecs_service_desired_count_app}"
  launch_type                        = "FARGATE"
  name                               = "staging-inamuu-service"

  lifecycle {
    ignore_changes = [
      "desired_count",
    ]
  }

  load_balancer {
    container_name   = "staging-inamuu-app"
    container_port   = "80"
    target_group_arn = "${aws_lb_target_group.staging-inamuu-app.arn}"
  }

  network_configuration {
    subnets = [
      "${aws_subnet.staging-inamuu-app-1a.id}",
    ]

    security_groups = [
      "${aws_security_group.staging-inamuu-app.id}",
    ]
  }

  task_definition = "${aws_ecs_task_definition.staging-inamuu-task.arn}"
}

ここまで来たら、あとはデプロイすればOK。
最初はとりあえずAWSコンソールから新しくタスク定義のリビジョンを作成すればOK.
自動的にデプロイされて、うまくいけば、ALB経由でコンテナにアクセスできるようになる。
細かい設定等についてはまた別途。