前置知识: 云计算

Terraform 基础

3 min入门

Terraform 声明式 IaC 入门:HCL 语法、plan/apply 工作流、变量传递、模块复用与许可现状。

前置知识与学习目标

Terraform 是 HashiCorp 出品的基础设施即代码(IaC)工具:你用 HCL 语言描述 「想要的资源长什么样」,Terraform 负责算出从现状到目标的步骤并执行。 类比:传统运维像「口头点菜」,Terraform 像「把菜谱存档」——每次改动都 先出预览(plan),可审查、可回滚、可复制到另一个环境。

背景事实(选型前须知):2023 年 Terraform 许可证由 MPL 改为 BSL(BUSL), 社区据此分叉出 MPL 许可的 OpenTofu(CLI 兼容,命令基本一致);撰写时 Terraform 稳定版为 1.1x 系列。本文示例对两者均适用。

学完本文你应当能够:写出包含 provider/variable/resource/output 的最小 配置;按 init -> plan -> apply 工作流安全地创建与销毁资源;用变量文件 区分环境;用模块复用配置。

1. 第一个可运行配置

# main.tf —— 声明提供商:版本约束 + 区域
terraform {
  required_version = ">= 1.6"        # 锁定 Terraform 自身最低版本
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"             # 允许 5.x 内升级,禁止跨大版本
    }
  }
}

provider "aws" {
  region = "cn-north-1"
}

# 定义可由外部传入的变量(带描述与校验是好习惯)
variable "instance_type" {
  type    = string
  default = "t3.micro"
}

# 定义资源:AWS 上的 EC2 实例
resource "aws_instance" "example" {
  ami           = "ami-0abcdef1234567890"   # 示例 ID,请换成你区域的最新 AMI
  instance_type = var.instance_type         # 引用变量

  tags = {
    Name        = "tf-example"
    ManagedIron = "terraform"              # 标记资源归属,便于成本分摊
  }
}

# 输出资源属性:apply 后展示,也可被其他配置/模块引用
output "instance_id" {
  value = aws_instance.example.id
}

四类块的记忆口诀:terraform 管「工具链约束」、provider 管「连哪个云」、 variable/output 管「输入与出口」、resource/data 管「要什么与查什么」 (data 块查询已存在的资源,如最新的 AMI,见 410-IaC)。

2. 核心工作流:init -> plan -> apply

# 1. 初始化:下载 provider 插件、配置后端(首次必跑,换 provider 版本再跑)
terraform init
# 预期输出:
# Terraform has been successfully initialized!

# 升级 provider/模块到约束允许的最新版本
terraform init -upgrade

# 2. 预览:Terraform 计算差异但不执行,务必逐行阅读 + / - / ~ 符号
terraform plan
# 预期输出末尾:
# Plan: 1 to add, 0 to change, 0 to destroy.

# 3. 应用:真实创建资源(默认要求手动输入 yes 确认)
terraform apply
# 预期输出末尾:
# Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
# Outputs:
# instance_id = "i-0123456789abcdef0"

plan 输出里的符号就是「变更语言」:+ 创建、- 销毁、-/+ 先删后建 (大部分属性变更会导致替换,注意停机)、~ 原地更新。看到意外出现的 - 就要停下来查原因。

# CI/CD 场景:免交互应用(生产慎用,建议改为 plan 人工审 + apply)
terraform apply -auto-approve

# 更稳妥的两段式:先保存计划文件,再精确应用「这份计划」
terraform plan -out=tf.tfplan
terraform apply tf.tfplan     # 应用的一定是刚才审过的那份,不会中途漂移

# 销毁全部资源(同样有 yes 确认)
terraform destroy

# 只销毁/重建指定资源(调试用,target 滥用会造成状态与配置脱节)
terraform destroy -target=aws_instance.example

3. 变量传递:命令行、文件与默认值

# 命令行单变量
terraform plan -var "instance_type=t3.small"

# 变量文件:prod.tfvars(键值对),命名环境变体
terraform plan -var-file=prod.tfvars
# terraform.tfvars 是默认自动加载的文件名,无需 -var-file

# 查看输出值;-json 便于脚本解析
terraform output
terraform output -json
# 预期输出(json):
# {"instance_id": {"sensitive": false, "type": "string", "value": "i-0123..."}}

优先级(从高到低):-var > *.auto.tfvars 与 -var-file > terraform.tfvars > 环境变量 TF_VAR_name > 变量默认值。敏感值(数据库 密码等)走 TF_VAR_ 环境变量或密钥管理服务,绝不写进 tfvars 提交仓库。

4. 验证与格式化:进 CI 的第一步

# 语法与类型校验(不连云,速度快,适合 pre-commit)
terraform validate

# 格式化为官方风格(对齐等号、规范缩进)
terraform fmt -recursive

# CI 检查模式:有文件不规范则退出码 1,不修改
terraform fmt -check -recursive

5. 模块复用

模块是「带输入输出的配置目录」,是 Terraform 工程化的核心。来源可以是 本地目录、Git 仓库或官方 Registry:

# 复用本地子目录模块:传参 cidr,取回输出 vpc_id
module "vpc" {
  source = "./modules/vpc"
  cidr   = "10.0.0.0/16"
}

# 复用官方 Registry 模块:务必锁版本,防止升级破坏
module "vpc_official" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.16.0"

  cidr = "10.1.0.0/16"
}

output "vpc_id" {
  value = module.vpc.vpc_id   # 引用模块输出
}
# 下载/更新模块(init 也会自动完成)
terraform get -update

陷阱:模块版本不锁(version 省略)会让不同人/不同时间的 init 拉到 不同版本,plan 结果「时好时坏」。Registry 模块一律锁版本号。

6. 陷阱与调试速查

现象/报错原因处理
terraform init 报 provider 下载失败网络/镜像源问题配置 provider 镜像或代理后重试
Error: Failed to load plugin schemasprovider 版本约束冲突terraform init -upgrade 后复现查看
plan 里出现大量 -/+属性变更触发资源替换检查 prevent_destroy/不可变字段,评估停机
Value for undeclared variabletfvars 里有变量没声明补 variable 块或删多余键
apply 卡在等待资源依赖未就绪或网络不通看超时提示;terraform apply 中断后先 plan 对账
状态被误删.terraform 或 state 丢失立即用远端后端与版本化备份恢复(见 043)

调试辅助:TF_LOG=DEBUG terraform plan 输出详细日志;单属性差异用 terraform plan -target 缩小范围排查。

小结

  • 初学者要点:四个核心块(terraform/provider/variable/resource)+ 三个命令(init/plan/apply);plan 是安全边界,apply 前必读;格式化与 校验进 pre-commit;敏感值不落 tfvars。
  • 进阶注意:provider 一律锁版本(~>),Registry 模块锁 version; -/+ 意味着停机替换,高危;两段式 plan/apply 保证「审的就是跑的」; 远端状态与锁是团队协作前提(见 430-TerraformStateModule);选型时 知道 BSL/OpenTofu 分叉的存在即可,语法层面两者目前高度一致。