通过Terraform创建云主机时如何处理多个云盘挂载的问题

在某些业务场景下,通过Terraform创建云主机时,一个机器可能需要挂载多个云盘。一般来说,云厂商会单独创建云主机和云硬盘,然后通过attachment的资源去挂载。因此,我们的模板大致如下:

resource "tencentcloud_instance" "basic" {
  instance_name     = var.instance_name
  password = "xxx"
}

resource "tencentcloud_cbs_storage" "storage" {
  for_each          = var.data_disks
  storage_name      = each.key
  storage_type      = each.value.disk_type
  storage_Size      = each.value.size
}

resource "tencentcloud_cbs_storage_attachment" "attachment" {
  count       = length(tencentcloud_cbs_storage.storage)
  storage_id  = element(values(tencentcloud_cbs_storage.storage)[*].id, count.index)
  instance_id = tencentcloud_instance.basic.id
}


variable "data_disks" {
  type = map(object({
    disk_type = string
    size      = number
  }))
  description = "Instance Data Disks"
  default     = {}
}

这个模板已经使用了很长时间,完全满足多盘的需求,并且具有一定的灵活性。然而,随着全方位降本的需求以及服务优化等措施,业务方评估可以考虑减少云盘数量。由于机型的特殊性,机器也不能回收重新创建。

由于之前一直没有减盘的场景,因此一直没有关注到减盘的问题。直到最近业务方评估需要减盘,我们发现在减盘时盘的attachment会销毁并重新创建,腾讯云这个资源的操作会伴随unmount动作,导致减盘之后盘没有被挂载上。

这个现象超出了我的预期。分析Terraform的日志后,发现attachment的索引是index,减盘时索引会重新计算,导致attachment资源被销毁并重建,从而导致云盘被卸载。

原因明确了,接下来就可以解决了。我们可以使用for_each来解决这个问题,如下所示:

resource "tencentcloud_cbs_storage_attachment" "attachment" {
  for_each = toset(values(tencentcloud_cbs_storage.storage)[*].id)
  storage_id  = each.key
  instance_id = tencentcloud_instance.foo.id
}

然而,事情往往不那么顺利:

│ Error: Invalid for_each argument
│
│   on main.tf line 61, in resource "tencentcloud_cbs_storage_attachment" "attachment":
│   61:   for_each = toset(values(tencentcloud_cbs_storage.storage)[*].id)
│     ├────────────────
│     │ tencentcloud_cbs_storage.storage is object with 6 attributes
│
│ The "for_each" value depends on resource attributes that cannot be
│ determined until apply, so Terraform cannot predict how many instances will
│ be created. To work around this, use the -target argument to first apply
│ only the resources that the for_each depends on.

在Terraform论坛上发现了一个issue,简而言之,就是for_each要求他的map key必须是已知明确的值,不能是依赖其他资源的值,因此会出现上述错误。了解到这个限制后,我们调整了模板:

resource "tencentcloud_cbs_storage_attachment" "attachment" {
  for_each = var.data_disks
  storage_id  = tencentcloud_cbs_storage.storage[each.key].id
  instance_id = tencentcloud_instance.basic.id
}

终于圆满解决了问题,新创建的实例可以使用新的模板。但是存量的实例无法调整,因此仍然需要忍受盘重新挂载的问题。

热门手游下载