Terraform:多租户的状态管理

Posted

技术标签:

【中文标题】Terraform:多租户的状态管理【英文标题】:Terraform: state management for multi-tenancy 【发布时间】:2017-08-29 07:34:03 【问题描述】:

由于我们正在评估 Terraform 以替代(部分)多租户 SaaS 的 Ansible 配置流程,我们意识到 Terraform 的便利性、性能和可靠性,因为我们可以处理基础架构更改(添加/删除)顺利,跟踪基础设施状态(这很酷)。

我们的应用程序是多租户 SaaS,我们为客户提供单独的实例 - 在 Ansible 中,我们拥有自己的动态清单(与 EC2 动态清单非常相似)。我们浏览了许多 Terraform 书籍/教程和最佳实践,其中许多建议应该在 Terraform 中单独和远程管理多环境状态,但它们都看起来像静态环境(如 Dev/Staging/Prod)。

是否有管理多租户应用的动态状态清单的最佳实践或真实示例?我们希望跟踪每个客户实例集的状态 - 轻松填充对它们的更改。

一种方法可能是我们为每个客户创建一个目录并在其中放置 *.tf 脚本,这将调用托管在全球某处的我们的模块。状态文件可能会被放到 S3 中,这样我们可以在需要时为每个客户填充更改。

【问题讨论】:

【参考方案1】:

您建议的方法对我来说听起来不错,但您可能会考虑做更多的事情。

将原始 Terraform 模板(下面树中的_template)保留为版本化工件(例如 git 存储库),并且只需传递键值属性即可重新创建您的基础架构。这样,您将在目录中放置非常少量的复制粘贴 Terraform 配置代码。

看起来是这样的:

/tf-infra
├── _global
│   └── global
│       ├── README.md
│       ├── main.tf
│       ├── outputs.tf
│       ├── terraform.tfvars
│       └── variables.tf
└── staging
    └── eu-west-1
        ├── saas
        │   ├── _template
        │   │   └── dynamic.tf.tpl
        │   ├── customer1
        │   │   ├── auto-generated.tf
        │   │   └── terraform.tfvars
        │   ├── customer2
        │   │   ├── auto-generated.tf
        │   │   └── terraform.tfvars
...

需要两个帮助脚本:

    模板渲染。使用sed生成module's source attribute或使用更强大的工具(例如在airbnb/streamalert中完成)

    包装脚本。运行terraform -var-file=... 通常就足够了。

共享的 terraform 状态文件以及应该是全局的资源(上面的目录 _global)可以存储在 S3 上,以便其他层可以访问它们。

PS:我非常愿意接受 cmets 提出的解决方案,因为这是一项有趣的工作:)

【讨论】:

【参考方案2】:

Terraform 在文件夹级别上工作,拉入所有.tf 文件(默认情况下是terraform.tfvars 文件)。

所以我们做了一些类似于Anton 的answer 的事情,但在使用 sed 进行模板化时消除了一些复杂性。因此,作为一个基本示例,您的结构可能如下所示:

$ tree -a --dirsfirst
.
├── components
│   ├── application.tf
│   ├── common.tf
│   ├── global_component1.tf
│   └── global_component2.tf
├── modules
│   ├── module1
│   ├── module2
│   └── module3
├── production
│   ├── customer1
│   │   ├── application.tf -> ../../components/application.tf
│   │   ├── common.tf -> ../../components/common.tf
│   │   └── terraform.tfvars
│   ├── customer2
│   │   ├── application.tf -> ../../components/application.tf
│   │   ├── common.tf -> ../../components/common.tf
│   │   └── terraform.tfvars
│   └── global
│       ├── common.tf -> ../../components/common.tf
│       ├── global_component1.tf -> ../../components/global_component1.tf
│       ├── global_component2.tf -> ../../components/global_component2.tf
│       └── terraform.tfvars
├── staging
│   ├── customer1
│   │   ├── application.tf -> ../../components/application.tf
│   │   ├── common.tf -> ../../components/common.tf
│   │   └── terraform.tfvars
│   ├── customer2
│   │   ├── application.tf -> ../../components/application.tf
│   │   ├── common.tf -> ../../components/common.tf
│   │   └── terraform.tfvars
│   └── global
│       ├── common.tf -> ../../components/common.tf
│       ├── global_component1.tf -> ../../components/global_component1.tf
│       └── terraform.tfvars
├── apply.sh
├── destroy.sh
├── plan.sh
└── remote.sh

在这里,您从根级别运行您的 plan/apply/destroy,其中包装器 shell 脚本处理诸如 cd'ing 进入目录并运行 terraform get -update=true 但还为文件夹运行 terraform init 等事情,因此您可以获得唯一的状态S3 的文件密钥,允许您独立跟踪每个文件夹的状态。

上述解决方案具有通用模块,这些模块包装资源以提供通用接口(例如,我们的 EC2 实例根据某些输入变量以特定方式标记,并提供私有 Route53 记录),然后是“实现的组件” .

这些组件包含一堆模块/资源,Terraform 将在同一文件夹中应用这些模块/资源。因此,我们可能会在application.tf 下放置一个 ELB、一些应用程序服务器和一个数据库,然后将其符号链接到一个位置,以便我们使用 Terraform 进行控制。如果我们可能在某个位置的资源上有一些差异,那么它们就会被分离出来。在上面的示例中,您可以看到staging/global 有一个global_component2.tf,它在生产中不存在。这可能只是在非生产环境中应用的东西,例如一些网络控制,以防止互联网访问环境。

真正的好处是,开发人员可以直接在源代码管理中轻松查看所有内容,而不是通过模板步骤生成所需的 Terraform 代码。

它还有助于遵循 DRY,环境之间唯一真正的区别在于位置中的 terraform.tfvars 文件,并且可以更轻松地在将更改生效之前测试更改,因为每个文件夹都与另一个文件夹几乎相同。

【讨论】:

使用这种方法,您将在每个文件夹内或从根目录运行 terraform?我问是因为根据这一点,状态文件可能存储在根路径或每个文件夹中。 您无法从父文件夹运行 Terraform。 Terraform 仅适用于当前目录中的内容。碰巧的是,我们有一些帮助脚本位于 repo 的根目录中,cd 进入我们想要操作的位置,然后从那里运行terraform CLI 命令。 是的,你可以,我一直这样做...terraform plan path/to/something 但是谢谢。我明白了,使用脚本并在文件夹中执行cd,我在每个文件夹中得到一个状态文件,这就是我想要的。有一个标志也可以将状态文件放在文件夹中,从根terraform plan path/to/something -state=path/to/something 如果我们必须将 Gitlab CI/CD 与 Hashicorp Vault 一起使用,我们将如何实施此解决方案?管道将如何切换上下文?它不会让它变得复杂和一个定时炸弹来搞砸事情吗?此外,如果我们使用 Azure 存储帐户,我们如何确保状态文件不被混淆,并且通过适当的备份/恢复得到适当的保护?

以上是关于Terraform:多租户的状态管理的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Terraform 中管理本地生成的有状态文件

如何从 terraform 状态中删除资源?

Hadoop跑满状态下的Yarn资源管理谈

加密存储在远程后端(如 GCS 存储桶)上的 Terraform 状态是不是有用?

Terraform 学习总结—— 如何解决存量云资源的管理难题

Terraform 学习总结—— 如何解决存量云资源的管理难题