Terragrunt如何保持Terraform的CLI 参数 DRY和可维护
使用Terragrunt,可以与Terraform 0.12和HCL2一起使用,帮助您的代码DRY和可维护
保持CLI 参数DRY
CLI标志是Terraform世界中复制/粘贴的另一个常见来源。例如,Terraform的典型模式是在account.tfvars文件中定义公共帐户级变量:
# account.tfvars
account_id = "123456789012"
account_bucket = "my-terraform-bucket"
并在region.tfvars文件中定义公共区域级变量:
# region.tfvars
aws_region = "us-east-2"
foo = "bar"
您可以使用-var-file参数告诉Terraform使用这些变量:
$ terraform apply \
-var-file=../../common.tfvars \
-var-file=../region.tfvars
每次都要记住这些-var-file参数可能会很乏味,而且容易出错。Terragrunt允许您通过将CLI参数定义为Terragrunt.hcl配置中的代码来保持CLI参数的干燥:
# terragrunt.hcl
terraform {
extra_arguments "common_vars" {
commands = ["plan", "apply"]
arguments = [
"-var-file=../../common.tfvars",
"-var-file=../region.tfvars"
]
}
}
现在,当您运行plan或apply命令时,Terragrunt将自动添加这些参数:
$ terragrunt apply
Running command: terraform with arguments [apply -var-file=../../common.tfvars -var-file=../region.tfvars]
您甚至可以使用get_terraform_commands_that_need_vars()内置函数自动获取接受-var-file和-var参数的所有命令列表:
# terragrunt.hcl
terraform {
extra_arguments "common_vars" {
commands = get_terraform_commands_that_need_vars()
arguments = [
"-var-file=../../common.tfvars",
"-var-file=../region.tfvars"
]
}
}
促进跨环境的不可变、版本化的Terraform模块
我们从编写数十万行基础结构代码中学到的最重要的教训之一是,大型模块应该被认为是有害的。也就是说,在一个Terraform模块中定义所有环境(dev、stage、prod等)甚至大量基础设施(服务器、数据库、负载均衡器、DNS等)都不是一个好主意。大型模块速度慢、不安全、很难更新、很难代码检查、很难测试,而且很脆弱(也就是说,你把所有的鸡蛋放在一个篮子里)。
因此,您通常希望将基础结构划分为多个模块:
├── prod
│ ├── app
│ │ ├── main.tf
│ │ └── outputs.tf
│ ├── mysql
│ │ ├── main.tf
│ │ └── outputs.tf
│ └── vpc
│ ├── main.tf
│ └── outputs.tf
├── qa
│ ├── app
│ │ ├── main.tf
│ │ └── outputs.tf
│ ├── mysql
│ │ ├── main.tf
│ │ └── outputs.tf
│ └── vpc
│ ├── main.tf
│ └── outputs.tf
└── stage
├── app
│ ├── main.tf
│ └── outputs.tf
├── mysql
│ ├── main.tf
│ └── outputs.tf
└── vpc
├── main.tf
└── outputs.tf
上面的文件夹结构显示了如何为每个环境(prod、qa、stage)和每种类型的基础设施(应用程序、数据库、VPC)分离代码。然而,缺点是它并不干燥。tf文件将包含大量重复。您可以通过在可重用的Terraform模块中定义所有基础结构来减少这一点,但即使是实例化模块的代码–包括配置提供者、后端、模块的输入变量和输出变量–也意味着您最终仍会为每个环境中的每个模块进行数十行或数百行复制/粘贴:
# prod/app/main.tf
provider "aws" {
region = "us-east-1"
# ... other provider settings ...
}
terraform {
backend "s3" {}
}
module "app" {
source = "../../../app"
instance_type = "m4.large"
instance_count = 10
# ... other app settings ...
}
# prod/app/outputs.tf
output "url" {
value = module.app.url
}
# ... and so on!
Terragrunt允许您定义一次Terraform代码,并从一个环境到另一个环境,提升完全相同代码的版本化、不可变的“工件”。以下是如何进行的快速概述。
首先,创建一个名为infrastructure-modules的Git repo,其中包含您的Terraform代码(.tf文件)。这与您刚才看到的Terraform代码完全相同,不同环境之间的任何变量都应该作为输入变量公开:
# infrastructure-modules/app/main.tf
provider "aws" {
region = "us-east-1"
# ... other provider settings ...
}
terraform {
backend "s3" {}
}
module "app" {
source = "../../../app"
instance_type = var.instance_type
instance_count = var.instance_count
# ... other app settings ...
}
# infrastructure-modules/app/outputs.tf
output "url" {
value = module.app.url
}
# infrastructure-modules/app/variables.tf
variable "instance_type" {}
variable "instance_count" {}
一旦这就位,您就可以通过创建一个Git标记来发布这个模块的新版本:
$ git tag -a "v0.0.1" -m "First release of app module"
$ git push --follow-tags
现在,在另一个名为infrastructure-live的Git repo中,您为所有环境创建了以前相同的文件夹结构,但是每个模块没有大量复制/粘贴的.tf文件,而是只有一个Terragrunt.hcl文件:
# infrastructure-live├── prod
│ ├── app
│ │ └── terragrunt.hcl
│ ├── mysql
│ │ └── terragrunt.hcl
│ └── vpc
│ └── terragrunt.hcl
├── qa
│ ├── app
│ │ └── terragrunt.hcl
│ ├── mysql
│ │ └── terragrunt.hcl
│ └── vpc
│ └── terragrunt.hcl
└── stage
├── app
│ └── terragrunt.hcl
├── mysql
│ └── terragrunt.hcl
└── vpc
└── terragrunt.hcl
每个Terragrunt.hcl文件的内容如下所示:
# infrastructure-live/prod/app/terragrunt.hclterraform {
source =
"github.com:foo/infrastructure-modules.git//app?ref=v0.0.1"
}inputs = {
instance_count = 10
instance_type = "m4.large"
}
上面的Terragrunt.hcl文件将source参数设置为指向刚刚在基础设施模块repo中创建的app模块,使用ref参数指定该repo的V0.0.1版本。它还在inputs={…}中为prod环境配置该模块的变量街区。
stage环境中的TerrAgrunt.hcl文件看起来类似,但它将在inputs={…}中配置更小/更少的实例阻止省钱:
# infrastructure-live/stage/app/terragrunt.hclterraform {
source =
"github.com:foo/infrastructure-modules.git//app?ref=v0.0.1"
}inputs = {
instance_count = 3
instance_type = "t2.micro"
}
当您运行terragrunt apply时,terragrunt会将您的应用程序模块下载到一个临时文件夹中,在该文件夹中运行terraform apply,将您在inputs={…}中指定的输入变量传递给模块块:
$ terragrunt applyDownloading Terraform configurations from github.com:foo/infrastructure-modules.git...Running command: terraform with arguments [apply]...
这样,每个环境中的每个模块都由单个TerrAgrunt.hcl文件定义,该文件只指定要部署的Terraform模块和特定于该环境的输入变量。这已经是你能得到的最干燥的了!
而且,您可以指定在每个环境中部署的模块的不同版本!例如,在对基础设施模块repo中的app模块进行一些更改后,您可以创建一个V0.0.2标记,并只更新qa环境来运行这个新版本:
# infrastructure-live/qa/app/terragrunt.hclterraform {
source =
"github.com:foo/infrastructure-modules.git//app?ref=v0.0.2"
}inputs = {
instance_count = 3
instance_type = "t2.micro"
}
如果它在qa环境中运行良好,您可以通过更新TerrAgrunt.hcl文件来将完全相同的代码提升到stage环境中以运行V0.0.2。最后,如果该代码在stage中运行良好,您可以通过更新TerrAgrunt.hcl文件使其也使用V0.0.2来再次将完全相同的代码升级为prod。
如果在任何时候遇到问题,它只会影响一个环境,您可以通过部署以前的版本号来回滚。这是不可变的基础结构在起作用!
结论
相信Terragrunt目前提供了实现干燥、可维护、不可变的Terraform代码的最佳方式。我们希望,随着时间的推移,所有的Terragrunt功能将被滚动到Terraform中,但与此同时,给Terragrunt一个机会作为一个止损解决方案,并让我们知道它是如何工作的!
- 点赞
- 收藏
- 关注作者
评论(0)