我一直在使用Terraform来构建我的AWS栈,并且一直很喜欢它。如果要在商业环境中使用,则需要针对不同环境(例如QA,STAGING,PROD)重用配置。
我怎样才能做到这一点?我是否需要创建一个包装器脚本来调用terraform的cli,同时按照下面的环境传入不同的状态文件?我想知道Terraform是否提供了更原生的解决方案。
terraform apply -state=qa.tfstate
答案 0 :(得分:17)
我建议你看看hashicorp best-practices repo,它有一个非常好的设置来处理不同的环境(类似于James Woolfenden所建议的)。
我们正在使用类似的设置,它运行得非常好。但是,这个最佳实践回购假设你使用的是Atlas,我们不是。我们创建了一个非常精细的Rakefile
,它基本上(再次通过最佳实践的repo)获取/terraform/providers/aws
的所有子文件夹,并使用命名空间将它们公开为不同的构建。因此,我们的rake -T
输出将列出以下任务:
us_east_1_prod:init
us_east_1_prod:plan
us_east_1_prod:apply
us_east_1_staging:init
us_east_1_staging:plan
us_east_1_staging:apply
这种分离可以防止dev可能排除的变化意外地影响(或更糟,破坏)prod中的某些东西,因为它是一个不同的状态文件。它还允许在实际应用于prod之前测试dev / staging的变化。
另外,我最近偶然发现了这个小小的写作,它基本上显示了如果你把所有东西放在一起会发生什么: https://charity.wtf/2016/03/30/terraform-vpc-and-why-you-want-a-tfstate-file-per-env/
答案 1 :(得分:13)
Paul的模块解决方案是正确的想法。但是,我强烈建议反对在同一个Terraform文件中定义所有环境(例如QA,登台,制作)。如果你这样做,那么每当你改变分期时,你都会冒着意外破坏生产的风险,这部分地阻碍了首先将这些环境隔离开来!有关可能出现问题的详细讨论,请参阅Terraform, VPC, and why you want a tfstate file per env。
我总是建议将每个环境的Terraform代码存储在一个单独的文件夹中。实际上,您甚至可能希望将每个“组件”(例如数据库,VPC,单个应用程序)的Terraform代码存储在单独的文件夹中。同样,原因是隔离:当对单个应用程序进行更改时(您可能每天执行10次),您不希望将整个VPC置于风险之中(您可能永远不会更改)。
因此,我的典型文件布局如下所示:
stage
└ vpc
└ main.tf
└ vars.tf
└ outputs.tf
└ app
└ db
prod
└ vpc
└ app
└ db
global
└ s3
└ iam
登台环境的所有Terraform代码都进入stage
文件夹,prod环境的所有代码都进入prod
文件夹,以及生活在环境之外的所有代码(例如IAM用户,S3存储桶)进入global
文件夹。
有关详细信息,请查看How to manage Terraform state。要深入了解Terraform最佳做法,请查看书籍 Terraform: Up & Running 。
答案 2 :(得分:13)
请注意,从版本0.10.0开始,Terraform支持Workspaces的概念(0.9.x中的环境)。
工作空间是Terraform状态的命名容器。通过多个工作区,Terraform配置的单个目录可用于管理多个不同的基础架构资源集。
在此处查看更多信息:https://www.terraform.io/docs/state/workspaces.html
答案 3 :(得分:6)
在扩展您的terraform使用时,您需要共享状态(开发人员,构建过程和不同项目之间),支持多个环境和区域。 为此,您需要使用远程状态。 在执行terraform之前,您需要设置状态。 (我使用powershell)
$environment="devtestexample"
$region ="eu-west-1"
$remote_state_bucket = "${environment}-terraform-state"
$bucket_key = "yoursharedobject.$region.tfstate"
aws s3 ls "s3://$remote_state_bucket"|out-null
if ($lastexitcode)
{
aws s3 mb "s3://$remote_state_bucket"
}
terraform remote config -backend S3 -backend-config="bucket=$remote_state_bucket" -backend-config="key=$bucket_key" -backend-config="region=$region"
#(see here: https://www.terraform.io/docs/commands/remote-config.html)
terraform apply -var='environment=$environment' -var='region=$region'
您的状态现在存储在S3中,按地区,按环境存储,然后您可以在其他tf项目中访问此状态。
答案 4 :(得分:2)
无需制作包装脚本。我们所做的是将env分成一个模块然后有一个顶级terraform文件,我们只为每个环境导入该模块。只要您将模块设置为采用足够的变量(通常是env_name和其他一些变量),您就会很好。作为一个例子
# project/main.tf
module "dev" {
source "./env"
env = "dev"
aws_ssh_keyname = "dev_ssh"
}
module "stage" {
source "./env"
env = "stage"
aws_ssh_keyname = "stage_ssh"
}
# Then in project/env/main.tf
# All the resources would be defined in here
# along with variables for env and aws_ssh_keyname, etc.
答案 5 :(得分:1)
Form Terraform版本0.10+有一种使用Workspace命令维护状态文件的方法
$ terraform workspace list // The command will list all existing workspaces
$ terraform workspace new <workspace_name> // The command will create a workspace
$ terraform workspace select <workspace_name> // The command select a workspace
$ terraform workspace delete <workspace_name> // The command delete a workspace
您需要做的第一件事就是为您的环境创建每个工作区
$ terraform workspace new dev
已创建并切换到工作区“ dev”!
您现在处于一个新的空白工作区。工作区隔离其状态, 因此,如果您运行“ terraform plan”,Terraform将不会看到任何现有状态 为此配置。
$terraform workspace new test
已创建并切换到工作区“测试”!
您现在处于一个新的空白工作区。工作区隔离其状态, 因此,如果您运行“ terraform plan”,Terraform将不会看到任何现有状态 为此配置。
$terraform workspace new stage
已创建并切换到工作区“舞台”!
您现在处于一个新的空白工作区。工作区隔离其状态, 因此,如果您运行“ terraform plan”,Terraform将不会看到任何现有状态 为此配置。
将创建后端terraform.tfstate.d目录
在它们下面您可以看到3个目录-dev,test,stage,每个目录都将在其工作区下维护其状态文件。
现在您要做的就是将环境变量文件移动到另一个文件夹中
每次执行terraform计划时仅保留一个变量文件,适用terraform
main.tf
dev_variable.tfvar
output.tf
记住要切换到正确的工作空间以使用正确的环境状态文件
$terraform workspace select test
main.tf
test_variable.tfvar
output.tf
Ref:https://dzone.com/articles/manage-multiple-environments-with-terraform-worksp
答案 6 :(得分:1)
绝对不需要为开发和生产环境设置单独的代码库。最佳实践规定(我的意思是 DRY)实际上您最好拥有一个代码库并简单地对其进行参数化,就像您在开发软件时通常所做的那样 - 您没有单独的开发文件夹应用程序的版本和应用程序的生产版本。您只需要确保正确的部署方案。地形也是如此。考虑这个“Hello world”的想法:
terraform-project
├── etc
│ ├── backend
│ │ ├── dev.conf
│ │ └── prod.conf
│ └── tfvars
│ ├── dev.tfvars
│ └── prod.tfvars
└── src
└── main.tf
etc/backend/dev.conf 的内容
storage_account_name = "tfremotestates"
container_name = "tf-state.dev"
key = "terraform.tfstate"
access_key = "****"
etc/backend/prod.conf 的内容
storage_account_name = "tfremotestates"
container_name = "tf-state.prod"
key = "terraform.tfstate"
access_key = "****"
etc/tfvars/dev.tfvars 的内容
environment = "dev"
etc/tfvars/prod.tfvars 的内容
environment = "prod"
src/main.tf 的内容
terraform {
backend "azurerm" {
}
}
provider "azurerm" {
version = "~> 2.56.0"
features {}
}
resource "azurerm_resource_group" "rg" {
name = "rg-${var.environment}"
location = "us-east"
}
现在您只需要将适当的值传递给 cli 调用,例如:
export ENVIRONMENT=dev
terraform init -backend-config=etc/backends/${ENVIRONMENT}.conf
terraform apply -vars-file=etc/tfvars/${ENVIRONMENT}.tfvars
这样:
当然,为了完全安全,您应该结合某种 git 流程和代码审查,也许是一些静态或集成测试、自动部署过程等。但我认为这个解决方案是最好的方法拥有多个 terraform 环境而无需重复代码,几年来它对我们来说非常有效。
答案 7 :(得分:0)
此主题中有很多不错的答案。让我也为对我和其他团队有用的想法做出贡献。
这个想法是有一个包含整个基础结构代码的“ umbrella”项目。
每个环境的terraform文件仅包含一个模块-“主”模块。
然后“主要”将包括资源和其他模块
- terraform_project
- env
- dev01 <-- Terraform home, run from here
- .terraform <-- git ignored of course
- dev01.tf <-- backend, env config, includes always _only_ the main module
- dev02
- .terraform
- dev02.tf
- stg01
- .terraform
- stg01.tf
- prd01
- .terraform
- prd01.tf
- main <-- main umbrella module
- main.tf
- variables.tf
- modules <-- submodules of the main module
- module_a
- module_b
- module_c
示例环境主文件(例如dev01.tf)将如下所示
provider "azurerm" {
version = "~>1.42.0"
}
terraform {
backend "azurerm" {
resource_group_name = "tradelens-host-rg"
storage_account_name = "stterraformstate001"
container_name = "terraformstate"
key = "dev.terraform.terraformstate"
}
}
module "main" {
source = "../../main"
subscription_id = "000000000-0000-0000-0000-00000000000"
project_name = "tlens"
environment_name = "dev"
resource_group_name = "tradelens-main-dev"
tenant_id = "790fd69f-41a3-4b51-8a42-685767c7d8zz"
location = "West Europe"
developers_object_id = "58968a05-dc52-4b69-a7df-ff99f01e12zz"
terraform_sp_app_id = "8afb2166-9168-4919-ba27-6f3f9dfad3ff"
kubernetes_version = "1.14.8"
kuberenetes_vm_size = "Standard_B2ms"
kuberenetes_nodes_count = 4
enable_ddos_protection = false
enable_waf = false
}
感谢您: