🎃

【入門#3】Terraformでタスク定義を作ろう

に公開

今回の目標

Terraformを使って、ECSのタスク定義を作ります。
将来的には下記のシステム構成図になるように進めていきますが、今回はその中のECSタスク定義に絞って解説します。

◯今回のシステム構成図の全体像

関連記事:https://zenn.dev/alt_tanuki/articles/7c1f97da33496b

前提

下記記事を完了し、Terraformが使用できる状態になっていること。

【入門#1】Terraformをインストールしよう!(tfenv事前準備編)

ECSは将来的にGithubActionで自動デプロイする想定です。
SSMからコンテナにコンソール接続もできるように行っていきます。

タスク定義には下記を行っていきます。
・S3の環境変数ファイルを参照
・タスク実行ロールとタスクロールを作成の上、アタッチ

ではやっていきましょう!

タスク定義が参照するS3の環境変数ファイルを用意する

storage.tfに下記を記述していきます。
aws_s3_objectでローカルのcask_tokyo.envをS3にアップロードします。
※cask_tokyo.envは.gitignoreに追記しておいたほうがいいですね。

# 環境変数用バケット作成
resource "aws_s3_bucket" "cask_tokyo_ecs_s3_bucket_config" {
  bucket = "${var.s3_bucket_name}-config"
}

# 環境変数ファイルの作成
resource "aws_s3_object" "s3_env_file" {
  bucket = aws_s3_bucket.cask_tokyo_ecs_s3_bucket_config.id

  key = "cask_tokyo.env"
  source = "cask_tokyo.env"
  content_type = "text/html"
  etag = filemd5("cask_tokyo.env")
}

.envファイルの例

RAILS_ENV=production

# Database
DATABASE_NAME=xxxx
DATABASE_HOST=xxxx
DATABASE_USERNAME=xxxx
DATABASE_PASSWORD=xxxxx

# S3
S3_BACKET_NAME=cask-tokyo-ecs

このあと環境変数を差し替える場合は、手元のcask_tokyo.envを書き換えて
Terraform applyをすると差し替えできます。!

タスク定義が参照するタスク実行ロール/タスクロールを用意する

compute.tfに下記のIAMロールを追記していきます。

◯タスク実行ロール
・AmazonS3FullAccess
・AmazonSSMFullAccess
・SSM用インラインポリシー(iam:GetRole、iam:PassRole)

タスクロール
・AmazonECSTaskExecutionRolePolicy
・AmazonRDSFullAccess
・AmazonS3FullAccess
・AWSAppRunnerServicePolicyForECRAccess
・CloudWatchログ用インラインポリシー(logs:CreateLogStream、logs:DescribeLogStreams、logs:PutLogEvents)

# ECSタスクロール
resource "aws_iam_role" "cask_tokyo_ecs_esc_task_role" {
  name = "cask-tokyo-ecs-ecs-task-role"

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

resource "aws_iam_role_policy_attachment" "cask_tokyo_ecs_esc_task_role_s3_full_access" {
  role       = aws_iam_role.cask_tokyo_ecs_esc_task_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess"
}

resource "aws_iam_role_policy_attachment" "cask_tokyo_ecs_esc_task_role_ssm_full_access" {
  role       = aws_iam_role.cask_tokyo_ecs_esc_task_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMFullAccess"
}

resource "aws_iam_role_policy" "cask_tokyo_ecs_task_inline_policy" {
  name = "PassRole"
  role = aws_iam_role.cask_tokyo_ecs_esc_task_role.id

  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Effect = "Allow",
        Action = [
          "iam:GetRole",
          "iam:PassRole"
        ],
        Resource = "arn:aws:iam::*:role/service-role/AmazonEC2RunCommandRoleForManagedInstances"
      }
    ]
  })
}

# ECSタスク実行ロール
resource "aws_iam_role" "cask_tokyo_ecs_task_exec_role" {
  name = "cask-tokyo-ecs-ecs-task-exec-role"

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

# ECSタスク実行ロール用ポリシー
resource "aws_iam_role_policy_attachment" "cask_tokyo_ecs_task_exec_role_ecs_policy" {
  role       = aws_iam_role.cask_tokyo_ecs_task_exec_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}

resource "aws_iam_role_policy_attachment" "cask_tokyo_ecs_task_exec_role_rds_full_access" {
  role       = aws_iam_role.cask_tokyo_ecs_task_exec_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonRDSFullAccess"
}

resource "aws_iam_role_policy_attachment" "cask_tokyo_ecs_task_exec_role_s3_full_access" {
  role       = aws_iam_role.cask_tokyo_ecs_task_exec_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess"
}

resource "aws_iam_role_policy_attachment" "cask_tokyo_ecs_task_exec_role_apprunner_ecr_access" {
  role       = aws_iam_role.cask_tokyo_ecs_task_exec_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSAppRunnerServicePolicyForECRAccess"
}

resource "aws_iam_role_policy" "cask_tokyo_ecs_task_exec_inline_policy" {
  name = "CreateLogRole"
  role = aws_iam_role.cask_tokyo_ecs_task_exec_role.id

  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Effect = "Allow",
        Action = [
          "logs:CreateLogStream",
          "logs:DescribeLogStreams",
          "logs:PutLogEvents"
        ],
        Resource = "*"
      }
    ]
  })
}

タスク定義を作成する

compute.tfに下記タスク定義を追記します。
費用は安くしたいので、いったんサーバースペックは小さめにします。

# ECS(タスク定義)
resource "aws_ecs_task_definition" "latest-trial" {
  family                   = var.project_name
  requires_compatibilities = ["FARGATE"]
  network_mode             = "awsvpc"
  cpu                      = 512
  memory                   = 1024

  # タスクロール
  task_role_arn      = aws_iam_role.cask_tokyo_ecs_esc_task_role.arn

  # タスク実行ロール
  execution_role_arn = aws_iam_role.cask_tokyo_ecs_task_exec_role.arn

  container_definitions = jsonencode([
    {
      # NOTE: Githubデプロイで都度変わる
      image = "${aws_ecr_repository.cask_tokyo_ecs_ecr.repository_url}:latest"

      name       = "task"
      essenntial = "true"
      portMappings = [{
        name          = "nginx-80-tcp"
        containerPort = 80
        protocol      = "tcp"
        appProtocol   = "http"
      }]

      # S3環境変数ファイルを設定する
      environmentFiles = [
        {
          type  = "s3"
          value = "arn:aws:s3:::${aws_s3_bucket.cask_tokyo_ecs_s3_bucket_config.bucket}/${aws_s3_object.s3_env_file.key}"
        }
      ]
    }
  ])
}

以上!

terraform applyしてみよう。
あららほい!!

タスク定義は少しむずかしいですね。。
これを使ってどんどんECS環境を構築していきましょう!

下記の記事を目次として、続きをどんどん構築を行っていきますので、今回作成したタスク定義の使い方などが気になる方はぜひ見に来てください!

https://zenn.dev/alt_tanuki/articles/7c1f97da33496b

Discussion