使用Terraform时的最佳实践
我正在将我们的基础设施交换到terraform。 实际管理terraform文件和状态的最佳做法是什么? 我意识到它的基础设施是代码,我会将我的.tf文件提交到git中,但是我也提交了tfstate吗?应该像S3一样驻留在什么地方?我最终希望CI能够管理所有这些,但是这非常困难,需要我找出文件的移动部分。使用Terraform时的最佳实践
我真的只是想看看人们是如何在那里实际利用这种类型的东西在生产
我也是在迁移现有AWS基础设施的状态对terraform所以应旨在更新的答案,因为我发展。
我一直严重依赖于官方Terraform examples和多次试验和错误来充实,我一直在不确定的地方。
.tfstate文件
Terraform配置可用于提供许多箱子放在不同的基础设施上,每个箱子都可能有不同的状态。由于它也可以由多人运行,所以此状态应该位于中央位置(如S3),但而不是 git。
这可以通过查看Terraform .gitignore
来确认。
开发控制
我们的目标是为开发者提供基础设施进行更多的控制,同时保持一个完整的审计(GIT日志),并有能力完整性检查的改变(引入请求)。考虑到这一点,我期望的新基础架构工作流程是:
- 常见AMI的基础包括可重复使用的模块,例如,木偶。
- DevOps使用Terraform提供的核心基础架构。
- 开发人员根据需要在Git中更改Terraform配置(实例数量;新VPC;添加区域/可用区域等)。
- 推送Git配置,并提交一份由DevOps小组成员进行健全性检查的拉取请求。
- 如果获得批准,调用网络挂接到CI构建和部署(不确定如何在这段时间划分多个环境)
编辑1 - 对当前状态
更新自从开始这个答案我有写了大量的TF代码,感觉在我们的事态中更加舒适。我们一路上遇到了错误和限制,但我接受这是使用新的,快速变化的软件的特征。
布局
我们有多个VPC的每个多个子网的复杂AWS基础设施。轻松管理这一点的关键是定义一个灵活的分类法,其中包含我们可以用来组织我们的基础设施代码(地形和木偶)的区域,环境,服务和所有者。
模块
下一步是建立一个单一的git仓库来存储terraform模块。对于模块我们的顶级目录结构如下:
tree -L 1 . . ├── README.md ├── aws-asg ├── aws-ec2 ├── aws-elb ├── aws-rds ├── aws-sg ├── aws-vpc └── templates
每一个设置了一些理智的默认值,但将它们公开为可通过我们的“胶水”,被覆盖的变量。
胶
我们与glue
第二库,使得使用上述模块。它在线路布局与我们的分类文档:
. ├── 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
里面我们有AWS客户端级别的特定帐户.tf
文件提供全球资源(如IAM角色);接下来是使用EC2 SSH公钥的区域级别;最后在我们的环境中(dev
,stg
,prod
等)是我们的VPC设置,存储实例创建和对等连接等。
附注:正如你所看到的,我违背了我自己的建议,保持terraform.tfstate
在git中。这是一个临时的措施,直到我搬到S3,但适合我,因为我目前是唯一的开发者。
下一步
这仍然是詹金斯一个手动过程,还没有,但我们正在移植一个相当大的,复杂的基础设施,到目前为止,一切顺利。就像我说的,很少有错误,但进展顺利!
编辑2 - 更改
这几乎是一年以来我写了这个最初的回答无一不Terraform的状态,自己也显著改变。我现在处于使用Terraform管理Azure集群的新位置,Terraform现在是v0.10.7
。
国家
人们一再告诉我的状态应该不 Git中去 - 他们是正确的。我们将此作为临时措施,与一个依靠开发人员沟通和纪律的双人团队合作。通过一个更大的分布式团队,我们现在可以完全利用S3中的远程状态和DynamoDB提供的locking。理想情况下,这将被移交给consul现在它是v1.0来削减跨云提供商。
模块
以前我们创建和使用的内部模块。这仍然是这种情况,但随着Terraform registry的到来和增长,我们尝试将这些用作至少一个基地。
文件结构
新的位置有只有两个INFX环境更简单的分类 - dev
和prod
。每个都有自己的变量和输出,重用我们上面创建的模块。 remote_state
提供程序还有助于在环境之间共享已创建资源的输出。我们的场景是不同Azure资源组中的子域到全局托管TLD。
├── main.tf ├── dev │ ├── main.tf │ ├── output.tf │ └── variables.tf └── prod ├── main.tf ├── output.tf └── variables.tf
规划
与分布式团队的额外挑战同样,我们现在总是拯救我们的terraform plan
命令的输出。我们可以检查并知道在没有和apply
阶段之间发生某些变化的风险的情况下将运行什么(尽管锁定有助于此)。请记住删除此计划文件,因为它可能包含纯文本“秘密”变量。
总的来说,我们对Terraform非常满意,并且不断学习和改进新功能。
我们使用Terraform很大程度上和我们推荐的设置如下:
文件布局
我们强烈建议您存储Terraform代码为每个环境中单独设置的(例如阶段,督促,QA)的模板(因此,单独的.tfstate
文件)。这很重要,以便在进行更改时,您的独立环境实际上彼此隔离。否则,在分阶段搞乱一些代码的时候,太容易炸掉一些东西了。请参阅Terraform, VPC, and why you want a tfstate file per env了解为什么多彩的讨论。
因此,我们典型的文件格式是这样的:
stage
└ main.tf
└ vars.tf
└ outputs.tf
prod
└ main.tf
└ vars.tf
└ outputs.tf
global
└ main.tf
└ vars.tf
└ outputs.tf
所有阶段VPC进入stage
文件夹Terraform代码,督促VPC进入prod
文件夹中的所有代码和所有VPC之外的代码(例如IAM用户,SNS主题,S3存储桶)进入global
文件夹。
需要注意的是,按照惯例,我们一般把我们的Terraform代码分解成3个文件:
-
vars.tf
:输入变量。 -
outputs.tf
:输出变量。 -
main.tf
:实际资源。
模块
通常情况下,我们定义两个文件夹中的基础设施:
-
infrastructure-modules
:此文件夹包含小,可重复使用,版本控制模块。将每个模块视为如何创建单个基础结构(如VPC或数据库)的蓝图。 -
infrastructure-live
:该文件夹包含实际的实时运行基础结构,它通过组合infrastructure-modules
中的模块创建。把这个文件夹中的代码想象成你从你的蓝图构建的房屋。
A Terraform module只是文件夹中的任意一组Terraform模板。例如,我们可能有一个在infrastructure-modules
称为vpc
文件夹中定义了所有的路由表,子网,网关,访问控制列表等单个VPC:
infrastructure-modules
└ vpc
└ main.tf
└ vars.tf
└ outputs.tf
然后我们就可以使用该模块中infrastructure-live/stage
和infrastructure-live/prod
创建舞台和产品VPCs。例如,这里是infrastructure-live/stage/main.tf
可能是什么样子:
module "stage_vpc" {
source = "git::[email protected]: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
场要么你的硬盘驱动器上的本地路径(例如source = "../infrastructure-modules/vpc"
),或在上面的例子是一个Git URL(见module sources)。 Git URL的优点是我们可以指定一个特定的git sha1或标签(ref=v0.0.4
)。现在,我们不仅将我们的基础架构定义为一堆小模块,还可以根据需要对这些模块进行版本升级或仔细更新或回滚。
我们为创建VPC,Docker集群,数据库等创建了一系列可重复使用,经过测试并记录在案的文档Infrastructure Packages,而且其中大部分都是版本化的Terraform模块。
国家
当您使用Terraform创建资源(如EC2实例,数据库的VPC),它记录的是什么在.tfstate
文件创建的信息。要对这些资源进行更改,您团队中的每个人都需要访问此文件,但不应将其检入到Git中(请参阅here for an explanation why)。
相反,我们建议您在S3中启用.tfstate
文件,启用Terraform Remote State,每次运行Terraform时都会自动推送/提取最新文件。请确保您的S3存储桶中有enable versioning,以便您可以回滚到旧的.tfstate
文件,以防止以某种方式损坏最新版本。但是,一个重要的注意事项:Terraform不提供锁定。所以如果两个团队成员在同一个.tfstate
文件上同时运行terraform apply
,他们最终可能会覆盖彼此的更改。
为了解决这个问题,我们创建了一个名为Terragrunt的开源工具,它是Terraform的一个精简包装器,它使用Amazon DynamoDB提供锁定(对大多数团队应该完全免费)。查看Add Automatic Remote State Locking and Configuration to Terraform with Terragrunt以了解更多信息。
进一步阅读
我们刚刚启动了一项名为A Comprehensive Guide to Terraform一系列博客文章,里面详细描述了所有我们学会了在现实世界中使用Terraform的最佳实践。
更新:Terraform博客系列综合指南非常受欢迎,因此我们将其扩展为一本名为Terraform: Up & Running的书!
我认为这是正确的答案。使用模块,对其进行版本控制,并保持环境分离。 – DrewVS
每次你想在不同的terraform组件/环境/模块上工作时,是否需要重新运行远程配置步骤(不管是否使用terragrunt或其他包装器)? – jmreicha
@jmreicha:如果您只是签出Terraform配置或者您想更改以前的远程配置,则需要运行'remote config'。 Terraform 0.9将引入'backends'的概念,这将简化很多这个。有关更多详细信息,请参见[此PR](https://github.com/hashicorp/terraform/pull/11286)。 –
通过@Yevgeny Brikman中有更深入的而是专门回答OP的问题:对TF文件
What's the best practice for actually managing the terraform files and state?
使用git。但是不要检查状态文件(即tfstate)。而是使用Terragrunt
将状态文件同步/锁定到S3。
but do I commit tfstate as well?
号
Should that reside somewhere like S3?
是
你有过因为这个答案运气好的话/问题?你的看起来很像我打算做的事,但你可能比我更进一步。 –
不错!我仍然无法在我身边实施,我们正在清理一些其他的东西,但知道一些要做/避免的事情真是太棒了。 –
我很好奇你为什么认为tfstate文件不应该存储在混帐?仅仅是因为旧国家不值得储蓄,还是有其他问题? – agbodike