地形 |对模块使用 "depends_on" 时 "apply" 阶段的循环依赖

Terraform | Cyclic dependency in "apply" stage when using "depends_on" for module

在模块上使用 depends_on 时,我在 terraform apply 阶段遇到循环依赖问题。

我申请的错误是:

* Cycle: aws_appautoscaling_policy.queue_depth_based_scale_out_policy, module.my_module.aws_ecs_task_definition.task_definition (destroy), aws_appautoscaling_policy.queue_depth_based_scale_in_policy

plan 阶段看起来非常好,plan 阶段没有错误。

我尝试使用以下命令识别图中的循环,

terraform graph -draw-cycles -module-depth=0 -type=plan  | dot -Tsvg > graph-plan.svg

plan 图中没有显示循环。 然后,尝试使用

来识别 apply 中的循环
terraform graph -draw-cycles -module-depth=0 -type=apply  | dot -Tsvg > graph-apply.svg

遗憾的是,此命令无法在图表中显示循环。

幸运的是,我能够使用以下命令在 apply 阶段图中看到循环,

terraform plan -out tfplan
terraform graph -draw-cycles -module-depth=0 tfplan | dot -Tsvg > graph-apply.svg

我图中的循环看起来像这样,

尽管如此,我仍然无法从图中找出这个循环的原因。

此外,问题似乎特别在于在模块上添加 depends_on。 因为我的模块中已经有几个 aws_appautoscaling_policy 依赖于 aws_appautoscaling_target 而依赖于 aws_ecs_service 因此最终依赖于 aws_ecs_task_definitionapply 用于此工作很好。

有一些 aws_appautoscaling_policy 与特定应用程序特别相关,因此我将它们单独添加(而不是作为模块的一部分),但由于自动缩放策略只能在服务注册为可扩展目标,因此我在模块上添加 depends_on,因为 aws_appautoscaling_target 是在模块中定义的。

这是我的模块代码片段,

resource "aws_ecs_task_definition" "task_definition" {
  family                = "${var.service_name}"
  container_definitions = "${var.container_definitions}"
  task_role_arn         = "${aws_iam_role.task_role.arn}"
  lifecycle {
    create_before_destroy = true
  }
}
resource "aws_ecs_service" "service" {
  name                               = "${var.service_name}"
  cluster                            = "${data.aws_ecs_cluster.ecs_cluster.arn}"
  task_definition                    = "${aws_ecs_task_definition.task_definition.arn}"
  deployment_minimum_healthy_percent = 50
  deployment_maximum_percent         = 100
  lifecycle {
    ignore_changes = ["desired_count"]
  }
}
resource "aws_appautoscaling_target" "ecs_target" {
  max_capacity       = "${var.max_scalabe_capacity}"
  min_capacity       = "${var.min_scalabe_capacity}"
  resource_id        = "service/${var.ecs_cluster_name}/${aws_ecs_service.service.name}"
  scalable_dimension = "ecs:service:DesiredCount"
  service_namespace  = "ecs"
}
resource "aws_appautoscaling_policy" "cpu_based_scale_in_policy" {
  name               = "${var.service_name}-${var.env}-cpu-based-scale-in-policy"
  policy_type        = "StepScaling"
  resource_id        = "service/${var.ecs_cluster_name}/${var.service_name}"
  scalable_dimension = "ecs:service:DesiredCount"
  service_namespace  = "ecs"
  step_scaling_policy_configuration {
    adjustment_type         = "ChangeInCapacity"
    cooldown                = "${var.scale_in_cooldown_period}"
    metric_aggregation_type = "Average"
    step_adjustment {
      metric_interval_upper_bound = "${var.scale_in_step_adjustment_upper_bound}"
      scaling_adjustment          = "${var.scale_in_step_adjustment_scaling_adjustment}"
    }
  }
  depends_on = ["aws_appautoscaling_target.ecs_target"]
}

这里是模块的用法,

module "my_module" {
  source = "GIT_URL_FOR_MODULE"
  VARIABLES_AS_NEEDED_BY_MODULE
}
resource "aws_appautoscaling_policy" "queue_depth_based_scale_in_policy" {
  name               = "${local.service_name}-${local.env}-queue-scale-in-policy-new"
  policy_type        = "StepScaling"
  resource_id        = "service/${local.ecs_cluster_name}/${local.service_name}"
  scalable_dimension = "ecs:service:DesiredCount"
  service_namespace  = "ecs"
  step_scaling_policy_configuration {
    adjustment_type         = "ChangeInCapacity"
    cooldown                = "${local.queue_scale_in_cooldown_period}"
    metric_aggregation_type = "Average"
    step_adjustment {
      metric_interval_upper_bound = "${local.queue_scale_in_step_adjustment_upper_bound}"
      scaling_adjustment          = "${local.queue_scale_in_step_adjustment_scaling_adjustment}"
    }
  }
  depends_on = ["module.my_module"]
}

