Day 14-for (i=0; i < 100; i++) createVM(i); infrastructure 也可以 for each 之一

infrastructure 也可以 for each 之一

课程内容与代码会放在 Github 上: https://github.com/chechiachang/terraform-30-days

赛後文章会整理放到个人的部落格上 http://chechia.net/

追踪粉专可以收到文章的主动推播

https://ithelp.ithome.com.tw/upload/images/20210901/20120327NvpHVr2QC0.jpg


本章介绍 terraform 的 for_each,以及如何运用 meta-argument 管理 resource

Resource and meta-argument

一般来说,每个 resource block {} 就代表一个 resource。然而在实务中,我们会有额外的需求

  • 需要管理许多大量重复的 resource
  • 需要管理重复的一组 resource

如果全部都写成一个一个独立的 resouce block

  • 会有大量重复的程序码,违反 DRY,且难以管理

因此 terraform 有提供 meta-argument。使用者可以在 resource 以外的地方,使用 meta-argument 改变 terraform evaluate resource block 的行为。

About loop

几乎所有的高级程序语言都有提供 for, each, loop 等语法,来处理不断重复的逻辑。能够高频率的执行重复工作,这对软件产业的效能是至关重要的。

这也是 IaC 管理公有云时,相对於 web console 操作有决定性的优势,例如:

  • 管理 1 台 VM 或是 10 台 VM,还可以使用 web console 手动控制
  • 数量级再提升,例如 100 台 1000 台,没有工具协助是做不到的
  • 人脑很不擅长处理,不断重复却又有一定变化程度的工作,很容易导致
    human error

Terraform 在管理大量类似的云端 resource 是十分有效率的,底下为各位示范。我们可以先用自己熟悉的语言来做个想像

例如 Cloud Computing 最常用到的功能之一,使用 loop 快速 provision VM

  • 同时要建立多个附属 resource,例如取得 public IP
  • 套用同一组防火墙规则,把 VM 加入管理
  • ...

如果是熟悉的语言,大概会是长什麽样子

# sudo code

createFirewallRule()

for i from 0 to 9 {
  createPublicIP($i)
}

for j from 0 to 9 {
  createVM(
    name: $j
    publicIP: $ip
    firewallRule: $firewallRule
    ...
  )
}

Terraform meta-argument

Terraform 提供两个不同用途的

loop expression in Terraform

范例:modules/kubernetes_cluster/node_pool.tf

# modules/kubernetes_cluster/node_pool.tf

resource "azurerm_kubernetes_cluster_node_pool" "main" {
  for_each              = var.node_pools
  name                  = each.value.name
  kubernetes_cluster_id = azurerm_kubernetes_cluster.main.id
  vm_size               = each.value.vm_size
  node_count            = each.value.node_count
  mode                  = each.value.mode
  priority              = each.value.priority
  node_labels           = each.value.node_labels
  node_taints           = each.value.node_taints

  depends_on = [
    azurerm_kubernetes_cluster.main
  ]
}

resource.azurerm_kubernetes_cluster_node_pool.main 中使用两个 meta-arguments

  • depends_on 建立依赖关系,确保 node_pool 会在 azurerm_kubernetes_cluster.main 产生後才会产生
  • for_each 可以依据 var.node_pools,为每个 var.node_pools 的 element 产生一个 resource.azurerm_kubernetes_cluster_node_pool.main

Depends On

首先先看 depends on terraform 官方文件

  • depends_on explicitly 建立依赖关系,确保 node_pool 会在 azurerm_kubernetes_cluster.main 产生後才会产生
  • 影响 provider 对 azure cloud 发出 api 的时机,i.e.
  • 延伸问题;如果不使用 depends_on 的话,plan 或 apply 仍然会成功吗?
    • 在这个范例,没有 depends_on 的话不影响顺序
    • 由於 node_pool 的参数中, kubernetes_cluster_id = azurerm_kubernetes_cluster.main.id 就需要 cluster 的 id,而 id 是 kubernetes_cluster 的参数,产生之後才会取得,terrafrom 会自动推测(automatically infer)两者的 dependency
    • 如果是其他例子,并没有透过参数引用建立关系,terraform 便无法判断,可能造成 apply 失败

提醒注意的地方

  • 虽然 terraform 提供 depends_on meta-argument,然而官方建议使用上应该作为最後手段(last resort)
  • 使用参数
  • depends_on 无法使用 Arbitrary expressions,意思是不能使用可变的数值,而是必须静态的值,举例
    • 如果改成下面这样
      • 希望判断 var.node_pools 内部 element 的长度,如果没有 element 就不用建立 depends_on
      • 这种写法 validate 会直接判断 Invalid Expression,无法 init 或 plan
  depends_on = [
    length(var.node_pools) > 0 ? azurerm_kubernetes_cluster.main : null
  ]

terragrunt init

  There are some problems with the configuration, described below.

The Terraform configuration must be valid before initialization so that
Terraform can determine which modules and providers need to be installed.
╷
│ Error: Invalid expression
│
│   on node_pool.tf line 16, in resource "azurerm_kubernetes_cluster_node_pool" "main":
│   16:     length(var.node_pools) > 0 ? azurerm_kubernetes_cluster.main : null
│
│ A single static variable reference is required: only attribute access and
│ indexing with constant keys. No calculations, function calls, template
│ expressions, etc are allowed here.
╵

ERRO[0003] 1 error occurred:
	* exit status 1

Expression evaluation

为何无法使用,许多 meta-argument 也是必须使用静态的参数:例如 depends_on, provider, lifecycle

  • 使用 provider 作为范例来说明可能比较单纯

  • terraform 首先需要 init,明确指定使用的 provider 版本与 source,或是让 terraform 扫描并推测使用的 provider

  • 没有 provider,便无法 plan 各个 resource,包含内部的参数 validate

  • 读取 state 内容,plan 与 apply 都必须有静态的 provider 存在,如果 provider 使是动态产生,有时存再有时不存在便会影响上述工作

  • terraform 限制 provider 的使用,设定必须是静态的

  • 但在少数的情形,可以产生 provider 後,透过操作移除已经 init 的 provider,这时远端 state 上已经存在,但本地没有 provider 可以读取与 plan,便会产生 orphan state

    • 没有 provider 可以 plan,当然就无法透过 provider 修改或删除,工作被卡住
  • depends_on 概念相同,如果依赖有时为 true,有时为 false 的判断 expression,就无法建立静态的依赖性

  • lifecycle 可以改变 terraform plan, apply 中的行为,也需要静态的 expression 设定

Terraform reference to values


<<:  Day 2.来交朋友吧!-Vue.js是谁?

>>:  基础建设:分散式服务追踪

DAY6: Node 的内部机制(二)

上一篇DAY5: Node 的内部机制(一)主要解释了JavaScript的同步与非同步,相信看完上...

这不是终点

时间过得非常快,在参加 30 天不中断的 iT 邦帮忙铁人赛,在 1/3 路程的时候,就会开始有点挣...

每日挑战,从Javascript面试题目了解一些你可能忽略的概念 - Day23

tags: ItIron2021 Javascript 前言 今天又要继续非同步与promise的爱...

Class

终於来到Class的章节了,Class是ES6所新增,在这之前都是使用Prototype去进行物件导...

#16 No-code 之旅 — Project Setup

嗨嗨~ 今天比较晚下班,还没写文章也还没写专案XD 今天先建立专案好了~ 周末再赶进度! Setup...