count
概述
默认情况下,一个资源块配置一个真实的基础设施对象(类似地,一个模块(Module)块将子模块的内容包含到配置中一次)。然而,有时您想要管理几个类似的对象(如固定的计算实例池),而无需为每个对象编写单独的块。Terraform 提供了两种方法可以实现这一点:count 和 for_each。
count
是 Terraform 中的一个元参数,允许您声明一个资源或模块(Module)的多个实例。如果资源或Module块包含一个值为整数的 count 参数,Terraform 将创建该数量的实例。它的基本作用是根据指定的数字值创建相应数量的资源副本,每个副本都是独立的基础设施对象。这是实现资源批量创建的最简单方式,特别适合于创建结构相似的资源集合,如服务器集群、多个相似存储桶或多个网络组件。
本文档将详细介绍 count
元参数的语法、用法,并提供在阿里云环境中的实际应用示例,帮助您理解如何有效地利用这一特性来简化基础设施管理。
注意:一个资源或Module块不能同时使用 count 和 for_each。
基本语法
count 是由 Terraform 语言定义的元参数。它可以与模块和所有资源类型一起使用。
count 元参数接受一个整数,并创建相应数量的资源或模块实例。每个实例都有一个与之关联的独特基础设施对象,并且在应用配置时,每个对象都会被单独创建、更新或销毁。
基本用法
resource "alicloud_instance" "server" {
count = 4 # 创建四个类似的 ECS 实例
instance_name = "server-${count.index}"
instance_type = "ecs.s6-c1m2.small"
image_id = "aliyun_2_1903_x64_20G_alibase_20230523.vhd"
vswitch_id = "vsw-abc123456"
security_groups = ["sg-abc123456"]
tags = {
Name = "Server ${count.index}"
}
}
count 对象
在设置了 count 的块中,表达式中可以使用额外的 count 对象,因此您可以修改每个实例的配置。此对象有一个属性:
count.index
— 对应于此实例的不同索引号(从 0 开始)。
在 count 中使用表达式
count 元参数接受数字表达式。然而,与大多数参数不同,count 值必须在 Terraform 执行任何远程资源操作之前已知。这意味着 count 不能引用任何在apply之前未知的资源属性(例如,创建对象时由远程 API 生成的唯一 ID)。
引用实例
当设置了 count 时,Terraform 区分块本身和与之关联的多个资源或模块实例。实例由索引号标识,从 0 开始。
<TYPE>.<NAME>
或module.<NAME>
(例如,alicloud_instance.server
)引用资源块。<TYPE>.<NAME>[<INDEX>]
或module.<NAME>[<INDEX>]
(例如,alicloud_instance.server[0]
,alicloud_instance.server[1]
等)引用单个实例。
这与没有 count 或 for_each 的资源和模块不同,后者可以在没有索引或键的情况下被引用。
同样,来自具有多个实例的子模块的资源在计划输出和 UI 的其他地方显示时,会以 module.<NAME>[<KEY>]
为前缀。对于没有 count 或 for_each 的模块,地址不会包含模块索引,因为模块的名称足以引用模块。
注意:在嵌套的 provisioner 或 connection 块中,特殊的 self 对象引用当前资源实例,而不是整个资源块。
使用示例
创建多个 ECS 实例
resource "alicloud_instance" "web_servers" {
count = 3
instance_name = "web-server-${count.index + 1}"
instance_type = "ecs.s6-c1m2.small"
image_id = "aliyun_2_1903_x64_20G_alibase_20230523.vhd"
vswitch_id = "vsw-abc123456"
security_groups = ["sg-abc123456"]
system_disk_category = "cloud_efficiency"
system_disk_size = 40
tags = {
Name = "WebServer-${count.index + 1}"
Role = "web"
Stage = "production"
}
}
创建多个 OSS 存储桶
variable "bucket_names" {
description = "List of bucket name prefixes"
type = list(string)
default = ["logs", "backups", "configs"]
}
resource "alicloud_oss_bucket" "buckets" {
count = length(var.bucket_names)
bucket = "${var.bucket_names[count.index]}-${random_id.suffix.hex}"
acl = "private"
tags = {
Name = var.bucket_names[count.index]
Environment = "Production"
}
}
resource "random_id" "suffix" {
byte_length = 4
}
条件式创建资源
您还可以使用 count 有条件地创建资源:
variable "create_slb" {
description = "Whether to create SLB"
type = bool
default = true
}
resource "alicloud_slb_load_balancer" "app" {
# 只有当 create_slb 为 true 时创建
count = var.create_slb ? 1 : 0
load_balancer_name = "app-lb"
address_type = "internet"
load_balancer_spec = "slb.s2.small"
tags = {
Name = "ApplicationLB"
}
}
在模块中使用 count
variable "enable_high_availability" {
description = "Whether to deploy in high availability mode with multiple zones"
type = bool
default = false
}
module "primary_zone" {
source = "./modules/zone_deployment"
zone_id = "cn-hangzhou-h"
# ... 其他配置
}
module "secondary_zone" {
source = "./modules/zone_deployment"
# 只有当启用高可用性时才部署第二个可用区
count = var.enable_high_availability ? 1 : 0
zone_id = "cn-hangzhou-i"
# ... 其他配置
}
如何选择 for_each 和 count
如果您的实例几乎相同,count 是合适的。如果它们的某些参数需要无法直接从整数派生的不同值,使用 for_each 会更安全。
在 for_each 可用之前,通常从列表的长度派生 count 并使用 count.index 查找原始列表值:
variable "vswitch_ids" {
type = list(string)
}
resource "alicloud_instance" "server" {
# 为每个交换机创建一个实例
count = length(var.vswitch_ids)
instance_name = "server-${count.index + 1}"
instance_type = "ecs.s6-c1m2.small"
image_id = "aliyun_2_1903_x64_20G_alibase_20230523.vhd"
vswitch_id = var.vswitch_ids[count.index]
tags = {
Name = "Server ${count.index + 1}"
}
}
这种方法是脆弱的,因为资源实例仍然由它们的索引而不是列表中的字符串值标识。如果从列表中间删除了一个元素,该元素之后的每个实例都会看到其 vswitch_id 值发生变化,导致比预期更多的远程对象更改。使用 for_each 可以提供相同的灵活性而不会带来额外的变更。
注:本文参考自 Terraform 官方文档 count,可前往了解更多细节。