AR ホームベーカリー

オイラのアウトプット用ホームベーカリー!

Terraform で Fargate 一式作ったときだけ db:create を実行する(そしてタスクを destroy した後に作り直しても実行されないようにする

タイトルがすべてです。 自分向けの備忘録を兼ねて。

どうするの例

こうする。 (variables とかタスク定義の json 記述は割愛しています)

しかしこれ書いたの 3 年前だからか、一部置き換えているとはいえめちゃくちゃ荒削りだなあ。

# db:create
# 話を簡単にするため、ネットワークはパブリックサブネットにしてあります
# 依存しているセキュリティグループやタスク定義、 variables などは実際に利用する際に別途必要です
# 何が必要なんだYo!と思ったらサッと眺めて、わかんなかったらコピペして plan すれば足りないやつを terraform が出してくれると思うよ

# ネットワーク定義
locals {
  some_service_migration = {
    network_config = {
      "awsvpcConfiguration": {
        "assignPublicIp" = "ENABLED"
        "subnets" = [
          aws_subnet.public_subnet_1a.id,
          aws_subnet.public_subnet_1c.id,
          aws_subnet.public_subnet_1d.id
        ]
        "securityGroups" = [
          aws_security_group.nginx.id
        ]
      }
    }
  }
}

# db:create のフォロー
resource "aws_ecs_task_definition" "db_create" {
  family = "${var.prefix}-db-create"
  requires_compatibilities = [ "FARGATE" ]
  network_mode = "awsvpc"
  task_role_arn = aws_iam_role.ecs_task_execution_role.arn
  execution_role_arn = aws_iam_role.ecs_task_execution_role.arn
  cpu = var.ecs_cpu
  memory = var.ecs_memory

  volume {
    name = "app"
  }

  container_definitions = templatefile("./task_definitions/db_create.json", {
    ecs_nginx_image_uri = var.ecs_nginx_image_uri,
    ecs_app_image_uri = var.ecs_app_image_uri,
    ecs_app_database_host = aws_db_instance.rds_instance.address,
    ecs_app_secret_database_username = aws_ssm_parameter.rds_username.arn,
    ecs_app_secret_database_password = aws_ssm_parameter.rds_password.arn,
    ecs_app_secret_rails_master_key = aws_ssm_parameter.rails_master_key.arn,
    ecs_app_secret_secret_key_base = aws_ssm_parameter.rails_secret_key_base.arn
    }
  )
}

resource "null_resource" "db_create" {
  # db:create は対象が存在していれば already exists で運用セーフなので、重複実行されても良い
  triggers = {
    cluster_arn = aws_ecs_cluster.ecs_cluster.arn
    task_definition_arn = aws_ecs_task_definition.db_create.arn
  }

  provisioner "local-exec" {
    command = <<EOT
aws ecs run-task --task-definition ${aws_ecs_task_definition.db_create.arn} --launch-type FARGATE --cluster ${aws_ecs_cluster.ecs_cluster.name} --network-configuration '${jsonencode(local.some_service_migration.network_config)}' --started-by "Terraform" --profile ${var.profile_name}
EOT
  }

  depends_on = [
    aws_ecs_task_definition.db_create,
    aws_ecs_cluster.ecs_cluster,
    aws_db_instance.rds_instance,
    aws_ssm_parameter.rds_username,
    aws_ssm_parameter.rds_password,
    aws_ssm_parameter.rails_master_key,
    aws_ssm_parameter.rails_secret_key_base
  ]
}

null_resource と local-exec を組み合わせる

null_resource は一度実行されると、triggers に指定したリソースが変更 (destroy など) されるまで再実行されない。 これを逆手に取る事で db:create を構築時一回だけ流す、というように利用できる。 任意でもう一回実行したければ、 ECS のクラスタ自体とタスク定義を両方破壊してもう一度作り直せばいい。

この状態だと、タスクなりクラスタなり破壊しただけでは triggers 両方変更されているわけではないので db:create は再実行されないので、概ね掲題の要素は満たせているはず。

db:create したいモチベーション

基本的に RDBMS の作り直しという風情なので、ECS も一緒に壊しても大丈夫だろ、という考えでこうしている。 migrate は手動でやるか、 CD から ecspresso なりで叩くなりか、手動で ecs exec コンテナログインして云々か、まあいろいろ。

レプリカなり増やす時は db:create いらないはずなのでこれで、というトコに落ち着いた。

参考

qiita.com