作者|Sumeet Ninawe
翻译|Seal软件
链接|https://spacelift.io/blog/terraform-environments
通常我们使用 Terraform 将我们的基础设施定义为代码,然后用 Terraform CLI 在我们选择的云平台中创建制定的基础设施组件。从表面上看,整个过程看起来似乎不需要花费太多精力。然而当我们深入研究将其用于真实场景时,很快就会遇到有关管理子生产和生产环境的问题。
在这篇文章中,我们将重点关注如何使用 Terraform Workspace 和 Git 分支来有效管理多个环境。
多环境基础设施
下面列出了使用 IaC 管理多个环境的基础设施的期望和要求:
-
可以使用相同的 IaC 配置来管理生产和非生产环境。
-
某些非生产环境,如开发、QA、测试版或 UAT 应该与生产的相同和缩小版本并永久存在。
-
团队成员能够创建、管理和销毁与生产相同的临时环境。
-
所有环境并不是在同一个云帐户或订阅中创建的。
这里的关键点在于对所有环境中的基础设施使用相同的 Terraform 配置模板。
Terraform Workspace
Terraform 提供了一个工作区功能,使用该功能可以使用相同的配置创建和管理多个相同的、按比例缩小的环境。以这种方式创建的多个环境是完全隔离的,不会以任何方式相互干扰。这是我们需要的关键功能。一起看看如何利用这个功能来满足我们的要求。
Terraform Workspace 不同于 Terraform Cloud Workspace。在 Terraform Cloud 中,工作空间类似于一个“项目”,对应于 Terraform 配置存储库。除了存储和管理状态信息外,还管理变量、凭证、历史跟踪等,以支持端到端的 Terraform Cloud CI/CD 工作流。
用于处理工作区的 Terraform CLI 命令
下表表示 Terraform 工作区命令的基本用法。每个命令都遵循如下简单格式:
terraform workspace <command>
下面的 CLI 输出显示了管理工作区的示例。简而言之,我们检查当前选择的工作空间——默认,然后创建一个名为 beta 的新工作空间,列出所有工作空间,并删除 beta 工作空间。
% terraform workspace show
default
% terraform workspace new beta
Created and switched to workspace "beta"!
You're now on a new, empty workspace. Workspaces isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.
% terraform workspace list
default
* beta
% terraform workspace select default
Switched to workspace "default".
% terraform workspace delete beta
Releasing state lock. This may take a few moments...
Deleted workspace "beta"!
% terraform workspace list
* default
工作区插值
要使用相同的配置管理多个按比例缩小的环境,需要有一种方法让 Terraform 知道我们正在使用哪个工作区,这有助于正确设置配置。例如,我们可能希望为特定工作区管理的环境提供更多的 EC2 实例,为其他环境提供更少的实例。
Terraform 工作空间插值序列为我们提供了一种实现这种动态变化的方法。通过访问所选工作区的值,我们可以使用多个构造和运算符来创建具有所需规模和其他自定义属性的环境。
考虑下面的例子。在这里,Terraform 配置旨在在 AWS 中创建 EC2 实例。但是,基于所选工作区的计数属性定义了要创建的实例数。这里,“terraform.workspace”插值序列用于访问它。
resource "aws_instance" "my_vm" {
count = terraform.workspace == "default" ? 3 : 1
ami = var.ami //Ubuntu AMI
instance_type = var.instance_type
tags = {
Name = format("%s_%s_%s", var.name_tag, terraform.workspace, count.index)
}
}
如果选择“默认”工作区,则将创建三个 EC2 实例,否则只创建一个。当然,这只是一个例子。我们可以使用更复杂的变量和运算符来管理更多的环境。
基础设施和应用程序开发
端到端产品开发涉及几个方面——基础设施和要部署在基础设施上的应用程序。通常,会有对应的个人团队来负责相应的任务。在微服务中,由于依赖性和资源限制,在本地机器上测试和开发应用程序可能并不总是可行的。应用程序团队成员可能需要调整临时环境来运行他们的测试用例,甚至在将更改部署到“永久”开发环境之前。
在这种情况下,无需担心 Terraform 源代码,因为他们可以简单地克隆存储库,然后使用工作区功能创建自己的临时环境。这种能力对于应用程序开发团队来说非常有用,可以在将更改合并到 dev 并将它们向前推进之前单独运行他们的测试用例。
帐户和凭据
多个环境通常使用多个云帐户或订阅进行管理。云平台还实施了“组织”概念,以从单个根帐户管理多个帐户。此根帐户负责所有管理活动,如计费、访问配置等。当 Terraform 配置被“应用”时,更改将根据其提供者配置为目标帐户进行验证和执行。
provider "aws" {
shared_config_files = ["/path/to/.aws/conf"]
shared_credentials_files = ["/path/to/.aws/creds"]
profile = "profile_name"
}
在这里,我们对配置文件名称进行了硬编码,以便 Terraform 为目标帐户使用适当的凭据。在这里,我们还可以利用工作区插值序列从共享凭证文件中动态选择配置文件名称。
Terraform Workspace 小结
在使用 Terraform 管理多个环境时,Terraform 中的状态管理可能是一个敏感话题。不过 Terraform 提供的工作区管理可以通过在当前设置的后端中创建子目录来在后台处理此问题。状态管理也可能是一个限制因素,因为所有状态文件都存储在同一个后端目录中。这意味着所有用于 terraform 配置的插件也会在每个工作区中被复制。
Terraform 工作区提供了一种创建瞬态环境的好方法,只需学习一些命令即可测试基础设施的变化。依靠内部布线——使用插值序列。如果代码已经构建,引入工作空间插值依赖性可能需要一些努力。
Git 分支
在本节中,我们将探索使用 Git 分支管理多个环境的可能性,并了解为什么它可能不是最佳策略。下图旨在满足本博文介绍中所述的要求。
开发的两个方面——基础设施和应用程序,分别以绿色和蓝色显示。此处表示的分支策略是将 Terraform 配置用于各种目的的应用。我们将深入探讨 Git 分支的各个部分。
Git 的目的
简而言之,Git 旨在协调整个团队的开发工作。它为部署维护各种版本的源代码和包版本。主分支通常包含经过充分测试的功能,适用于任何给定软件的一般用途。
执行开发活动或以错误修复、功能或增强的形式引入任何更改——创建主分支的副本,在其上执行修改、重建、部署到子生产环境,并在合并之前进行彻底测试对主要分支的更改。
环境的 Git 分支
考虑到这一点,使用 Git 分支来管理多个环境也是可以的,每个环境一个分支是非常理想的。在给定的图表中,infra-dev 团队在三个分支上工作:
-
Main – 用于管理生产基础设施设置。
-
QA – 用于管理 QA 基础设施设置,合格用户在其中执行 UAT 测试。
-
Dev – 用于管理开发基础架构设置,其中首先发布功能并进行单元测试。
在高层次上,从主分支分出并创建相同配置的副本以创建 QA 和开发环境是有意义的。
存在的问题
然而,当我们将 Terraform 的更深层次视为 IaC 时,我们需要关注一些点:
-
状态文件管理和关联的远程后端。
-
缩放转化为环境特定属性方面。
-
多个帐户的凭据。
这里考虑的环境是单独的基础设施部署。这些环境都有自有的状态信息,需要对其进行远程安全管理。远程后端在 terraform 资源块中定义。
下面的示例使用 AWS S3 后端。
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.18.0"
}
}
backend "s3" {
bucket = "tf-state-bucket"
key = "terraform.tfstate"
region = "eu-central-1"
dynamodb_table = "tf_state_lock"
}
}
假设这是生产环境使用的配置,即主分支。当我们从主分支分出来时,后端配置也会被复制。所有 Terraform CLI 命令都假定此后端对于所有其他副本(分支)都相同,但这是不可取的,而且可能是非常危险的。事实上,运行任何 Terraform 命令(如 plan、apply、destroy)将引用生产状态文件,甚至对生产执行操作。
如果我们手动修改后端配置,为 QA 和开发环境使用不同的后端,那么它就违背了 Git 的初衷。Git 合并会产生冲突,并要求开发人员通过选择后端来解决这些冲突。
这个情况也适用于.tfvars 文件中定义的特定于环境的属性值。各种环境的扩展方面是通过变量管理的——更具体地说,是 .tfvars 文件。现代 Git 工作流通常需要在任何分支上进行推送和拉取。在这种方法中可能是不可行的。
提供商配置可能包含多个别名以表示多个云提供商帐户和区域中的部署。这也被 Git 的优点所覆盖。
CI/CD 流水线
大多数远程 Git 存储库都提供了以 CI/CD 流水线的形式引入自动化的能力。值得注意的是 GitHub Actions 和 Gitlab CI/CD。就源代码版本控制而言,使用远程 Git 存储库并定义处理凭据的自动化流水线是有意义的。在我们的示例中,如果我们在特定分支上进行提交或批准拉取请求,则可以运行特定于分支的管道,该管道使用特定于环境的凭证将更改应用到正确的目标环境。
然而,即使这解决了凭据问题,特定于环境的提供程序配置和属性仍然是 Git 工作流程的一部分。这与 Terraform 期望这些配置用于我们想要的更改方式不一致。此外,CI/CD 流水线功能是任何其他 Terraform 工作流都可以利用的功能。所以这不会增加依赖 Git 分支的任何特定优势。
应用开发
现代应用程序开发基于微服务、容器和函数。通常,本地开发环境是开发团队遇到的一个问题,具体取决于各种因素。一个简单的例子——当运行一组相互依赖和其他因素的容器时,可能会发现开发机器上可用的资源不够用。
使用 Terraform 作为 IaC 确实有助于旋转临时和隔离的环境来为开发人员执行单元测试。还可以从所需的源分支(主分支、QA 分支或开发分支)创建一个临时 Git 分支,并创建一个隔离的缩小环境——如上图中的“Temp2”部署所示。
此外,假设任何应用程序功能都依赖于仍在开发中的特定基础结构组件。在这种情况下,应用程序团队可以选择从基础架构开发的“dev”分支分支出来,其中包含预期的更改。它由图中的“Temp1”部署表示。
应该注意的是,使用 Git 分支策略管理环境使用了一个总体上的假设,即拥有正确的分支策略。例如,应用程序开发团队创建的分支不能合并到任何基础设施开发团队的分支中。如果 Terraform 有办法知道当前检出哪个分支,那么采用 Git 分支策略会更有意义。在使用 Terraform 工作空间时,工作空间插值序列提供了这个确切的功能。