透過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的日誌:
# tencentcloud_cbs_storage_attachment.attachment[0] must be replaced
-/+ resource "tencentcloud_cbs_storage_attachment" "attachment" {
~ id = "disk-mcklmp5z" -> (known after apply)
~ storage_id = "disk-mcklmp5z" -> "disk-rspjpenh" # forces replacement
# (1 unchanged attribute hidden)
}
# tencentcloud_cbs_storage_attachment.attachment[1] must be replaced
-/+ resource "tencentcloud_cbs_storage_attachment" "attachment" {
~ id = "disk-rspjpenh" -> (known after apply)
~ storage_id = "disk-rspjpenh" -> "disk-k9c5lg1v" # forces replacement
# (1 unchanged attribute hidden)
}
# tencentcloud_cbs_storage_attachment.attachment[2] must be replaced
-/+ resource "tencentcloud_cbs_storage_attachment" "attachment" {
~ id = "disk-k9c5lg1v" -> (known after apply)
~ storage_id = "disk-k9c5lg1v" -> "disk-jl5g1u7f" # forces replacement
# (1 unchanged attribute hidden)
}
# tencentcloud_cbs_storage_attachment.attachment[3] must be replaced
-/+ resource "tencentcloud_cbs_storage_attachment" "attachment" {
~ id = "disk-jl5g1u7f" -> (known after apply)
~ storage_id = "disk-jl5g1u7f" -> "disk-mytvnnif" # forces replacement
# (1 unchanged attribute hidden)
}
發現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:
https://discuss.hashicorp.com/t/the-for-each-value-depends-on-resource-attributes-that-cannot-be-determined-until-apply/25016
簡而言之,就是foreach要求他的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
}
圓滿解決,新建立的例項用上新的模板,但是存量的例項無法調整還是得忍受下盤重新掛載的問題。