Terraform:多租户的州管理

时间:2017-04-04 08:00:17

标签: amazon-web-services amazon-ec2 terraform

随着我们正在评估Terraform以替换(部分)我们的多租户SaaS的Ansible供应流程,我们意识到Terraform的便利性,性能和可靠性,因为我们可以处理基础架构的变化(添加/顺利地移除)跟踪下面的状态(非常酷)。

我们的应用程序是一个多租户SaaS,我们为客户提供单独的实例 - 在Ansible,我们拥有自己的动态库存(与EC2动态库存完全相同)。我们经历了许多Terraform书籍/教程和最佳实践,许多人建议多环境状态应该单独管理。远程Terraform,但它们看起来像静态环境(如Dev / Staging / Prod)。

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

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

2 个答案:

答案 0 :(得分:7)

Terraform适用于文件夹级别,提取所有.tf个文件(默认情况下为terraform.tfvars个文件)。

所以我们做了一些类似于Antonanswer的事情,但是在使用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

在这里,您可以从根级别运行您的计划/ apply / destroy,其中包装器shell脚本处理诸如进入目录并运行terraform get -update=true之类的内容,但也为该文件夹运行terraform init以便您获取S3的唯一状态文件密钥,允许您独立跟踪每个文件夹的状态。

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

这些组件包含一组模块/资源,Terraform将在同一文件夹中应用这些模块/资源。因此,我们可能会在application.tf下放置一个ELB,一些应用程序服务器和一个数据库,然后将它符号化到一个位置,为我们提供一个用Terraform控制的地方。如果我们可能在某个地点的资源方面存在一些差异,那么它们就会被分开。在上面的示例中,您可以看到staging/globalglobal_component2.tf生产中不存在的terraform.tfvars。这可能仅适用于非生产环境,例如某些网络控制,以防止Internet访问环境。

这里的真正好处是,所有内容都可以直接在开发人员的源代码控制中轻松查看,而不是使用模板生成所需的Terraform代码。

它还有助于关注DRY,其中环境之间的唯一真正差异在于位置中的www.company.net/projectX文件,并且在更改之前更容易测试更改,因为每个文件夹与其他文件夹几乎相同

答案 1 :(得分:2)

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

将原始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 ... 保留在下面的树中)作为版本化工件(git repo,例如),只需传递键值属性即可重新创建基础结构。这样,您将在目录中放置非常少量的复制粘贴Terraform配置代码。

它的外观如下:

sed

需要两个辅助脚本:

  1. 模板呈现。使用terraform -var-file=...生成module's source attribute或使用功能更强大的工具(例如在airbnb/streamalert中完成)< / p>

  2. 包装脚本。通常运行_global就足够了。

  3. 共享的terraform状态文件以及应该是全局的资源(上面的目录select SUM(Points),SUM(Score) FROM table_name GROUP BY Club )可以存储在S3上,以便其他层可以访问它们。

    PS:我非常乐意对提议的解决方案发表评论,因为这是一项有趣的任务:)