【问题标题】:In Terraform, how can I use another deployment's resources?在 Terraform 中,如何使用另一个部署的资源?
【发布时间】:2021-05-12 17:46:53
【问题描述】:

Terraform v0.12.x

这是我的另一篇帖子How to use Terraform modules for code re-use? 的后续问题。

我有 2 个旨在重用其他模块的模块。我的目录结构是...

/terraform/
/terraform/blue/main.tf
/terraform/green/main.tf
/terraform/module_snapshot/main.tf
/terraform/module_ebs/main.tf

我想在两个部署之间重复使用 module_ebs/main.tfblue/main.tfgreen/main.tf。它只是这样做

resource "aws_ebs_volume" "ebs" {
  availability_zone = "us-east-1a"
  snapshot_id       = "sn-123456abcded"
  size              = 500
  type              = "gp2"
  tags = {
    Name        = "test-ebs"
  }
}

output "ebs_id" {
  value       = aws_ebs_volume.ebs.id
  description = "Volume id of the EBS volume"
}

这个想法是green/main.tf 使用module_ebs/main.tf 创建一个EBS 卷(它有一个名为ebs_id 的输出)。

provider "aws" {
  region = "us-east-1"
}

terraform {
  required_version = ">= 0.12.17, < 0.13"
  backend "s3" {
    bucket = "my-terraform-states"
    key    = "test-modules/terraform.tfstate"
    region = "us-east-1"
  }
}

module "green_ebs" {
  source "../module_ebs"
}
output "green_ebs_id" {
  value = module.green_ebs.ebs_id
}

当我这样做时,我得到了所需的 EBS 卷

$ cd /terraform/green
$ terraform plan -out out.o
$ terraform apply "out.o"
green_ebs_id = "vol-123456abcdef"

现在我想让blue/main.tf 拍摄green 的EBS 卷的快照,所以我这样做了

provider "aws" {
  region = "us-east-1"
}

terraform {
  required_version = ">= 0.12.17, < 0.13"
  backend "s3" {
    bucket = "my-terraform-states"
    key    = "test-modules/terraform.tfstate"
    region = "us-east-1"
  }
}

module "green" {
  source "../module"
}
module "snapshot" {
  source "../module_snapshot"
  green_ebs_id = module.green.green_ebs_id
}
output "blue_ebs_id" {
  value = module.blue_ebs.ebs_id
}

但是,当我运行上述脚本时,它(当然)也运行green/main.tf,这当然会破坏它的 EBS 卷并创建另一个卷,这不是我想要做的。

$ cd /terraform/blue
$ terraform plan -out out.o
# module.green.aws_ebs_volume.ebs will be destroyed
- resource "aws_ebs_volume" "ebs" {
...
}

如何在不销毁和重新创建资源的情况下使用其他部署的资源?

