微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何为多种环境组织 terraform 模块? 示例模块应用模块分离资源的分离定义单体架构总结

如何解决如何为多种环境组织 terraform 模块? 示例模块应用模块分离资源的分离定义单体架构总结

网络上的每个 terraform 指南都提供了部分解决方案,几乎总是不是真实的图片
我明白了,并不是每个人都有相同的基础设施需求,但让我担心的是常见场景:

  1. 多种环境(开发、阶段)
  2. 远程后端 (s3)
  3. 一些基本资源(bucket 或 ec2 实例)

未在真实示例项目中的任何地方出现。
我正在寻找那个,同时,我已经研究并得出结论,除了我还想要的那些需求:

  1. 利用模块
  2. 不使用工作区,而是使用不同的每个环境目录方法
  3. 不要使用 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.tfstatestage.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 等。

应用模块

有时值得创建更具体的模块。

假设您有一个应用程序,它需要 LambdaECS serviceCloudWatch alarmsRDSEBS 等。所有这些元素都紧密相连。

您有两个选择:

  • 为上述每个项目创建单独的模块 - 但是您的应用程序使用 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 存储库...
  • 您需要做更多的工作来应对不同的环境。您需要使用计数等...

优点:

  • 很容易检查您的基础架构是否是最新的
  • 没有复杂的目录结构...
  • 您所有的环境都完全相同。
  • 重构可能更容易,因为您在很少的地方拥有所有资源。
  • 需要少量初始化。

总结

正如你所看到的,这是更多的架构问题,学习它的唯一方法是获得更多经验或阅读其他人的一些帖子......

我仍在尝试找出最佳方式,我可能会尝试第一种方式。

不要把我的优势当作确定的事情。这篇文章只是我的经验,可能不是最好的。

参考文献

我会发布一些对我有很大帮助的参考资料:

,

我意识到正如@MarkB 所建议的那样,terraform 工作区实际上是多环境项目的解决方案。

所以我的项目结构看起来像这样:

infra/
  dev/
    dev.tfvars
  stage/
    stage.tfvars 
  provider.tf
  main.tf
  variables.tf

ma​​in.tf 引用模块,provider.tf 设置提供程序,backend.tf 将设置远程后端(尚未添加)等

此配置中的 'terraform plan' 变为 'terraform plan -var-file dev/dev.tfvars',我在其中指定具有特定配置的文件环境。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?