亚马逊AWS官方博客

Terraform 最佳实践之变量传递

在本文中,我们将探讨在企业中使用 Terraform 时,如何在面对不同组织部门的情况下进行全局规划。由于环境中的组件通常是动态变化的,需要在动态过程中实现变量传递。接下来,我们将讲解实现这一目标的具体方法。

组织架构和需求实现

  • 基础设施层:通常由基础架构部门(如 SRE/DevOps 工程师)负责初始化账号,给到不同的部门交付基础架构的设计,例如 Amazon VPC 和基础权限,这些构成了公司的核心安全基础。
  • 应用层:各个业务部门通常有自己的应用层面的需求,这一层主要部署业务需求相关的应用级资源,例如 Amazon EKS 和 Amazon RDS。

模块使用

如果需要实现上述的需求,就需要调用特定的模块,学会协调和使用模块。通常,我们会用到公有模块和私有模块。

  • 公有模块:由 Terraform 官方提供的开发的模块 https://github.com/terraform-aws-modules,通常涵盖了常用的所有基础服务,可以用于快速构建基础环境。例如:Amazon VPC、Amazon IAM、Amazon EC2,等等。
  • 私有模块:根据企业的特殊需求自定义开发的模块。例如,为实现 VPC Peering 而创建的模块,并根据场景需求不断迭代完善。

目录结构

在 Terraform 中,各部门创建资源时通常使用特定的目录结构。为了便于区分,可以将这些目录称为“Playbook”,与 Modules 目录结构加以区分。

核心问题

所以接下来引出的问题是,在各部门创建 Playbook 资源的过程中,可能存在公司或部门约定的全局变量。然而,由于每次调用通常是以独立目录的方式进行,如何在这种情况下实现全局变量的共享和使用?

另外,假设 SRE 部门已编写了一些用于资源创建的 Playbook,例如 VPC 的创建。作为业务部门,如果想单独编写 RDS 创建的 Playbook,应如何基于 SRE 部门的 Playbook,获取目标 VPC 下不同子网的部署信息?

为了更好地说明问题,我们先基于上面的假设看一下全局设计图。

解决方案

管理全局变量

在 Terraform 调用过程中,是以目录为基础单元进行调用的。在默认机制中,存在一定限制,但我们可以通过将全局变量作为 output 来定义,然后在不同部门的 Playbook 中直接调用该模块。具体见下图。

通过这种方式,在每个 Playbook 里面调用 global_variables 模块,从而通过做到变量的引用,这样实际就建立起一种约定机制。

跨 Playbook 调用

不同 Playbook 之间的调用层次非常偏上层。如果我们在一个单独的 Playbook 里面,直接使用 resource 和 module,都可以直接使用其属性值;但是在不同 Playbook 之间调用,需要用到 tfstate 进行引用,而存在于 tfstat 里面的值,实际上随着 Playbook 的执行而变化。实际上,这种引用关系仍然是通过 playbook 里面定义的 output 实现的,如果没有定义对应的 output,调用的 Playbook 也不会读取到。但是如果我们基础架构发生变化,关于资源的 ARN 会发生变化,这种引用关系实际上是一种动态的引用逻辑。

例如:

data.terraform_remote_state.infra.outputs.unicorn_sin_private_subnets
PowerShell

其中:

data.terraform_remote_state.infra # 前半段来自 Dept A App Playbook 的调用定义

.outputs.unicorn_sin_private_subnets # 后半段来自 Dept A Infra Playbook 定义的 Outputs
PowerShell

Playbook 调用 Public Modules 或者 Private Modules

我们建议使用指定的模块版本,指向模块的 Git 仓库。具体使用方法:

module "unicorn-sin-vpc" {

  providers = {
    aws = aws.unicorn_sin
  }

  source = "git::https://github.com/terraform-aws-modules/terraform-aws-vpc.git?ref=v3.19.0"

  name = local.sin_name
  cidr = "172.16.0.0/16"

  azs             = ["${local.sin_region}a", "${local.sin_region}b", "${local.sin_region}c"]
  public_subnets = ["172.16.0.0/24", "172.16.1.0/24", "172.16.2.0/24"]
  intra_subnets = ["172.16.3.0/24", "172.16.4.0/24", "172.16.5.0/24"]
  private_subnets = ["172.16.8.0/22", "172.16.12.0/22", "172.16.16.0/22"]
  database_subnets  = ["172.16.100.0/24", "172.16.101.0/24", "172.16.102.0/24"]

  enable_ipv6 = false

  enable_dns_hostnames = true
  enable_dns_support   = true

  enable_nat_gateway = true
  single_nat_gateway = false


  public_subnet_tags_per_az = {
    "${local.sin_region}a" = {
      "availability-zone" = "${local.sin_region}a"
    }
  }

  tags = local.unicorn_tags

  vpc_tags = {
    Name = "unicorn-sin"
  }
}
PowerShell

我们当前引用的是一个公开的 Terraform 实现的 Amazon VPC 模块:

source = "git::https://github.com/terraform-aws-modules/terraform-aws-vpc.git?ref=v3.19.0"
PowerShell

指定版本后,可以确保部署的一致性和可重复性,避免意外升级带来的破坏性,提供可靠的 CI/CD 流程。而使用 Private Modules 原理也是一致的。

结论

通过采用以上最佳实践,企业可以更高效地管理 Terraform 变量,实现团队间的协作与基础设施的一致性。这些方法同样适用于 Terraform Cloud 或 GitOps 流程,有助于构建可扩展且易维护的基础设施管理体系。

本篇作者

刘佳

亚马逊云科技解决方案架构师,负责基于 AWS 云计算客户方案咨询和架构设计,在金融领域有着多年的数据中心虚拟化解决方案经验。

张乾

亚马逊云科技解决方案架构师,负责基于 AWS 的解决方案咨询和设计,在系统架构设计、应用研发、容器服务、SRE 领域有丰富的实践经验。