管道中遵循的步骤是:

terraform get -update=true
terraform init
terraform taint -allow-missing -module=${MODULE_NAME} aws_ecs_task_definition.task_definition
terraform plan -out tfplan -input=false
terraform apply -input=false tfplan

很高兴了解这个循环背后的原因吗?

另一点要强调的是,当我们 destroy 一切并从头开始重新创建时,terraform apply 是成功的。只有当我 taint 我的任务定义并且在我的扩展策略中有一些更新放置在模块之外时才会观察到循环。

注意:在我的管道中,我确实污染了以前的任务定义,以确保使用新任务定义立即启动服务,否则不会立即使用新任务定义推出任务。

我已经成功摆脱了循环依赖。 这是使用的方法,

我没有依赖整个模块,而是在模块中添加了 aws_appautoscaling_target 的输出。 然后我只是在扩展策略中使用这个输出来确保创建隐式依赖。

这里是示例代码,

模块

resource "aws_ecs_task_definition" "task_definition" {
  family                = "${var.service_name}"
  container_definitions = "${var.container_definitions}"
  task_role_arn         = "${aws_iam_role.task_role.arn}"
  lifecycle {
    create_before_destroy = true
  }
}
resource "aws_ecs_service" "service" {
  name                               = "${var.service_name}"
  cluster                            = "${data.aws_ecs_cluster.ecs_cluster.arn}"
  task_definition                    = "${aws_ecs_task_definition.task_definition.arn}"
  deployment_minimum_healthy_percent = 50
  deployment_maximum_percent         = 100
  lifecycle {
    ignore_changes = ["desired_count"]
  }
}
resource "aws_appautoscaling_target" "ecs_target" {
  max_capacity       = "${var.max_scalabe_capacity}"
  min_capacity       = "${var.min_scalabe_capacity}"
  resource_id        = "service/${var.ecs_cluster_name}/${aws_ecs_service.service.name}"
  scalable_dimension = "ecs:service:DesiredCount"
  service_namespace  = "ecs"
}
resource "aws_appautoscaling_policy" "cpu_based_scale_in_policy" {
  name               = "${var.service_name}-${var.env}-cpu-based-scale-in-policy"
  policy_type        = "StepScaling"
  resource_id        = "service/${var.ecs_cluster_name}/${var.service_name}"
  scalable_dimension = "ecs:service:DesiredCount"
  service_namespace  = "ecs"
  step_scaling_policy_configuration {
    adjustment_type         = "ChangeInCapacity"
    cooldown                = "${var.scale_in_cooldown_period}"
    metric_aggregation_type = "Average"
    step_adjustment {
      metric_interval_upper_bound = "${var.scale_in_step_adjustment_upper_bound}"
      scaling_adjustment          = "${var.scale_in_step_adjustment_scaling_adjustment}"
    }
  }
  depends_on = ["aws_appautoscaling_target.ecs_target"]
}
output "scalable_target_id" {
  value = "${aws_appautoscaling_target.ecs_target.id}"
}

注意模块中添加的输出代码块。 这里是模块的用法,其中模块的输出被消耗在扩展策略名称中。

module "my_module" {
  source = "GIT_URL_FOR_MODULE"
  VARIABLES_AS_NEEDED_BY_MODULE
}
resource "aws_appautoscaling_policy" "queue_depth_based_scale_in_policy" {
  name               = "${local.service_name}-${local.env}-queue-scale-in-policy-new-${module.my_module.scalable_target_id}"
  policy_type        = "StepScaling"
  resource_id        = "service/${local.ecs_cluster_name}/${local.service_name}"
  scalable_dimension = "ecs:service:DesiredCount"
  service_namespace  = "ecs"
  step_scaling_policy_configuration {
    adjustment_type         = "ChangeInCapacity"
    cooldown                = "${local.queue_scale_in_cooldown_period}"
    metric_aggregation_type = "Average"
    step_adjustment {
      metric_interval_upper_bound = "${local.queue_scale_in_step_adjustment_upper_bound}"
      scaling_adjustment          = "${local.queue_scale_in_step_adjustment_scaling_adjustment}"
    }
  }      
}

尽管如此,我仍然无法弄清楚为什么会出现循环。