使用Terraform的最佳做法

我正在将我们的基础设施交换到terraform。 实际pipe理terraform文件和状态的最佳做法是什么? 我意识到这是基础设施的代码,我会提交我的.tf文件到git,但我也提交tfstate? 应该像S3一样居住? 我最终希望CI能够pipe理所有这些,但是这是非常困难的,需要我找出文件的移动部分。

我真的只是想看看那里的人们如何在生产中实际使用这种types的东西

我也处于将现有的AWS基础设施迁移到Terraform的状态,所以我的目标是在我开发时更新答案。

我一直非常依赖Terraform官方的例子和多次反复试验来填补我不确定的地方。

.tfstate文件

可以使用Terraformconfiguration在不同的基础设施上configuration多个盒子,每个盒子可以具有不同的状态。 由于它也可以由多人运行这个状态应该在一个集中的位置(如S3),而不是混帐。

这可以证实看Terraform .gitignore

开发者控制

我们的目标是为开发人员提供更多的基础设施控制权,同时保持完整的审计(git log)和完整性检查更改(pull请求)的能力。 考虑到这一点,我所瞄准的新基础架构工作stream程是:

  1. 常见AMI的基础包括可重复使用的模块,例如木偶。
  2. DevOps使用Terraform提供的核心基础架构。
  3. 开发人员根据需要在Git中更改Terraformconfiguration(实例数量;新VPC;添加区域/可用区域等)。
  4. 推送Gitconfiguration,并提交一个由DevOps小组成员进行健全性检查的拉取请求。
  5. 如果获得批准,则将webhook调用到CI以构build和部署(此时不确定如何分区多个环境)

编辑1 – 更新当前状态

自从开始这个答案以来,我写了大量的TF代码,感觉在我们的事务中更加舒适。 我们已经遇到了一些错误和限制,但是我接受这是使用新的,快速变化的软件的一个特征。

布局

我们有一个复杂的AWS基础设施和多个VPC,每个都有多个子网。 轻松pipe理这一点的关键是定义一个灵活的分类法,其中包括我们可以用来组织我们的基础设施代码(地形和木偶)的区域,环境,服务和所有者。

模块

下一步是创build一个单独的git仓库来存储我们的terraform模块。 我们的模块顶级dir结构如下所示:

tree -L 1 . . ├── README.md ├── aws-asg ├── aws-ec2 ├── aws-elb ├── aws-rds ├── aws-sg ├── aws-vpc └── templates

每一个设置一些理智的默认值,但暴露他们作为可以被我们的“胶水”覆盖的variables。

我们有一个使用上面提到的模块的第二个存储库。 它与我们的分类文档一致:

. ├── README.md ├── clientA │ ├── eu-west-1 │ │ └── dev │ └── us-east-1 │ └── dev ├── clientB │ ├── eu-west-1 │ │ ├── dev │ │ ├── ec2-keys.tf │ │ ├── prod │ │ └── terraform.tfstate │ ├── iam.tf │ ├── terraform.tfstate │ └── terraform.tfstate.backup └── clientC ├── eu-west-1 │ ├── aws.tf │ ├── dev │ ├── iam-roles.tf │ ├── ec2-keys.tf │ ├── prod │ ├── stg │ └── terraform.tfstate └── iam.tf

在客户端级别内部,我们拥有提供全球资源(如IAMangular色)的AWS账户特定.tf文件; 接下来是区域级与EC2 SSH公钥; 最后,在我们的环境( devstgprod等)是我们的VPC设置,实例创build和对等连接等存储。

注意:正如你所看到的,我违背了我自己的build议,在git中保持terraform.tfstate 。 这是一个临时的措施,直到我搬到S3,但适合我,因为我目前是唯一的开发者。

下一步

这仍然是一个手动过程,而不是jenkins,但我们正在移植一个相当庞大,复杂的基础设施,迄今为止非常好。 就像我说的,没有什么问题,但是进展顺利!

编辑2 – 更改

我写了这个最初的答案已经快一年了,Terraform和我自己的状态都发生了很大的变化。 我现在处于使用Terraform来pipe理Azure集群的新位置,Terraform现在是v0.10.7

人们一再告诉我,国家不应该去Git – 他们是正确的。 我们用这个作为临时措施与一个依靠开发者沟通和纪律的两个人小组。 通过一个更大的分布式团队,我们现在可以完全利用S3中的远程状态和DynamoDB提供的locking。 理想情况下,这将被迁移到领事现在是v1.0切断跨云提供商。

模块

以前我们创build并使用了内部模块。 这仍然是这种情况,但随着Terraformregistry的出现和增长,我们尝试将这些作为至less一个基础。

文件结构

新的职位只有两个infx环境 – devprod分类法简单得多。 每个都有自己的variables和输出,重用我们上面创build的模块。 remote_state提供程序还有助于在环境之间共享已创build资源的输出。 我们的场景是不同的Azure资源组中的子域到全局托pipe的顶级域名。

├── main.tf ├── dev │ ├── main.tf │ ├── output.tf │ └── variables.tf └── prod ├── main.tf ├── output.tf └── variables.tf

规划

