随着我们正在评估Terraform以替换(部分)我们的多租户SaaS的Ansible供应流程,我们意识到Terraform的便利性,性能和可靠性,因为我们可以处理基础架构的变化(添加/顺利地移除)跟踪下面的状态(非常酷)。
我们的应用程序是一个多租户SaaS,我们为客户提供单独的实例 - 在Ansible,我们拥有自己的动态库存(与EC2动态库存完全相同)。我们经历了许多Terraform书籍/教程和最佳实践,许多人建议多环境状态应该单独管理。远程Terraform,但它们看起来像静态环境(如Dev / Staging / Prod)。
是否有针对多租户应用管理状态动态库存的最佳做法或实际示例?我们希望跟踪每个客户实例集的状态 - 轻松填充对它们的更改。
一种方法可能是我们为每个客户创建一个目录,并在其中放置* .tf脚本,这将调用托管在全局某处的模块。状态文件可能会被放到S3,这样我们就可以根据需要为每个客户填充更改。
答案 0 :(得分:7)
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
在这里,您可以从根级别运行您的计划/ apply / destroy,其中包装器shell脚本处理诸如进入目录并运行terraform get -update=true
之类的内容,但也为该文件夹运行terraform init
以便您获取S3的唯一状态文件密钥,允许您独立跟踪每个文件夹的状态。
上述解决方案具有通用模块,它们包装资源以提供事物的通用接口(例如,我们的EC2实例根据某些输入变量以特定方式标记,并且还提供了专用的Route53记录),然后是“已实现的组件”
这些组件包含一组模块/资源,Terraform将在同一文件夹中应用这些模块/资源。因此,我们可能会在application.tf
下放置一个ELB,一些应用程序服务器和一个数据库,然后将它符号化到一个位置,为我们提供一个用Terraform控制的地方。如果我们可能在某个地点的资源方面存在一些差异,那么它们就会被分开。在上面的示例中,您可以看到staging/global
有global_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
需要两个辅助脚本:
模板呈现。使用terraform -var-file=...
生成module's source attribute或使用功能更强大的工具(例如在airbnb/streamalert中完成)< / p>
包装脚本。通常运行_global
就足够了。
共享的terraform状态文件以及应该是全局的资源(上面的目录select SUM(Points),SUM(Score) FROM table_name GROUP BY Club
)可以存储在S3上,以便其他层可以访问它们。
PS:我非常乐意对提议的解决方案发表评论,因为这是一项有趣的任务:)