如何解决如何为多种环境组织 terraform 模块? 示例模块应用模块分离资源的分离定义单体架构总结
网络上的每个 terraform 指南都提供了部分解决方案,几乎总是不是真实的图片。
我明白了,并不是每个人都有相同的基础设施需求,但让我担心的是常见场景:
- 多种环境(开发、阶段)
- 远程后端 (s3)
- 一些基本资源(bucket 或 ec2 实例)
未在真实示例项目中的任何地方出现。
我正在寻找那个,同时,我已经研究并得出结论,除了我还想要的那些需求:
- 利用模块
- 不使用工作区,而是使用不同的每个环境目录方法
- 不要使用 terragrunt 包装器
我当前的结构,不使用模块 - 只有根模块:
infra/ ------------------------------ 'terraform init','terraform apply' inside here*
main.tf ------------------------ Sets up aws provider,backend,backend bucket,dynamodb table
terraform.tfvars
variables.tf ----------------- Holds few variables such as aws_region,project_name...
我想要的结构文件夹树(用于单个存储桶资源的简单开发和暂存模拟)是这样的:
infra/
dev/
s3/
modules.tf ------ References s3 module from local/remote folder with dev inputs
stage/
s3/
modules.tf ------ References s3 module from local/remote folder with stage inputs
但是我以前的根模块中的文件呢?我仍然想像以前一样有一个远程后端,现在我想有两个状态文件(dev.tfstate 和 stage.tfstate) 在同一个后端存储桶中? backend.tf 文件在每个子目录中的外观如何?它们将位于何处?在 s3/ 文件夹或 dev/ 文件夹中?
这有点令人困惑,因为我正在从 根模块 'terraform init' 方法过渡到 特定子目录 'terraform init',我不清楚是否我仍然应该有一个根模块或另一个文件夹,例如名为 global/ 的文件夹,我应该考虑我应该在项目开始时初始化的先决条件,并且从那时起基本上不用管它创建了 dev/ 和 staging/ 可以引用的存储桶?
还有一个问题是:如果我在每个环境中有 s3/ ec2/ ecr/ 子目录,我应该在哪里执行 'terraform plan' 命令?是否遍历所有子目录?
当我有了上面的答案和清晰的图片时,通过 DRY 来改进它会很棒,但是现在,我重视通过示例提供更实用的解决方案,而不仅仅是理论上的 DRY 解释。谢谢!
解决方法
我可以分享我们最终为 Indeni Cloudrail 服务所做的工作。希望能帮到你。
我们创建了一个包含所有模块的文件夹。然后,有一个名为“all”的模块,它基本上使用正确的参数调用其他模块(s3、acm 等)。 “所有”模块都有变量。
然后,还有环境。他们每个人都使用这些变量的特定值调用“all”模块。
这是 Terraform 代码根目录上的“查找”命令的输出(抱歉,它不够漂亮)。我删除了许多文件,因为不需要它们来说明问题:
./common.tfvars
./terragrunt.hcl
./environments
./environments/prod
./environments/prod/main.tf
./environments/prod/terragrunt.hcl
./environments/prod/lambda.layer.zip
./environments/prod/terraform.tfvars
./environments/prod/lambda.zip
./environments/prod/common.tf
./environments/dev-john
./environments/dev-john/main.tf
./environments/dev-john/terragrunt.hcl
./environments/dev-john/terraform.tfvars
./environments/dev-john/common.tf
./environments/mgmt-dr
./environments/mgmt-dr/data.tf
./environments/mgmt-dr/main.tf
./environments/mgmt-dr/terragrunt.hcl
./environments/mgmt-dr/network.tf
./environments/mgmt-dr/terraform.tfvars
./environments/mgmt-dr/jenkins.tf
./environments/mgmt-dr/keypair.tf
./environments/mgmt-dr/common.tf
./environments/mgmt-dr/openvpn-as.tf
./environments/mgmt-dr/tgw.tf
./environments/mgmt-dr/vars.tf
./environments/staging
./environments/staging/main.tf
./environments/staging/terragrunt.hcl
./environments/staging/terraform.tfvars
./environments/staging/common.tf
./environments/mgmt
./environments/mgmt/data.tf
./environments/mgmt/main.tf
./environments/mgmt/terragrunt.hcl
./environments/mgmt/network.tf
./environments/mgmt/terraform.tfvars
./environments/mgmt/route53.tf
./environments/mgmt/acm.tf
./environments/mgmt/jenkins.tf
./environments/mgmt/keypair.tf
./environments/mgmt/common.tf
./environments/mgmt/openvpn-as.tf
./environments/mgmt/tgw.tf
./environments/mgmt/alb.tf
./environments/mgmt/vars.tf
./environments/develop
./environments/develop/main.tf
./environments/develop/terragrunt.hcl
./environments/develop/terraform.tfvars
./environments/develop/common.tf
./environments/preproduction
./environments/preproduction/main.tf
./environments/preproduction/terragrunt.hcl
./environments/preproduction/terraform.tfvars
./environments/preproduction/common.tf
./environments/prod-dr
./environments/prod-dr/main.tf
./environments/prod-dr/terragrunt.hcl
./environments/prod-dr/terraform.tfvars
./environments/prod-dr/common.tf
./environments/preproduction-dr
./environments/preproduction-dr/main.tf
./environments/preproduction-dr/terragrunt.hcl
./environments/preproduction-dr/terraform.tfvars
./environments/preproduction-dr/common.tf
./README.rst
./modules
./modules/secrets-manager
./modules/secrets-manager/main.tf
./modules/s3
./modules/s3/main.tf
./modules/cognito
./modules/cognito/main.tf
./modules/cloudfront
./modules/cloudfront/main.tf
./modules/cloudfront/files
./modules/cloudfront/files/lambda.zip
./modules/cloudfront/main.py
./modules/all
./modules/all/ecs.tf
./modules/all/data.tf
./modules/all/db-migration.tf
./modules/all/s3.tf
./modules/all/kms.tf
./modules/all/rds-iam-auth.tf
./modules/all/network.tf
./modules/all/acm.tf
./modules/all/cloudfront.tf
./modules/all/templates
./modules/all/lambda.tf
./modules/all/tgw.tf
./modules/all/guardduty.tf
./modules/all/cognito.tf
./modules/all/step-functions.tf
./modules/all/secrets-manager.tf
./modules/all/api-gateway.tf
./modules/all/rds.tf
./modules/all/cloudtrail.tf
./modules/all/vars.tf
./modules/ecs
./modules/ecs/cluster
./modules/ecs/cluster/main.tf
./modules/ecs/task
./modules/ecs/task/main.tf
./modules/step-functions
./modules/step-functions/main.tf
./modules/api-gw
./modules/api-gw/resource
./modules/api-gw/resource/main.tf
./modules/api-gw/method
./modules/api-gw/method/main.tf
./modules/api-gw/rest-api
./modules/api-gw/rest-api/main.tf
./modules/cloudtrail
./modules/cloudtrail/main.tf
./modules/cloudtrail/README.rst
./modules/transit-gateway
./modules/transit-gateway/attachment
./modules/transit-gateway/attachment/main.tf
./modules/transit-gateway/README.rst
./modules/transit-gateway/gateway
./modules/transit-gateway/gateway/main.tf
./modules/openvpn-as
./modules/openvpn-as/main.tf
./modules/load-balancer
./modules/load-balancer/outputs.tf
./modules/load-balancer/main.tf
./modules/load-balancer/vars.tf
./modules/lambda
./modules/lambda/main.tf
./modules/vpc
./modules/vpc/3tier
./modules/vpc/3tier/main.tf
./modules/vpc/3tier/README.rst
./modules/vpc/peering
./modules/vpc/peering/main.tf
./modules/vpc/peering/README.rst
./modules/vpc/public
./modules/vpc/public/main.tf
./modules/vpc/public/README.rst
./modules/vpc/endpoint
./modules/vpc/endpoint/main.tf
./modules/vpc/README.rst
./modules/vpc/isolated
./modules/vpc/isolated/main.tf
./modules/vpc/isolated/README.rst
./modules/vpc/subnets
./modules/vpc/subnets/main.tf
./modules/vpc/subnets/README.rst
./modules/guardduty
./modules/guardduty/README.md
./modules/guardduty/region
./modules/guardduty/region/main.tf
./modules/guardduty/region/guardduty.tf
./modules/guardduty/region/sns-topic.tf
./modules/guardduty/region/vars.tf
./modules/guardduty/.gitignore
./modules/guardduty/base
./modules/guardduty/base/data.tf
./modules/guardduty/base/guardduty-sqs.tf
./modules/guardduty/base/guardduty-lambda.tf
./modules/guardduty/base/variables.tf
./modules/guardduty/base/guardduty-kms.tf
./modules/guardduty/base/bucket.tf
./modules/guardduty/base/guardduty-sns.tf
./modules/guardduty/base/src
./modules/guardduty/base/src/guardduty_findings_relay.py
./modules/guardduty/base/src/guardduty_findings_relay.zip
./modules/jenkins
./modules/jenkins/main.tf
./modules/rds
./modules/rds/main.tf
./modules/acm
./modules/acm/main.tf
,
我与 terraform
一起工作了 5 年。在我的职业生涯中,我在模块和环境方面犯了很多错误。
下面的文字只是我的知识和经验的分享。他们可能很糟糕。
真正的示例项目可能很难找到,因为 terraform
不用于创建开源项目。共享 terraform
文件通常是不安全的,因为您会暴露内部结构中的所有漏洞
模块用途和大小
您应该创建具有单一用途的模块,但您的模块应该是通用的。
示例模块
您可以创建 bastion host module
,但更好的主意是创建一个 module for generic server
。此模块可能有一些专门用于解决您的业务问题的逻辑,例如 CW Log group
、一些通用的 security group rules
等。
应用模块
有时值得创建更具体的模块。
假设您有一个应用程序,它需要 Lambda
、ECS service
、CloudWatch alarms
、RDS
、EBS
等。所有这些元素都紧密相连。
您有两个选择:
- 为上述每个项目创建单独的模块 - 但是您的应用程序使用 5 个模块。
- 创建一个大模块,然后您可以使用单个模块部署您的应用
- 混合上述解决方案 - 我更喜欢
一切都取决于细节和某些情况。
但我将向您展示我如何在不同公司的作品中使用 terraform。
分离资源的分离定义
这是项目,您将环境作为目录。对于每个应用程序、网络、数据资源,您都有单独的状态。我将可变数据保存在单独的目录中(如 RDS、EBS、EFS、S3 等),因此所有应用程序、网络等都可以被销毁和重新创建,因为它们是无状态的。没有人可以销毁有状态的项目,因为数据可能会丢失。这就是我过去几年一直在做的事情。
project/
├─ packer/
├─ ansible/
├─ terraform/
│ ├─ environments/
│ │ ├─ production/
│ │ │ ├─ apps/
│ │ │ │ ├─ blog/
│ │ │ │ ├─ ecommerce/
│ │ │ ├─ data/
│ │ │ │ ├─ efs-ecommerce/
│ │ │ │ ├─ rds-ecommerce/
│ │ │ │ ├─ s3-blog/
│ │ │ ├─ general/
│ │ │ │ ├─ main.tf
│ │ │ ├─ network/
│ │ │ │ ├─ main.tf
│ │ │ │ ├─ terraform.tfvars
│ │ │ │ ├─ variables.tf
│ │ ├─ staging/
│ │ │ ├─ apps/
│ │ │ │ ├─ ecommerce/
│ │ │ │ ├─ blog/
│ │ │ ├─ data/
│ │ │ │ ├─ efs-ecommerce/
│ │ │ │ ├─ rds-ecommerce/
│ │ │ │ ├─ s3-blog/
│ │ │ ├─ network/
│ │ ├─ test/
│ │ │ ├─ apps/
│ │ │ │ ├─ blog/
│ │ │ ├─ data/
│ │ │ │ ├─ s3-blog/
│ │ │ ├─ network/
│ ├─ modules/
│ │ ├─ apps/
│ │ │ ├─ blog/
│ │ │ ├─ ecommerce/
│ │ ├─ common/
│ │ │ ├─ acm/
│ │ │ ├─ user/
│ │ ├─ computing/
│ │ │ ├─ server/
│ │ ├─ data/
│ │ │ ├─ efs/
│ │ │ ├─ rds/
│ │ │ ├─ s3/
│ │ ├─ networking/
│ │ │ ├─ alb/
│ │ │ ├─ front-proxy/
│ │ │ ├─ vpc/
│ │ │ ├─ vpc-pairing/
├─ tools/
要申请单个应用程序,您需要:
cd ./project/terraform/environments/<ENVIRONMENT>/apps/blog;
terraform apply;
你可以看到所有环境中都有很多目录。正如我所见,这些工具各有利弊。
缺点:
- 很难检查所有模块是否同步
- 复杂的 CI
- 复杂的目录结构,特别是对于团队中的新人来说,但这是逻辑
- 可能有很多依赖项,但如果你从一开始就考虑,这不是问题。
- 您需要小心,以保持完全相同的环境。
- 需要进行大量初始化,并且很难进行重构。
优点:
- 小改动后快速应用
- 分离的应用程序和资源。在不了解整个系统的情况下,很容易修改小模块或小部署
- 取下东西后更容易清理
- 很容易判断哪个模块需要修复。我使用自己编写的一些工具来分析基础架构特定部分的状态,并且可以向特定开发人员发送电子邮件,告知他的基础架构由于某些原因需要重新同步。
- 与单体相比,您可以更轻松地拥有不同的环境。如果您在环境中不需要它,您可以销毁单个应用程序
单体架构
上次我开始在新公司工作。它们将基础架构定义保存在几个巨大的存储库(或文件夹)中,当您执行 terraform apply 时,您会同时创建所有应用程序。
project/
├─ modules/
│ ├─ acm/
│ ├─ app-blog/
│ ├─ app-ecommerce/
│ ├─ server/
│ ├─ vpc/
├─ vars/
│ ├─ user/
│ ├─ prod.tfvars
│ ├─ staging.tfvars
│ ├─ test.tfvars
├─ applications.tf
├─ providers.tf
├─ proxy.tf
├─ s3.tf
├─ users.tf
├─ variables.tf
├─ vpc.tf
这里为每个环境准备不同的输入值。
例如,您想对 prod 应用更改:
terraform apply -var-file=vars/prod.tfvars -lock-timeout=300s
应用暂存:
terraform apply -var-file=vars/staging.tfvars -lock-timeout=300s
缺点:
- 您没有依赖项,但有时您需要手动准备一些环境元素,如域、弹性 IP 等,或者您需要在
terraform plan/apply
之前创建它们。那么你有问题 - 很难清理,因为您同时拥有数百个资源和模块
- 超长的 terraform 执行时间。此处规划/应用单个环境大约需要 45 分钟
- 很难理解整个环境。
- 如果您保持该结构以分离网络、应用程序、DNS 等,通常您需要拥有 2/3 存储库...
- 您需要做更多的工作来应对不同的环境。您需要使用计数等...
优点:
- 很容易检查您的基础架构是否是最新的
- 没有复杂的目录结构...
- 您所有的环境都完全相同。
- 重构可能更容易,因为您在很少的地方拥有所有资源。
- 需要少量初始化。
总结
正如你所看到的,这是更多的架构问题,学习它的唯一方法是获得更多经验或阅读其他人的一些帖子......
我仍在尝试找出最佳方式,我可能会尝试第一种方式。
不要把我的优势当作确定的事情。这篇文章只是我的经验,可能不是最好的。
参考文献
我会发布一些对我有很大帮助的参考资料:
- https://www.terraform-best-practices.com/
- https://github.com/antonbabenko/terraform-best-practices-workshop
我意识到正如@MarkB 所建议的那样,terraform 工作区实际上是多环境项目的解决方案。
所以我的项目结构看起来像这样:
infra/
dev/
dev.tfvars
stage/
stage.tfvars
provider.tf
main.tf
variables.tf
main.tf 引用模块,provider.tf 设置提供程序,backend.tf 将设置远程后端(尚未添加)等
此配置中的 'terraform plan' 变为 'terraform plan -var-file dev/dev.tfvars',我在其中指定具有特定配置的文件环境。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。