Terraform 之使用 Dynamic block 实现模块动态加载

Conditional Dynamic block in Terraform

在这里插入图片描述

背景

在上文中介绍了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”

猜你喜欢

转载自blog.csdn.net/dongshi_89757/article/details/127887401