背景
在上文中介绍了Terraform的功能,以及如何使用terrform workspace管理多个部署环境。本文会继续聊聊Terraform其他一些用法,介绍下如何结合 Terraform 语法中Dynamic block, 允许我们可以在不同Workspace中使用同个Terraform Module 动态加载block。
需求
在上个案例中,公司使用Terraform在多个环境(INT,Staging, Prod)上部署Elastic Cloud,用作系统及客户日志存储分析等 。我们创建了可复用模块 ec_deployment
module来部署我们的EC集群,代码中已经定义了ES Hot节点、WARM节点等各个配置。现在,假设我们只想在Prod环境中启用 Dedicated Master 以及Cold&Frozen节点,来提高ES性能以及实现日志的Long Storage retention 。 如果我们在ec_deployment
模块中直接加入 topology块的话,会影响到DEV环境 也创建相应的节点,而使其无法再使用该模块文件。那应该如何做呢?
方案
-
方案一: 创建新的
ec_dev_deployment
模块以及ec_prod_deployment
模块来区分不同环境的部署文件(代码冗余,不建议) -
方案二:Dynamic block 动态加载
根据terrform文档,提及dynamic block 的行为与for表达式非常相似,而并没有说明此外我们还可以在 该block之内实现一个条件逻辑,使得不同workspace根据变量来改变要部署的topology,我们经过实验发现这却是可行的。
实现
还是先来看看Terraform folder 下ec_deployment
module
`-- modules
|-- ec_deployment
| |-- README.md
| |-- data.tf
| |-- locals.tf
| |-- main.tf
| |-- outputs.tf
| |-- provider.tf
| `-- variables.tf
其中main.tf 中实现ElastcCloud Topology 的相关代码如下:
elasticsearch {
autoscale = "false"
topology {
id = "hot_content"
zone_count = var.elasticsearch_hot_zones
size = var.elasticsearch_hot_size
size_resource = "memory"
}
dynamic "topology" {
for_each = var.elasticsearch_master_zones > 0 ? [1] : []
content {
id = "master"
zone_count = var.elasticsearch_master_zones
size = var.elasticsearch_master_size
size_resource = "memory"
}
}
dynamic "topology" {
for_each = var.elasticsearch_warm_zones > 0 ? [1] : [])
content {
id = "warm"
zone_count = var.elasticsearch_warm_zones
size = var.elasticsearch_warm_size
size_resource = "memory"
}
}
}
与前文不同的是,你会发现在这里我们加入了 dynamic "topology"
语法块,来实现Warm,Master节点部分的代码。其中:
- for_each 指令用于条件逻辑(节点区域大于零则启用该bolck)
- var 定义了一个变量 (var.elasticsearch_warm_zones)
接着在envs 目录中, 定制各个ElasticCloud Terraform code具体变量值
| |-- envs
| | |-- production
| | | |-- ec_deployment
| | | | `-- infra
| | | | |-- locals.tf
| | | | |-- main.tf
| | | | |-- outputs.tf
| | | | `-- provider.tf
| | |-- staging
| | | |-- ec_deployment
| | | | `-- infra
| | | | |-- locals.tf
| | | | |-- main.tf
| | | | |-- outputs.tf
| | | | `-- provider.tf
| | |-- int
| | | |-- ec_deployment
| | | | `-- infra
| | | | |-- locals.tf
| | | | |-- main.tf
| | | | |-- outputs.tf
| | | | `-- provider.tf
如INT下 main.tf
module "ec_deployment" {
source = "../../../../modules/ec_deployment/"
deployment_name = "aws-integration-infra"
deployment_region = "us-east-1"
deployment_stack_version = "8.4.1"
deployment_template_id = "aws-storage-optimized-v3"
elasticsearch_hot_size = "15g"
elasticsearch_hot_zones = 2
elasticsearch_warm_size = "4g"
elasticsearch_warm_zones = 0
elasticsearch_cold_size = "2g"
elasticsearch_cold_zones = 0
elasticsearch_frozen_size = "4g"
elasticsearch_frozen_zones = 0
elasticsearch_master_size = "1g"
elasticsearch_master_zones = 0
PROD下 main.tf
module "ec_deployment" {
source = "../../../../modules/ec_deployment/"
deployment_ready = var.deployment_ready
deployment_name = local.deployment_name
deployment_alias = local.deployment_alias_name
deployment_region = local.deployment_region
deployment_stack_version = local.deployment_stack_version
deployment_template_id = "aws-storage-optimized-v3"
elasticsearch_hot_size = "15g"
elasticsearch_hot_zones = 4
elasticsearch_warm_size = "4g"
elasticsearch_warm_zones = 2
elasticsearch_cold_size = "2g"
elasticsearch_cold_zones = 2
elasticsearch_frozen_size = "4g"
elasticsearch_frozen_zones = 1
elasticsearch_master_size = "1g"
elasticsearch_master_zones = 3
可以看到,在Prod里我们分别为warm&cold指定了节点区域数量,这样在
Terraform Plan之后,会发现INT的资源state情况没有变化(节点区域数为零),而Prod将会增加这些节点的部署
效果
Terraform Apply 之后可以看到不同环境下 Elastic Cloud集群的差异
INT环境下的 ES 节点状态会和之前一样,而Prod下创建了这些新的节点
类似的用法,当我在给ES index创建ILM(index lifecycle magement)Policy 时也会用到(一些Index需要rollover, 而另一些则不需要)
例如,在ec_provisioning
模块中可以有如下定义
resource "elasticstack_elasticsearch_index_lifecycle" "main" {
for_each = var.indices
name = each.key
dynamic "warm" {
for_each = toset(each.value.ilm_warm_enabled ? ["enable"] : [])
content {
min_age = each.value.ilm_warm_min_age
forcemerge {
max_num_segments = 1
}
}
}
delete {
min_age = each.value.ilm_delete_min_age
delete {
delete_searchable_snapshot = true
}
}
}
在ENV下,定义各个index 的具体template setting 以及ILM policy
{
log-access = {
number_of_shards = 2
ilm_delete_min_age = "14d"
},
log-syslog = {
ilm_delete_min_age = "360d"
ilm_warm_min_age = "90d"
ilm_warm_enabled = true
}
由于 log-syslog
启用了 ilm_warm_enabled, 因此该index 在90天后日志数据会从hot节点rollover到warm节点
结论
总之,在编写可重用模块时,或者在跨多个环境使用同一个模块并希望区分其中的某些配置时,动态块会非常有用。
值得注意的是, Hashicorp建议不要过度使用动态块,因为这会使配置难以读取和维护。
参考链接:
https://developer.hashicorp.com/terraform/language/expressions/dynamic-blocks
https://medium.com/geekculture/terraform-how-to-use-dynamic-blocks-when-conditionally-deploying-to-multiple-environments-57e63c0a2b56
原文关注公众号:“云原生SRE”