入门:IaC、声明式与第一个资源
基于 Terraform(1.x)· 核于 2026-07
速查
- IaC(Infrastructure as Code):把基础设施(服务器、网络、DNS、数据库、权限……)当代码写、进版本库、可 review、可回滚,取代「点控制台点出来」。
- 声明式 vs 命令式:Terraform 是声明式——你写「最终要什么」,它算差异;不像 shell/Ansible 那样写「一步步怎么做」。
- HCL:HashiCorp Configuration Language,
.tf文件;一个目录下所有.tf自动合并,顺序无关(Terraform 靠依赖图排序)。 - Provider:对接某平台 API 的插件(
aws/google/azurerm/kubernetes/…),在required_providers声明、provider块配置,来自 Registry。 terraform init:初始化工作目录——下载 Provider、初始化 backend、装模块;换 Provider/backend 后要重跑。terraform plan:生成执行计划(+创建 /~修改 /-销毁 /-/+替换);-out=plan.tfplan可存盘供apply精确执行。terraform apply:执行变更,默认交互式确认;-auto-approve跳过确认(CI 用,慎用)。terraform destroy:销毁本配置管理的全部资源,等价apply -destroy。- state:
terraform.tfstate记录「配置资源 ↔ 真实对象」映射,是真相之源,别手改。 - 期望状态 + 幂等:反复
apply收敛到同一状态;没变更时 plan 显示No changes。 fmt/validate:terraform fmt规范化缩进、terraform validate校验语法与内部一致性(不连云)。- 变量注入:
-var、-var-file、terraform.tfvars、*.auto.tfvars、TF_VAR_*环境变量。 - 许可提醒:Terraform 1.6+ 为 BUSL(非开源);要纯开源用 OpenTofu(命令
tofu,其余几乎一致)——详见生态与工程化。
一、什么是 IaC,为什么要声明式
「基础设施即代码」的核心主张:基础设施的每一次变更都应该像应用代码一样,被写进文件、进版本控制、经过 review、可复现、可回滚。它取代的是「登录控制台手点」这种不可追溯、不可复现、环境漂移严重的做法。
Terraform 属于其中的声明式流派,这是理解它的第一把钥匙:
- 命令式(imperative):你写下一步步的操作——「创建 VPC,然后创建子网,然后创建实例……」。典型代表是 shell 脚本、部分 Ansible playbook。问题是脚本要自己处理「已经存在了怎么办」「上次跑到一半挂了怎么办」。
- 声明式(declarative):你只描述最终要什么——「我要一个 VPC + 三个子网 + 一台实例」。Terraform 读取当前 state、对比配置,自己算出「要新建谁、改谁、删谁」,并按依赖顺序执行。你不关心过程,只关心结果。
声明式带来两个关键性质:
- 期望状态收敛:无论当前是什么状态,
apply后都会收敛到配置描述的目标。 - 幂等:同一份配置反复
apply,第二次开始都是No changes——因为现状已等于期望。
二、安装与第一个 Provider
安装方式很多(Homebrew、官方 apt/yum 源、tfenv 版本管理器、直接下二进制),装完验证:
terraform version
# Terraform v1.x.x on darwin_arm64Terraform 本体只是个「引擎」,真正会调 API 的是 Provider。你必须声明要用哪些 Provider,Terraform 才会在 init 时从 Registry 下载。约定俗成把它放在 terraform.tf 或 versions.tf:
# versions.tf
terraform {
required_version = ">= 1.6"
required_providers {
aws = {
source = "hashicorp/aws" # Registry 上的地址:命名空间/名字
version = "~> 5.0" # 版本约束:>=5.0 且 <6.0
}
}
}
# provider 块:配置该 Provider 的连接参数(区域、认证等)
provider "aws" {
region = "ap-northeast-1"
# 生产不要在这写密钥!用环境变量 AWS_ACCESS_KEY_ID / AWS_PROFILE / OIDC
}认证凭据别硬编码
provider 块里几乎永远不该出现明文 access key。用环境变量、共享凭据文件、或 CI 里的 OIDC(见生态篇)。硬编码的凭据会随代码进版本库,也可能被写进 plan 文件。
三、第一个资源:resource
resource 块是 Terraform 的主角,声明一个「要被管理的真实对象」。语法固定为 resource "<类型>" "<本地名>":
# main.tf
resource "aws_instance" "web" {
ami = "ami-0abcd1234efgh5678"
instance_type = "t3.micro"
tags = {
Name = "hello-terraform"
}
}- 类型(
aws_instance):由 Provider 决定,前缀通常是 Provider 名,代表「EC2 实例」这种资源种类。 - 本地名(
web):只在 Terraform 内部用于引用(aws_instance.web.id),不会成为云上资源的名字。 - 参数(
ami/instance_type/tags):具体填什么由 Provider 文档定义。
引用其它资源的属性时,Terraform 会自动建立依赖。比如给实例挂一个弹性 IP:
resource "aws_eip" "web_ip" {
# 引用上面实例的 id —— 这行让 Terraform 知道:必须先建 instance 再建 eip
instance = aws_instance.web.id
}这种「A 引用了 B 的属性 → A 依赖 B」叫隐式依赖,是 Terraform 排序的主要依据(详见配置语言篇)。
四、核心工作流:init → plan → apply → destroy
terraform init:初始化
在配置目录第一次动手前必须跑。它会:下载 required_providers 里声明的 Provider 到 .terraform/、初始化 backend(state 存哪)、下载引用的模块。
terraform init
# Initializing provider plugins...
# - Installing hashicorp/aws v5.x.x...
# Terraform has been successfully initialized!换了 Provider 版本、加了新模块、改了 backend,都要重跑 init(必要时 init -upgrade)。
terraform plan:先看后做
plan 是 Terraform 最有价值的一步——它读当前 state、对比配置,只算不做,把将发生的变更列成计划给你审:
terraform plan
# Terraform will perform the following actions:
# # aws_instance.web will be created
# + resource "aws_instance" "web" {
# + ami = "ami-0abcd1234efgh5678"
# + instance_type = "t3.micro"
# + id = (known after apply)
# }
# Plan: 1 to add, 0 to change, 0 to destroy.符号含义要记牢:+ 新建、~ 原地修改、- 销毁、-/+(或 +/-)先删后建的替换。(known after apply) 表示这个值要等真正创建后才知道。想固化这份计划可以 plan -out=tfplan,再让 apply tfplan 严格照它执行。
terraform apply:执行
terraform apply
# ...同样的 plan 输出...
# Do you want to perform these actions?
# Enter a value: yes默认会再算一遍 plan 并要你输入 yes 确认。CI 环境里用 terraform apply -auto-approve 或先 plan -out 再 apply tfplan(更安全,保证执行的就是审过的那份)。
terraform destroy:销毁
terraform destroy # 等价 terraform apply -destroy销毁本配置管理的全部资源,同样要确认。想只销毁部分可配合 -target(应急用,别常态化)。
一张图记住全流程:
# 编辑 .tf 文件(write)
terraform init # 一次性:装 provider / 连 backend
terraform fmt # 规范化格式
terraform validate # 校验语法(不连云)
terraform plan # 看将要发生什么
terraform apply # 确认并执行
# ……日常改配置后重复 plan / apply……
terraform destroy # 不需要时拆除五、期望状态与幂等:Terraform 的世界观
理解这一节,才算真正入门。
apply 成功后,state 里记下了「web 实例 = 云上那台 i-xxxx」。此时你不改配置再 plan:
terraform plan
# No changes. Your infrastructure matches the configuration.因为 Terraform 会先 refresh(读云上真实状态刷新到 state 副本),再对比配置——现状 == 期望,所以无操作。这就是幂等。
如果有人绕过 Terraform 去控制台改了这台实例(比如手动改了 tag),下次 plan 会检测到 drift(漂移):
terraform plan
# ~ resource "aws_instance" "web" {
# ~ tags = {
# ~ "Name" = "changed-by-hand" -> "hello-terraform"
# }
# }
# Plan: 0 to add, 1 to change, 0 to destroy.Terraform 认定「配置才是真相」,会计划把它改回配置描述的样子。这正是声明式 + state 的威力,也是「别绕过 Terraform 手改基础设施」这条铁律的由来(drift 细节见状态管理篇)。
六、变量与输出:让配置可复用
写死值不利复用。用 variable 把可变部分参数化、用 output 把结果暴露出来:
# variables.tf
variable "instance_type" {
type = string
default = "t3.micro"
description = "EC2 实例规格"
}
# main.tf 中引用
resource "aws_instance" "web" {
ami = "ami-0abcd1234efgh5678"
instance_type = var.instance_type # 用 var.<名> 引用
}
# outputs.tf
output "public_ip" {
value = aws_instance.web.public_ip # apply 后打印出来
}赋值方式(优先级从高到低):命令行 -var="instance_type=t3.small" > -var-file > *.auto.tfvars > terraform.tfvars > 环境变量 TF_VAR_instance_type > default。变量、输出、locals 的完整用法见配置语言篇。
到这里你已经能跑通「写配置 → plan → apply → 改 → 再 apply → destroy」的完整闭环。接下来按本叶地图深入语言、状态、模块与生态四块。