【问题标题】:Terraform - why this is not causing circular dependency?Terraform - 为什么这不会导致循环依赖?
【发布时间】:2020-03-07 19:56:53
【问题描述】:

Terraform 注册表 AWS VPC 示例 terraform-aws-vpc/examples/complete-vpc/main.tf 有下面的代码,在我看来这是一个循环依赖。

data "aws_security_group" "default" {
  name   = "default"
  vpc_id = module.vpc.vpc_id
}

module "vpc" {
  source = "../../"

  name = "complete-example"

...
 # VPC endpoint for SSM
  enable_ssm_endpoint              = true
  ssm_endpoint_private_dns_enabled = true
  ssm_endpoint_security_group_ids  = [data.aws_security_group.default.id] # <----- 

...

data.aws_security_group.default 指“module.vpc.vpc_id”,module.vpc 指“data.aws_security_group.default.id”。

请解释为什么这不会导致错误以及为什么 module.vpc 可以引用 data.aws_security_group.default.id?

【问题讨论】:

  • 因为依赖图适用于单个元素,而不是模块。无论您将安全组 ID 作为ssm_endpoint_security_group_ids 的一部分传递到何处,都不会对安全组产生循环依赖。但这完全没有必要,因为您可以直接在模块内部传递资源的安全组。为确保您还必须发布模块的源代码。
  • 嗨@ydaetskcoR,谢谢,但仍然不知道为什么。据我了解,数据源首先运行以检索数据,并且需要现有资源,在这种情况下,尚未创建的 module.vpc 的默认安全组。我想知道为什么它没有失败。
  • 由于插值,在创建 VPC 之前不运行。我仍然希望它在第一次创建时失败,因为安全组创建将与 VPC 创建同时发生(假设它还插入 VPC ID 并且模块输出直接来自 VPC 资源)所以在那指向未创建安全组。但更重要的是,如果您不发布模块的源代码,就不可能真正具体地说明事情是如何工作的。但考虑到您的模块看起来非常复杂,我会尝试生成一个 minimal reproducible example 来代替。
  • @ydaetskcoR 代码全部在 Terraform Registry AWS VPC 的 Github 中 github.com/terraform-aws-modules/terraform-aws-vpc ,示例是 github 中的 github.com/terraform-aws-modules/terraform-aws-vpc/tree/master/…

标签: terraform


【解决方案1】:

在 Terraform 语言中,模块创建一个单独的命名空间,但它不是依赖图中的一个节点。相反,每个模块的输入变量和输出值都是依赖图中的独立节点。

因此,此配置包含以下依赖项:

  • data.aws_security_group.default 资源依赖于 module.vpc.vpc_id,具体来说是该模块中的 output "vpc_id" 块,而不是整个模块。
  • vpc 模块的variable "ssm_endpoint_security_group_ids" 变量取决于data.aws_security_group.default 资源。

我们在这里看不到你的问题中vpc模块的内部,但是只要模块内部的output "vpc_id"variable "ssm_endpoint_security_group_ids"之间没有依赖关系,上面的就可以了。

我假设这样的连接不存在,所以这里对象的评估顺序是这样的:

  • aws_vpc.example in module.vpc 已创建(我只是为此起了一个名字,因为它不包含在您的问题中)
  • 评估module.vpc 中的output "vpc_id",引用module.vpc.aws_vpc.example,并生成module.vpc.vpc_id
  • 使用module.vpc.vpc_id 的值读取根模块中的data.aws_security_group.default
  • 评估module.vpcvariable "ssm_endpoint_security_group_ids",引用data.aws_security_group.default
  • module.vpc 中创建aws_vpc_endpoint.example,包括对var.ssm_endpoint_security_group_ids 的引用。

请注意,在上述所有内容中,我都在谈论 in 模块中的对象,而不是模块本身。这些模块仅用于为对象创建单独的命名空间,然后单独的对象本身(包括单独的 variableoutput 块)参与依赖关系图。


通常这种设计细节是不可见的:Terraform 通常只是使用它来潜在地优化并发性,方法是在整个模块准备好处理之前开始处理模块的一部分。不过,在像这样的一些有趣的情况下,您也可以有意地利用这种设计,以便调用模块的操作可以显式地夹在子模块的两个操作之间。

我们可能使用此功能的另一个原因是当两个模块自然地相互依赖时,例如在an experimental module I built 中隐藏了设置 VPC 对等连接的一些棘手细节:

locals {
  vpc_nets = {
    us-west-2 = module.vpc_usw2
    us-east-1 = module.vpc_use1
  }
}

module "peering_usw2" {
  source = "../../modules/peering-mesh"

  region_vpc_networks = local.vpc_nets
  other_region_connections = {
    us-east-1 = module.peering_use1.outgoing_connection_ids
  }

  providers = {
    aws = aws.usw2
  }
}

module "peering_use1" {
  source = "../../modules/peering-mesh"

  region_vpc_networks = local.vpc_nets
  other_region_connections = {
    us-west-2 = module.peering_usw2.outgoing_connection_ids
  }

  providers = {
    aws = aws.use1
  }
}

(以上只是来自an example in the module repository的相关sn-p。)

在上述情况下,peering-mesh 模块经过精心设计以允许这种相互引用,在内部为每对区域 VPC 决定哪一个是对等发起者,哪一个是对等接受者。 outgoing_connection_ids 输出只指aws_vpc_peering_connection 资源,aws_vpc_peering_connection_accepter 只指var.other_region_connections,所以结果是一堆并发操作创建aws_vpc_peering_connection 资源,然后是一堆并发操作创建aws_vpc_peering_connection_accepter 资源。

【讨论】:

  • 感谢TF 0.12背后的人的回复。感谢您的工作。我想我明白了。这是 [depends_on 模块](github.com/hashicorp/terraform/issues/18239) 不会发生的原因吗?虽然我们用户可能会假设 TF 模块是一种原子单元(DAG 中的节点?),但根模块和 TF 模块中的所有资源都平放在一个地方,并且由所有资源创建 DAG .希望这种理解是正确的。
猜你喜欢
  • 2013-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-08
相关资源
最近更新 更多