LabHub
学习 学习路径 课程

Terraform 实战

count 与 for_each — 用索引还是用键

在 LabHub 中继续学习

一句话总结

countfor_each的区别不是“要制作几个”,而是在每个实例上贴上什么标签。而且那个标签直接粘贴到状态文件中。

概念图: 在每个实例上贴上什么标签 · 删除中间项目时 · 两个再生 · 如果每个实例都可以有彼此区分的名字的话foreach

为什么需要这个?

为了制作三台服务器count = 3使用。运行良好。几个月后,列表变成了变量。count = length(var.servers)可以。到这里为止也没问题。事故发生在删除中间项目时

count制作的实例的地址是[0][1][2]把列表中第二个项目删除的话,第三个会被拉到第二个位置。在工具看到的世界上,“[1]内容发生了变化[2]消失了”。所以计划中出现的是两个再生,而不是试图删除的一台。如果是实际的云,正常运转的服务器会被破坏并重新创建。

for_each用这个问题作为名字解决了这个问题。地址是["web-a"]["web-b"]因为是意义深远的键,所以从列表中删除一个键,就会只消失一个键,其余的什么都不动。

怎么行动

项目 count for_each
接收值 数字 集合(set)或映射(map)
实例地址 res[0] res["kr"]
在里面使用的参考 count.index each.keyeach.value
删除中间项目 向后推后重新生成 仅删除对应键
适合的地方 只有数量才有意义的时候,打开和关闭的开关 每个项目都有身份感的时候

实际操作标准很简单。如果每个实例都可以有彼此区分的名字的话for_each,只要数量有意义的话count.count可以继续使用的代表性位置是功能开关。count = var.enable_debug ? 1 : 0是表达“有或没有”的惯用语,因为只有一个实例,所以没有堆积问题。

把列表for_each不能就这样传递。列表是有顺序的,for_each要求无序的键,所以toset(...)应该换成。这时键就是元素值本身。翻转地图时,键就是地图的键,each.value是那个值——因为这边喜欢在各项目上附加多个属性,所以在实际工作中使用得更多。

for表达式是另一件东西。for_each如果是一个可以制作多个资源的元因子,for是将一个值换成其他形状的表达式。{ for k, v in var.services : k => v.port }像这样用于将地图再次折叠成地图。在整理输出或区域值时几乎必须使用。

在现场相遇的样子

第一,搬家必须moved罗.已经count应用于的资源for_each换成的话,判断工具的旧地址消失了,有了新的地址。也就是说全部破坏,全部重新制作moved用区块"res[0]res["n0"]变成这个了”告诉他的话,只会转移状态地址,不会被破坏。如果计划中出现哪怕一点破坏,那就是再生性正在发生,而不是迁移。

**第二,搬迁前留下现在的地址。**搬迁工作的第一步不是代码修改,而是记录。state list结果和状态的副本必须先作为文件留下来。如果要说迁移结束后没有“遗漏的资源”,必须在移动前将列表和移动后的列表并排放置,确认数量和对应关系,如果没有留下前者,就没有确认的方法。事故通常发生在只有20个中移动了19个时发生,剩下的一个会在下一次apply中悄悄被破坏。

第三,不要计算身高。for_each如果与的键不同的资源的属性是只有在apply时才能确定的值,那么在计划阶段无法知道实例列表,会发生错误。键必须像变量或常数一样是在计划时确定的值

**第四,很难更改键名。**一旦确定的键名实际上成为资源的识别者,以后az-aap-northeast-2a修改也是移居工作。这时答案也是moved所以,第一次决定身高时,最好再考虑一次“这个名字2年后还能用吗”。这样比较划算。

地址变更的话资源就会更换。

countfor_each真正的区别不是语法,而是写在状态上的地址。 如果那个地址变了,Terraform会把它视为其他资源并删除,然后重新创建。

count:    aws_instance.web[0]      aws_instance.web[1]      aws_instance.web[2]
for_each: aws_instance.web["a"]    aws_instance.web["b"]    aws_instance.web["c"]

列表中的b假设删除。count在那里c因为被拉到1号位置 1号和2号都成为更换对象。for_each在那里["b"]就消失了。 如果数据库或磁盘在该列表中,这种差异将区分事故和普通部署。

for_each的钥匙应该在计划时点可以知道。就像其他资源的输出一样 (known after apply)如果用in值作为钥匙的话,“The for_each value depends on resource attributes that cannot be determined until apply" 出现。值稍后 虽然可以确定,但钥匙现在必须确定

# 이름을 열쇠로 삼는다 — 만들기 전에 이미 안다
resource "aws_instance" "web" {
  for_each      = { for s in var.servers : s.name => s }
  instance_type = each.value.type
  subnet_id     = aws_subnet.app[each.value.az].id
}

**已经count搬动用做的东西的时候moved写。**不然的话全部 删除后重新制作。

moved {
  from = aws_instance.web[0]
  to   = aws_instance.web["a"]
}

**count也有可以用的位置。**就像决定是否制作一样,数量 只有0或1人的情况。

resource "aws_nat_gateway" "this" {
  count = var.enable_nat ? 1 : 0
}

此时参考是aws_nat_gateway.this[0].id成为,关闭的话,那个参考会出错。 所以one(aws_nat_gateway.this[*].id)try包起来。

下次实习要做的事情

count制作三个,确认数字索引被固定在状态中,在移动前将地址列表作为文件留下来。然后toset用和地图for_each用两种方式使用。for用表达式制作输出地图,条件性count表示功能开关。最后moved用区块countfor_each证明计划将搬迁到罗,不破坏只更改地址。