我正在将我们的基础设施交换成terraform。 实际管理terraform文件和状态的最佳做法是什么? 我将它的基础设施视为代码,并且我将我的.tf文件提交到git中,但我也提交了tfstate吗?它应该驻留在S3这样的地方吗?我最终希望CI能够管理所有这些,但这种情况已经很紧张,需要我找出文件的移动部分。
我真的只是想看看那里的人们是如何在生产中使用这类东西的
答案 0 :(得分:71)
我们大量使用Terraform,我们推荐的设置如下:
我们强烈建议将每个环境(例如stage,prod,qa)的Terraform代码存储在不同的模板集中(因此,单独的.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的所有Terraform代码都进入stage
文件夹,prod VPC的所有代码都进入prod
文件夹,以及生活在VPC之外的所有代码(例如IAM用户,SNS主题,S3存储桶)进入global
文件夹。
请注意,按照惯例,我们通常会将Terraform代码分解为3个文件:
vars.tf
:输入变量。outputs.tf
:输出变量。main.tf
:实际资源。通常,我们在两个文件夹中定义基础架构:
infrastructure-modules
:此文件夹包含小型,可重复使用的版本化模块。将每个模块视为如何创建单个基础架构(如VPC或数据库)的蓝图。infrastructure-live
:此文件夹包含实际的实时运行基础架构,它是通过组合infrastructure-modules
中的模块创建的。将此文件夹中的代码视为您根据蓝图构建的实际房屋。 Terraform module只是文件夹中的任何一组Terraform模板。例如,我们可能在vpc
中有一个名为infrastructure-modules
的文件夹,它定义了单个VPC的所有路由表,子网,网关,ACL等:
infrastructure-modules
└ vpc
└ main.tf
└ vars.tf
└ outputs.tf
然后我们可以在infrastructure-live/stage
和infrastructure-live/prod
中使用该模块来创建舞台和prod 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
字段指向硬盘驱动器上的本地路径(例如source = "../infrastructure-modules/vpc"
),或者如上例所示,一个Git URL(见module sources)。 Git URL的优点是我们可以指定特定的git sha1或标记(ref=v0.0.4
)。现在,我们不仅将基础架构定义为一堆小模块,而且我们可以对这些模块进行版本化,并根据需要仔细更新或回滚。
我们已经创建了许多可重用,经过测试和记录的Infrastructure Packages来创建VPC,Docker群集,数据库等等,而且大多数都只是版本化的Terraform模块。
当您使用Terraform创建资源(例如EC2实例,数据库,VPC)时,它会记录有关在.tfstate
文件中创建的内容的信息。要对这些资源进行更改,团队中的每个人都需要访问同一个.tfstate
文件,但不应将其检入Git(请参阅here for an explanation why)。
相反,我们建议您通过启用Terraform Remote State在{3}}中存储.tfstate
个文件,这会在每次运行Terraform时自动推送/拉取最新文件。确保在您的S3存储桶中enable versioning,以便您可以回滚到较旧的.tfstate
文件,以防您以某种方式损坏最新版本。但是,一个重要的注意事项: Terraform不提供锁定。因此,如果两个团队成员同时在同一个terraform apply
文件上运行.tfstate
,则他们最终可能会覆盖彼此的更改。
为了解决这个问题,我们创建了一个名为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 的书!
答案 1 :(得分:68)
我也处于将现有AWS基础架构迁移到Terraform的状态,因此我的目标是在我开发时更新答案。
我一直非常依赖官方的Terraform examples和多次试错,以充实我不确定的领域。
.tfstate
个文件
Terraform配置可用于在不同基础架构上配置多个盒子,每个盒子可能具有不同的状态。由于它也可以由多个人运行,因此该状态应该位于集中位置(如S3),但不是 git。
可以通过Terraform .gitignore
确认这一点。
开发者控制
我们的目标是为开发人员提供更多的基础架构控制,同时保持完整的审计(git日志)和完整性检查更改(拉取请求)的能力。考虑到这一点,我的目标是:
编辑1 - 更新当前状态
自从开始这个答案以来,我已经编写了很多TF代码,并且在我们的事态中感觉更舒服。我们在此过程中遇到了错误和限制,但我接受这是使用新的,快速变化的软件的一个特征。
<强>布局强>
我们有一个复杂的AWS基础架构,其中有多个VPC,每个VPC都有多个子网。轻松管理这一点的关键是定义一个灵活的分类,包括区域,环境,服务和所有者,我们可以使用它来组织我们的基础设施代码(terraform和puppet)。
<强>模块强>
下一步是创建一个git存储库来存储我们的terraform模块。我们的模块的顶级目录结构如下所示:
tree -L 1 .
结果:
├── README.md
├── aws-asg
├── aws-ec2
├── aws-elb
├── aws-rds
├── aws-sg
├── aws-vpc
└── templates
每个人设置一些理智的默认值,但将它们公开为可以被我们的&#34;胶水&#34;覆盖的变量。
<强>胶强>
我们有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
命令的输出。我们可以检查并知道将运行什么,而不会在plan
和apply
阶段之间发生某些变化(尽管锁定有助于此)。请记住删除此计划文件,因为它可能包含纯文本&#34; secret&#34;变量。
总体而言,我们对Terraform非常满意,并继续学习和改进添加的新功能。
答案 2 :(得分:9)
以前remote config
允许这样做,但现在已被&#34; backends&#34;取代,因此terraform遥控器不再可用。
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
有关详细信息,请参阅docs。
答案 3 :(得分:4)
由@Yevgeny Brikman更深入地介绍,但特别回答OP的问题:
实际管理terraform文件和状态的最佳做法是什么?
将git用于TF文件。但是不要检查状态文件(即tfstate)。而是使用Terragrunt
将状态文件同步/锁定到S3。
但我也提交tfstate吗?
没有。
它应该存在于S3这样的地方吗?
是
答案 4 :(得分:1)
我知道这里有很多答案,但是我的方法却大不相同。
⁃ Modules
⁃ Environment management
⁃ Separation of duties
模块
环境管理
IaC已使SDLC流程与基础架构管理相关,并且期望拥有开发基础架构以及开发应用程序环境是不正常的。
职责分离
如果您所在的组织规模较小或正在运行个人基础架构,那么这实际上并不适用,但可以帮助您管理运营。
这也有助于解决发行方面的问题,因为您会发现某些资源很少更改,而其他资源则始终更改。分离消除了风险和复杂性。
该策略与AWS的多账户策略具有相似之处。阅读更多信息。
CI / CD
这是一个主题,但是Terraform在良好的管道中效果很好。这里最常见的错误是将CI视为灵丹妙药。从技术上讲,Terraform仅应在组装管道的阶段配置基础结构。这将与CI阶段(通常验证和测试模板)所发生的情况不同。
写在手机上,所以请原谅任何错误。
答案 5 :(得分:0)
如果您仍在寻找更好的解决方案,请看一看可以代替维护不同环境文件夹结构的工作空间,这些文件夹可以具有工作空间特定的变量。
与Yevgeniy Brikman mentioned一样,最好具有模块结构。
答案 6 :(得分:0)
在回答非常扎实和有益的内容之前,我将尝试添加 我的2分钱在这里
使用更少的资源可以更轻松,更快捷地工作:
terraform plan
和terraform
都应用make Cloud API调用来验证资源状态。爆炸半径较小,资源较少:
使用远程状态启动项目:
tfstate
文件是一场噩梦。尝试练习一致的结构和命名约定:
将资源模块保持尽可能简单。
不要硬编码可以作为变量传递或使用数据源发现的值。
使用data
来源和terraform_remote_state
作为组合中基础结构模块之间的粘合剂。
(参考文章: https://www.terraform-best-practices.com/code-structure)
示例:
使用更少的资源可以更轻松,更快捷地工作,因此 在下面,我们介绍了推荐的代码布局。
注意:仅供参考,因为每个项目都有其自己的特定特征,因此请勿严格遵循
.
├── 1_tf-backend #remote AWS S3 + Dynamo Lock tfstate
│ ├── main.tf
│ ├── ...
├── 2_secrets
│ ├── main.tf
│ ├── ...
├── 3_identities
│ ├── account.tf
│ ├── roles.tf
│ ├── group.tf
│ ├── users.tf
│ ├── ...
├── 4_security
│ ├── awscloudtrail.tf
│ ├── awsconfig.tf
│ ├── awsinspector.tf
│ ├── awsguarduty.tf
│ ├── awswaf.tf
│ └── ...
├── 5_network
│ ├── account.tf
│ ├── dns_remote_zone_auth.tf
│ ├── dns.tf
│ ├── network.tf
│ ├── network_vpc_peering_dev.tf
│ ├── ...
├── 6_notifications
│ ├── ...
├── 7_containers
│ ├── account.tf
│ ├── container_registry.tf
│ ├── ...
├── config
│ ├── backend.config
│ └── main.config
└── readme.md
答案 7 :(得分:0)
我相信在使用terraform编排基础结构时几乎不需要遵循最佳实践
- 不要再写相同的代码(可重用性)
- 保持环境配置分开,以轻松维护它。
- 使用远程后端s3(加密)和dynamo DB处理并发锁定
- 创建一个模块并在主基础架构中多次使用该模块,就像可重复使用的功能一样,可以通过传递不同的参数来多次调用它。
处理多个环境
大多数时候,建议的方法是使用terraform“工作区”来处理多个环境,但是我相信工作区的使用可能会根据组织中的工作方式而有所不同。 另一种是为您的每个环境(例如,舞台,产品,质量检查)存储Terraform代码以分隔环境状态。但是,在这种情况下,我们只是在许多地方复制相同的代码。
├── main.tf
├── dev
│ ├── main.tf
│ ├── output.tf
│ └── variables.tf
└── prod
├── main.tf
├── output.tf
└── variables.tf
我通过保留在每个环境文件夹中,采用了不同的方法来处理和避免相同的Terraform代码重复,因为我相信大多数情况下所有环境都是90%相同。
├── deployment
│ ├── 01-network.tf
│ ├── 02-ecs_cluster.tf
│ ├── 03-ecs_service.tf
│ ├── 04-eks_infra.tf
│ ├── 05-db_infra.tf
│ ├── 06-codebuild-k8s.tf
│ ├── 07-aws-secret.tf
│ ├── backend.tf
│ ├── provider.tf
│ └── variables.tf
├── env
│ ├── dev
│ │ ├── dev.backend.tfvar
│ │ └── dev.variables.tfvar
│ └── prod
│ ├── prod.backend.tfvar
│ └── prod.variables.tfvar
├── modules
│ └── aws
│ ├── compute
│ │ ├── alb_loadbalancer
│ │ ├── alb_target_grp
│ │ ├── ecs_cluster
│ │ ├── ecs_service
│ │ └── launch_configuration
│ ├── database
│ │ ├── db_main
│ │ ├── db_option_group
│ │ ├── db_parameter_group
│ │ └── db_subnet_group
│ ├── developertools
│ ├── network
│ │ ├── internet_gateway
│ │ ├── nat_gateway
│ │ ├── route_table
│ │ ├── security_group
│ │ ├── subnet
│ │ ├── vpc
│ └── security
│ ├── iam_role
│ └── secret-manager
└── templates
与环境相关的配置
将与环境相关的配置和参数保留在变量文件中,并传递该值以配置基础结构。例如如下
dev.backend.tfvar
region = "ap-southeast-2"
bucket = "dev-samplebackendterraform"
key = "dev/state.tfstate"
dynamo_db_lock = "dev-terraform-state-lock"
dev.variable.tfvar
environment = "dev"
vpc_name = "demo"
vpc_cidr_block = "10.20.0.0/19"
private_subnet_1a_cidr_block = "10.20.0.0/21"
private_subnet_1b_cidr_block = "10.20.8.0/21"
public_subnet_1a_cidr_block = "10.20.16.0/21"
public_subnet_1b_cidr_block = "10.20.24.0/21"
基础结构部分的条件跳过
在特定于env的变量文件中创建配置,然后根据该变量决定创建还是跳过该部分。这样,可以根据需要跳过基础结构的特定部分。
variable vpc_create {
default = "true"
}
module "vpc" {
source = "../modules/aws/network/vpc"
enable = "${var.vpc_create}"
vpc_cidr_block = "${var.vpc_cidr_block}"
name = "${var.vpc_name}"
}
resource "aws_vpc" "vpc" {
count = "${var.enable == "true" ? 1 : 0}"
cidr_block = "${var.vpc_cidr_block}"
enable_dns_support = "true"
enable_dns_hostnames = "true"
}
需要以下命令来初始化和执行每种环境的基础设施更改,将其cd cd到所需的环境文件夹。
terraform init -var-file=dev.variables.tfvar -backend-config=dev.backend.tfvar ../../deployment/
terraform apply -var-file=dev.variables.tfvar ../../deployment
答案 8 :(得分:0)
我不喜欢子文件夹的想法,因为这将导致每个环境的源不同,并且这往往会漂移。
更好的方法是为所有环境使用单个堆栈(让我们说dev,preprod和prod)。要在单个环境上工作,请使用terraform workspace
。
terraform workspace new dev
这将创建一个新的工作区。这包括专用的状态文件和可以在代码中使用的变量terraform.workspace
。
resource "aws_s3_bucket" "bucket" {
bucket = "my-tf-test-bucket-${terraform.workspace}"
}
通过这种方式,您将获得名为的存储桶
(使用terraform workspace select <WORKSPACE>
更改环境)。
要使代码甚至可以进行多区域验证,请执行以下操作:
data "aws_region" "current" {}
resource "aws_s3_bucket" "bucket" {
bucket = "my-tf-test-bucket-${data.aws_region.current.name}-${terraform.workspace}"
}
获得(针对美国东部1地区)
答案 9 :(得分:0)
要遵循的一些Terraform最佳做法:
避免硬编码: 有时,开发人员直接手动创建资源。您需要标记这些资源,并使用terraform导入将它们包括在代码中。 样本:
account_number =“ 123456789012” account_alias =“ mycompany”
从Docker容器运行Terraform: Terraform发布了一个官方的Docker容器,可让您轻松控制可以运行的版本。
在CI / CD管道中设置构建作业时,建议运行Terraform Docker容器。
TERRAFORM_IMAGE=hashicorp/terraform:0.11.7
TERRAFORM_CMD="docker run -ti --rm -w /app -v ${HOME}/.aws:/root/.aws -v ${HOME}/.ssh:/root/.ssh -v `pwd`:/app $TERRAFORM_IMAGE"
有关更多信息,请参阅我的博客:https://medium.com/tech-darwinbox/how-darwinbox-manages-infrastructure-at-scale-with-terraform-371e2c5f04d3
答案 10 :(得分:0)
我想为这个话题做贡献。
在某些特殊情况下,将需要手动访问Terraform状态文件。诸如重构,破坏更改或修复缺陷之类的操作将需要操作人员进行Terraform状态操作。在这种情况下,请使用堡垒主机,VPN等,计划对Terraform状态的非凡控制访问。
检查longer best practices blog,其中涵盖了这一点,包括CI / CD管道指南。
答案 11 :(得分:-1)
使用地形云管理和保存状态,以及上面的建议。