变量与输出深入
深入学习 Terraform 的变量系统和输出管理。
一、输入变量详解
1.1 变量类型
基础类型:
# 字符串
variable "instance_type" {
type = string
default = "t2.micro"
}
# 数字
variable "instance_count" {
type = number
default = 2
}
# 布尔值
variable "enable_monitoring" {
type = bool
default = true
}
集合类型:
# 列表
variable "availability_zones" {
type = list(string)
default = ["us-west-2a", "us-west-2b"]
}
# 集合(唯一值)
variable "allowed_ports" {
type = set(number)
default = [80, 443, 22]
}
# 映射
variable "instance_tags" {
type = map(string)
default = {
Environment = "Dev"
Team = "Platform"
}
}
结构化类型:
# 对象
variable "database_config" {
type = object({
engine = string
engine_version = string
instance_class = string
allocated_storage = number
})
default = {
engine = "postgres"
engine_version = "14.7"
instance_class = "db.t3.micro"
allocated_storage = 20
}
}
# 元组(固定长度和类型的列表)
variable "subnet_config" {
type = tuple([string, number, bool])
default = ["10.0.0.0", 24, true]
}
1.2 变量验证
variable "environment" {
type = string
description = "部署环境"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "环境必须是 dev、staging 或 prod。"
}
}
variable "instance_type" {
type = string
validation {
condition = can(regex("^t[2-3]\\.", var.instance_type))
error_message = "实例类型必须是 t2 或 t3 系列。"
}
}
variable "vpc_cidr" {
type = string
validation {
condition = can(cidrhost(var.vpc_cidr, 0))
error_message = "必须是有效的 CIDR 块。"
}
}
1.3 敏感变量
variable "database_password" {
type = string
sensitive = true
validation {
condition = length(var.database_password) >= 8
error_message = "密码长度至少为 8 个字符。"
}
}
# 使用敏感变量
resource "aws_db_instance" "main" {
password = var.database_password # 在日志中被隐藏
}
1.4 变量优先级
优先级从高到低:
- 命令行参数
-var - 命令行变量文件
-var-file - *自动加载的 .auto.tfvars
- terraform.tfvars
- 环境变量
TF_VAR_xxx - 默认值
default
# 示例
export TF_VAR_environment=dev
terraform apply \
-var-file="common.tfvars" \
-var-file="prod.tfvars" \
-var="instance_count=5"
二、输出值详解
2.1 基础输出
output "instance_id" {
description = "EC2 实例 ID"
value = aws_instance.web.id
}
output "instance_public_ip" {
description = "EC2 实例公网 IP"
value = aws_instance.web.public_ip
}
output "vpc_id" {
description = "VPC ID"
value = aws_vpc.main.id
}
2.2 敏感输出
output "database_password" {
description = "数据库密码"
value = random_password.db_password.result
sensitive = true
}
output "private_key" {
description = "SSH 私钥"
value = tls_private_key.ssh.private_key_pem
sensitive = true
}
2.3 结构化输出
output "vpc_details" {
description = "VPC 完整信息"
value = {
id = aws_vpc.main.id
cidr_block = aws_vpc.main.cidr_block
public_subnets = aws_subnet.public[*].id
private_subnets = aws_subnet.private[*].id
nat_gateway_ips = aws_eip.nat[*].public_ip
}
}
output "instance_summary" {
description = "所有实例的摘要信息"
value = [
for instance in aws_instance.app : {
id = instance.id
private_ip = instance.private_ip
public_ip = instance.public_ip
az = instance.availability_zone
}
]
}
2.4 条件输出
output "load_balancer_dns" {
description = "负载均衡器 DNS(如果启用)"
value = var.enable_lb ? aws_lb.main[0].dns_name : null
}
output "database_endpoint" {
description = "数据库端点"
value = var.create_database ? aws_db_instance.main[0].endpoint : "Database not created"
}
2.5 输出依赖
output "connection_string" {
description = "数据库连接字符串"
value = "postgresql://${var.db_username}:${var.db_password}@${aws_db_instance.main.endpoint}/${var.db_name}"
sensitive = true
depends_on = [
aws_db_instance.main,
aws_security_group.db
]
}
三、Local Values(本地值)
3.1 基础用法
locals {
# 组合变量
environment_name = "${var.project_name}-${var.environment}"
# 通用标签
common_tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "Terraform"
Owner = var.team_name
}
# 条件值
instance_type = var.environment == "prod" ? "t3.large" : "t3.micro"
# 计算值
total_instances = var.instance_count * var.replica_count
}
3.2 使用本地值
resource "aws_instance" "app" {
count = local.total_instances
ami = data.aws_ami.ubuntu.id
instance_type = local.instance_type
tags = merge(
local.common_tags,
{
Name = "${local.environment_name}-app-${count.index + 1}"
}
)
}
3.3 复杂计算
locals {
# 从 CIDR 计算子网
vpc_cidr_parts = split("/", var.vpc_cidr)
vpc_prefix = local.vpc_cidr_parts[0]
vpc_size = local.vpc_cidr_parts[1]
# 为每个 AZ 创建子网 CIDR
subnet_cidrs = [
for i, az in var.availability_zones :
cidrsubnet(var.vpc_cidr, 8, i)
]
# 构建资源映射
instances_by_az = {
for az in var.availability_zones :
az => [
for instance in aws_instance.app :
instance.id if instance.availability_zone == az
]
}
}
四、变量文件最佳实践
4.1 文件组织
project/
├── variables.tf # 变量声明
├── terraform.tfvars # 默认值(不提交)
├── dev.tfvars # 开发环境
├── staging.tfvars # 预发布环境
├── prod.tfvars # 生产环境
└── example.tfvars # 示例配置(提交到 Git)
4.2 variables.tf
# ========================================
# 通用配置
# ========================================
variable "project_name" {
description = "项目名称"
type = string
}
variable "environment" {
description = "环境名称"
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "环境必须是 dev、staging 或 prod。"
}
}
# ========================================
# 网络配置
# ========================================
variable "vpc_cidr" {
description = "VPC CIDR 块"
type = string
default = "10.0.0.0/16"
}
variable "availability_zones" {
description = "可用区列表"
type = list(string)
}
# ========================================
# 计算资源配置
# ========================================
variable "instance_type" {
description = "EC2 实例类型"
type = string
default = "t3.micro"
}
variable "instance_count" {
description = "实例数量"
type = number
default = 2
validation {
condition = var.instance_count > 0 && var.instance_count <= 10
error_message = "实例数量必须在 1-10 之间。"
}
}
4.3 dev.tfvars
project_name = "myapp"
environment = "dev"
vpc_cidr = "10.0.0.0/16"
availability_zones = ["us-west-2a", "us-west-2b"]
instance_type = "t3.micro"
instance_count = 1
enable_monitoring = false
enable_backup = false
4.4 prod.tfvars
project_name = "myapp"
environment = "prod"
vpc_cidr = "10.1.0.0/16"
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
instance_type = "t3.large"
instance_count = 3
enable_monitoring = true
enable_backup = true
backup_retention = 30
五、动态变量技巧
5.1 使用 for 表达式
# 创建映射
locals {
subnet_tags = {
for idx, cidr in local.subnet_cidrs :
"subnet-${idx}" => {
Name = "subnet-${idx}"
CIDR = cidr
}
}
}
# 过滤列表
locals {
prod_instances = [
for instance in var.instances :
instance if instance.environment == "prod"
]
}
5.2 条件表达式
locals {
# 三元运算符
instance_type = var.environment == "prod" ? "t3.large" : "t3.micro"
# 默认值处理
vpc_cidr = var.vpc_cidr != "" ? var.vpc_cidr : "10.0.0.0/16"
# 复杂条件
db_instance_class = (
var.environment == "prod" ? "db.r5.2xlarge" :
var.environment == "staging" ? "db.t3.large" :
"db.t3.micro"
)
}
5.3 合并策略
locals {
# 合并默认标签和自定义标签
instance_tags = merge(
var.common_tags,
var.custom_tags,
{
Name = "${var.project_name}-${var.environment}"
}
)
# 深度合并配置
full_config = merge(
var.default_config,
var.environment_config,
{
updated_at = timestamp()
}
)
}
六、输出使用场景
6.1 模块间通信
# 网络模块输出
output "vpc_id" {
value = aws_vpc.main.id
}
output "private_subnet_ids" {
value = aws_subnet.private[*].id
}
# 应用模块使用
module "network" {
source = "./modules/network"
}
module "app" {
source = "./modules/app"
vpc_id = module.network.vpc_id
subnet_ids = module.network.private_subnet_ids
}
6.2 生成配置文件
output "kubeconfig" {
description = "Kubernetes 配置文件内容"
value = templatefile("${path.module}/kubeconfig.tpl", {
cluster_name = aws_eks_cluster.main.name
cluster_endpoint = aws_eks_cluster.main.endpoint
cluster_ca = aws_eks_cluster.main.certificate_authority[0].data
})
sensitive = true
}
6.3 CI/CD 集成
output "deployment_info" {
description = "部署信息(供 CI/CD 使用)"
value = jsonencode({
load_balancer_url = "https://${aws_lb.main.dns_name}"
docker_registry = aws_ecr_repository.app.repository_url
deploy_timestamp = timestamp()
environment = var.environment
})
}
七、最佳实践
7.1 变量命名规范
# ✅ 推荐:清晰、描述性强
variable "vpc_cidr_block" {}
variable "enable_nat_gateway" {}
variable "database_instance_class" {}
# ❌ 避免:模糊、缩写过度
variable "cidr" {}
variable "nat" {}
variable "db_class" {}
7.2 提供充分的文档
variable "instance_type" {
description = <<-EOT
EC2 实例类型。
- dev: t3.micro
- staging: t3.small
- prod: t3.large 或更大
EOT
type = string
default = "t3.micro"
}
7.3 使用类型约束
# ✅ 推荐:明确类型
variable "instance_count" {
type = number
default = 2
}
# ❌ 避免:使用 any
variable "instance_count" {
type = any
}
小结
掌握变量和输出系统的关键点:
- 变量类型: 选择合适的类型确保数据正确性
- 验证规则: 提前捕获配置错误
- 敏感数据: 正确标记和处理敏感信息
- 本地值: 减少重复和提高可读性
- 输出值: 有效传递信息和集成
下一章我们将学习如何使用表达式和函数来增强配置的灵活性。