当 ECS 中有多个服务仅命令不同时,如何避免 terraform 重复?

How to avoid duplication in terraform when having multiple services in ECS which differ only in the command?

我们通过定义 template_file 在 Terraform 中创建 ECS 服务,该 template_file 使用所有需要的变量填充任务定义 JSON 模板。然后使用呈现的 template_file 创建 aws_ecs_task_definition。通过此任务定义,创建了 aws_ecs_service

data "template_file" "web" {
  template = "${file("${path.module}/tasks/web.json")}"

  vars {
    ...
  }
}

resource "aws_ecs_task_definition" "web" {
  container_definitions    = "${data.template_file.web.rendered}"
  requires_compatibilities = ["FARGATE"]
  ...
}

data "aws_ecs_task_definition" "web" {
  task_definition = "${aws_ecs_task_definition.web.family}"
}

resource "aws_ecs_service" "web" {
  name            = "web"
  task_definition = "${aws_ecs_task_definition.web.family}:${max("${aws_ecs_task_definition.web.revision}", "${data.aws_ecs_task_definition.web.revision}")}"
  ...
}

还有其他服务的任务定义与第一个服务几乎相同,只是与另一个命令有细微差别(例如启动 sidekiq 而不是 Web 应用程序)。

除了复制所有内容(JSON 模板,template_file 以及所有定义的变量,aws_ecs_task_definitionaws_ecs_service),还有其他方法吗?

Modules 是在 Terraform 中解决这个问题的主要方法。

如果将现有代码移动到单个文件夹中,则可以定义允许自定义该模块的变量,例如要传递给 ECS 服务的命令。

所以在你的情况下你可能有这样的事情:

modules/foo-service/main.tf

data "template_file" "web" {
  template = "${file("${path.module}/tasks/web.json")}"

  vars {
    # ...
    command = "${var.command}"
  }
}

resource "aws_ecs_task_definition" "web" {
  container_definitions    = "${data.template_file.web.rendered}"
  requires_compatibilities = ["FARGATE"]
  # ...
}

data "aws_ecs_task_definition" "web" {
  task_definition = "${aws_ecs_task_definition.web.family}"
}

resource "aws_ecs_service" "web" {
  name            = "web"
  task_definition = "${aws_ecs_task_definition.web.family}:${max("${aws_ecs_task_definition.web.revision}", "${data.aws_ecs_task_definition.web.revision}")}"
  # ...
}

modules/foo-service/variables.tf

variable "command" {}

staging/main.tf

module "foo_service_web" {
  source  = "../modules/foo-service"
  command = "bundle exec server"
}

module "foo_service_sidekiq" {
  source  = "../modules/foo-service"
  command = "bundle exec sidekiq"
}

扩展已接受的答案以显示如何同时删除由 template_file vars 块中定义的变量引起的重复(这些变量不会更改,因此必须在模块调用之间重复) .仅内联这些变量或使用默认值也不是解决方案,因为它们仍然会在项目之间发生变化,只是不在同一项目的服务内。我们可以使用局部变量来设置默认值并使用合并函数覆盖默认值:

main.tf

locals {
  task_variables = {
    image = "..."
    # lots of other variables
    command = "[\"nginx\", \"-g\", \"daemon off; error_log /dev/stdout info;\"]"
  }
}

# first invocation of the module, overriding the command
module "sidekiq" {
  source = "ecs_service"
  ...
  task_variables = "${merge(
    local.task_variables,
    map(
      "command", "[\"bash\", \"-c\", \"exec bundle exec sidekiq\"]",
    )
  )}"
}

# second invocation of the module, no overrides
module "web" {
  source = "ecs_service"
  task_variables = "${local.task_variables}"
}

模块ecs_service

variable "task_variables" {
  type = "map"
}

data "template_file" "web_task" {
  template = "${file("${path.module}/tasks/task_definition.json")}"

  vars = "${var.task_variables}"
}

resource "aws_ecs_task_definition" "web" {
  container_definitions    = "${data.template_file.web_task.rendered}"
  ...
}

data "aws_ecs_task_definition" "web" {
  task_definition = "${aws_ecs_task_definition.web.family}"
  ...
}

resource "aws_ecs_service" "web" {
  task_definition = "${aws_ecs_task_definition.web.family}:${max("${aws_ecs_task_definition.web.revision}", "${data.aws_ecs_task_definition.web.revision}")}"
  ...
}