再次面临分布式团队的额外挑战,我们现在总是保存我们的terraform plan命令的输出。 我们可以检查并知道在planapply阶段之间会有什么变化的风险(尽pipelocking有帮助)。 请记住删除此计划文件,因为它可能包含纯文本“秘密”variables。

总的来说,我们对Terraform感到非常满意,并且不断学习和改进新function。

我们大量使用Terraform,我们推荐的设置如下:

文件布局

我们强烈build议将您的每个环境(例如stage,prod,qa)的Terraform代码存储在不同的模板集合(因此,单独的.tfstate文件)中。 这很重要,这样一来,在进行更改时,您的独立环境就会彼此隔离。 否则,在升级代码的同时,也很容易炸掉某些东西。 请参阅Terraform,VPC,以及为什么您需要每个env的tfstate文件来讨论原因。

因此,我们典型的文件格式如下所示:

 stage └ main.tf └ vars.tf └ outputs.tf prod └ main.tf └ vars.tf └ outputs.tf global └ main.tf └ vars.tf └ outputs.tf 

VPC阶段的所有Terraform代码进入stage文件夹,产品VPC的所有代码都进入prod文件夹,并且生活在VPC之外的所有代码(例如,IAM用户,SNS主题,S3存储桶)进入global文件夹。

请注意,按照惯例,我们通常将我们的Terraform代码分解成3个文件:

  • vars.tf :inputvariables。
  • outputs.tf :输出variables。
  • main.tf :实际的资源。

模块

通常,我们在两个文件夹中定义基础设施:

  1. infrastructure-modules :这个文件夹包含小的,可重用的版本模块。 将每个模块想象成如何创build单个基础设施(如VPC或数据库)的蓝图。
  2. infrastructure-live :该文件夹包含实际运行中的基础架构,它通过组合infrastructure-modules模块中infrastructure-modules 。 把这个文件夹中的代码想象成你从蓝图中build立起来的实际房屋。

Terraform模块只是一个文件夹中的任何一组Terraform模板。 例如,我们可能在infrastructure-modules中有一个名为vpc的文件夹,它定义了单个VPC的所有路由表,子网,网关,ACL等:

 infrastructure-modules └ vpc └ main.tf └ vars.tf └ outputs.tf 

然后,我们可以在infrastructure-live/stageinfrastructure-live/prod制作中使用该模块来创build舞台和制作VPC。 例如,这里是infrastructure-live/stage/main.tf可能看起来像:

 module "stage_vpc" { source = "git::git@github.com:gruntwork-io/module-vpc.git//modules/vpc-app?ref=v0.0.4" vpc_name = "stage" aws_region = "us-east-1" num_nat_gateways = 3 cidr_block = "10.2.0.0/18" } 

要使用模块,请使用module资源并将其source域指向硬盘上的本地path(例如, source = "../infrastructure-modules/vpc" ),或者如上例所示,将Git URL (请参阅模块来源 )。 Git URL的优点是我们可以指定一个特定的git sha1或标签( ref=v0.0.4 )。 现在,我们不仅将我们的基础架构定义为一堆小模块,还可以根据需要对这些模块进行版本升级或仔细更新或回滚。

我们已经创build了许多可重复使用,经过testing和logging的基础设施软件包,用于创buildVPC,Docker集群,数据库等等,而且其中大部分都是版本化的Terraform模块。

当您使用Terraform创build资源(例如EC2实例,数据库,VPC)时,它将logging在.tfstate文件中创build的信息。 要对这些资源进行更改,您团队中的每个人都需要访问这个.tfstate文件,但是您不应该将其检入到Git中(请参阅此处以解释原因 )。

相反,我们build议通过启用Terraform远程状态在S3中存储.tfstate文件,每次运行Terraform时都会自动推/拉最新的文件。 请确保在S3存储桶中启用版本控制 ,以便您可以回滚到较旧的.tfstate文件,以防以某种方式损坏最新版本。 但是,一个重要的注意事项: Terraform不提供locking 。 所以如果两个团队成员运行terraform apply在同一个.tfstate文件同时.tfstate ,他们可能会最终覆盖彼此的变化。

为了解决这个问题,我们创build了一个名为Terragrunt的开源工具,它是Terraform的一个精简包装,它使用Amazon DynamoDB提供locking(对大多数团队应该是完全免费的)。 查看使用Terragrunt将自动远程状态locking和configuration添加到Terraform以获取更多信息。

进一步阅读

我们刚刚开始了一系列名为“综合地形指南”的博客文章,详细描述了我们在现实世界中使用Terraform所学到的所有最佳实践。

更新:Terraform博客系列综合指南非常受欢迎,因此我们将其扩展到一本名为Terraform:Up&Running的书

使用remote config ,现在变得更简单了:

 terraform remote config -backend-config="bucket=<s3_bucket_to_store_tfstate>" -backend-config="key=terraform.tfstate" -backend=s3 terraform remote pull terraform apply terraform remote push 

有关详细信息,请参阅文档 。

@Yevgeny Brikman更深入地报道,但是专门回答OP的问题:

 What's the best practice for actually managing the terraform files and state? 

使用git的TF文件。 但是不要检查状态文件(即tfstate)。 而是使用Terragrunt将状态文件同步/locking到S3。

 but do I commit tfstate as well? 

没有。

 Should that reside somewhere like S3?