【问题讨论】:

    标签: terraform terraform-provider-aws


    【解决方案1】:

    对于如何实现这一点,有几种不同的变体,它们对coupling 等考虑因素以及隐式关系与显式接口有一些不同的权衡。

    一种常见的方法是建立一些约定,通过这些约定,下游配置可以间接使用数据源找到上游配置创建的对象。在您的情况下,这可能涉及为您的 EBS 卷设计一个两种配置都同意的标记方案,以便第二个配置可以找到由第一个配置创建的对象。

    在第一个配置中:

    resource "aws_ebs_volume" "ebs" {
      availability_zone = "us-east-1a"
      snapshot_id       = "sn-123456abcded"
      size              = 500
      type              = "gp2"
      tags = {
        Name = "production-appname"
      }
    }
    

    在第二种配置中:

    data "aws_ebs_volume" "example" {
      filter {
        name   = "tag:Name"
        values = ["production-appname"]
      }
    }
    

    本例中的约定是“Name”标签的值是“production-appname”。对于您的目的而言,这可能不是完全正确的约定,但它展示了总体思路。然后第二个配置可以通过data.aws_ebs_volume.example.id 访问该ID。

    正如我在开篇中提到的,上述方法进行了一些设计权衡:

    • coupling 相对较低,因为第二个模块只需要 something 先前创建具有特定标签的 EBS 卷,因此您可以稍后重构您的系统,以便 EBS卷是在不同的 Terraform 配置中创建的,或者是使用 Terraform 以外的其他软件创建的,而对下游没有任何更改。
    • 但是,这两者之间的连接是隐式,因为它依赖于共享约定而不是显式接口。这可能会使整个系统架构更难理解,除非您小心地将这些隐式约定记录在您的团队知道的某个地方。

    另一个变体是让您的上游配置将信息显式发布到专门用于该目的的配置存储中。例如,在 AWS 中您可能会使用 AWS SSM 参数存储,在 Terraform 中使用 aws_ssm_parameter 托管资源类型和数据源表示:

    resource "aws_ssm_parameter" "foo" {
      name  = "appname_ebs_volume_id"
      type  = "String"
      value = aws_ebs_volume.ebs.id
    }
    
    data "aws_ssm_parameter" "foo" {
      name = "appname_ebs_volume_id"
    }
    

    这里两个配置之间有一个共享的约定,但是约定是写入一个专门用于存储配置的位置,因此“集合点”(在这种情况下为 SSM 参数)清楚地表示在上游和下游配置,保持相似级别的耦合但增加了明确性。


    最后一个选项是利用大多数“真实”Terraform 配置将其状态快照保存在远程网络位置这一事实。 terraform_remote_state 数据源是一个特殊的数据源,它从远程位置读取状态快照并提取存储在那里的根模块输出,因此您可以在其他地方使用该数据。因此,您可以利用您在第一个模块中声明的输出来填充第二个模块中的资源配置,只要应用第二个模块的每个人都有足够的访问权限来读取第一个模块中的最新状态快照。

    我认为,这第三个选项与第一个选项相反:

    • 耦合度,因为下游配置被配置为直接从其他配置的状态读取这个值。如果您稍后要重构并将 EBS 卷移动到不同的 Terraform 配置或使用其他软件对其进行管理,则需要编辑第二个配置以使其了解新的源。
    • 但是,这可能是最明确的选项,因为 EBS 卷 ID 显然是有意从第一个配置中导出为在其他地方使用的值,而第二个配置准确描述了哪个子系统负责生成该值。

    在所有情况下,这些选项都不是“正确”或“错误”,但我个人认为第二个选项是其他两个选项之间的一个很好的折衷方案,因为它缓和了两个相互竞争的设计考虑因素。选择哪一个将取决于您试图描述的系统的目标和约束,但如果您的情况没有明确的“赢家”,我认为第二个是一个很好的默认值。

    Terraform 文档指南Module Composition 中提供了一些关于以灵活方式减少耦合和分解系统的技术的一般指导。它不是专门关于跨多个单独的 Terraform 配置拆分基础设施,但其中描述的技术可以帮助您进行设置,以便您可以更轻松地更改有关如何在以后分解系统的决定,以便您可以推迟添加多个单独的复杂性配置,直到您发现真正需要这样做为止。

    【讨论】:

    • 由于我用名称标记绿色 EBS,我可能会使用您的过滤方法。谢谢。
    【解决方案2】:

    如果您可以使用外部“starter”脚本(bash、jq 等),那么您绝对可以做到这一点。 第一次运行后,会创建一个 terraform.tfstate 文件。它包含所有已创建资源的描述及其 ID。 使用您的启动脚本,您可以遍历状态文件,提取必要的 ID 并使用 terraform import {module_2_resource_name} {module_1_resource_id} 将资源导入新模块。您也可以尝试使用terraform import -state=path 直接重用其他状态文件。但是你应该小心。 module_1 和 module_2 中资源的定义应该相同,以免破坏。

    【讨论】:

      猜你喜欢
      • 2020-09-16
      • 2021-12-11
      • 1970-01-01
      • 1970-01-01
      • 2021-12-14
      • 2020-12-17
      • 1970-01-01
      • 2022-07-28
      • 1970-01-01
      相关资源
      最近更新 更多