凌晨两点,PagerDuty又响了。生产环境崩了,原因是某个变量传了字符串"true"而不是布尔值true。这种低级错误本不该发生——如果你用了类型约束。
Terraform变量不是简单的传值容器。加上类型约束,它就变成一份契约,明确告诉调用者:这里要什么、不要什么。今天把AWS Terraform的类型系统彻底理了一遍,发现很多踩过的坑其实早有解法。
基础类型:三道保险
最常用的是string、number、bool。看起来简单,但很多人栽在隐式转换上。
number同时支持整数和浮点,给count参数用的时候特别省心。string必须用双引号,支持空格,放区域名称或资源ID很合适。bool就是true/false,做功能开关时比字符串"enabled"安全一百倍。
代码示例:
variable "instance_count" { type = number default = 1 }
variable "region" { type = string default = "us-east-1" }
variable "monitoring_enabled" { type = bool default = true }
集合类型:三种容器,三种脾气
list是有序的,元素类型必须一致。可用区列表用它很合适,因为顺序有意义,而且你能用索引取第几个。
set和list长得像,但有两个致命区别:元素必须唯一,顺序不保证。安全组规则常用它,因为端口22写两遍没意义,而且AWS本来也不管你先写80还是443。
map是键值对,标签系统全靠它。Environment = "Dev"这种结构,比硬编码字符串好维护得多。
variable "availability_zones" { type = list(string) default = ["us-east-1a", "us-east-1b"] }
variable "allowed_ports" { type = set(number) default = [22, 80, 443] }
variable "tags" { type = map(string) default = { Environment = "Dev" Name = "Dev-EC2-instance" } }
结构类型:复杂配置的救星
object是命名属性的集合,每个字段可以不同类型。region是string、monitoring是bool、instance_count是number——这种异构结构用object最清晰,比拆成三个独立变量好懂。
tuple是固定长度、固定类型的序列。比如ingress规则需要[端口号, 协议, 端口号],用tuple([number, string, number])一锁死,传错顺序立刻报错,不会等到apply阶段才发现。
代码示例:
variable "config" { type = object({ region = string monitoring = bool instance_count = number }) default = { region = "us-east-1" monitoring = true instance_count = 1 } }
variable "ingress_values" { type = tuple([number, string, number]) default = [443, "TCP", 443] }
为什么类型约束能救命
不用类型约束,Terraform把变量当弱类型处理。后果很经典:plan阶段一切正常,apply阶段突然爆炸;或者某个模块悄悄改了输出格式,下游资源接收了完全没预料到的值。
加上类型约束,错误在plan阶段就被拦住。变量定义本身成了文档,新人看代码不用猜这个字段该传什么。基础设施代码的可预测性直接上一个台阶。
三条实战原则
简单值用基础类型。相关联但类型不同的数据,用object或tuple封装。需要索引就用list,去重场景用set,标签系统用map。类型约束写清楚,半夜PagerDuty响的概率至少降一半。
热门跟贴