本文行文:Terraform 是什么 → 为什么需要它(手动点控制台的问题)→ 声明式与命令式的区别
Terraform 是什么
Terraform 是 HashiCorp 开源的基础设施即代码(Infrastructure as Code,IaC)工具:把云上的 VPC、ECS、SLB、RDS、DNS、Kubernetes 资源写成配置文件,交给它去创建、修改和销毁。
它由三部分构成:
- HCL 配置语言:HashiCorp Configuration Language,专为描述资源而生,可读性接近声明式配置而非编程语言;
- Provider 插件生态:每个云厂商或平台对应一个 provider(
alicloud、aws、azurerm、kubernetes、cloudflare……),Terraform 本体只是一个调度器,真正调 API 的是 provider; - State 状态文件:记录”代码里的资源地址”与”真实资源的 ID”之间的映射,下一次执行靠它算出差异。详见 Terraform 状态文件。
核心工作流只有三步:写配置(write)→ 看计划(plan)→ 执行(apply)。
# 声明期望状态:要一个 VPC 和一个交换机terraform { required_providers { alicloud = { source = "aliyun/alicloud" version = "~> 1.230" } }}
provider "alicloud" { region = "cn-hangzhou"}
resource "alicloud_vpc" "this" { vpc_name = "demo-vpc" cidr_block = "10.0.0.0/16"}
resource "alicloud_vswitch" "this" { vpc_id = alicloud_vpc.this.id # 引用即依赖:交换机一定在 VPC 之后创建 cidr_block = "10.0.1.0/24" zone_id = "cn-hangzhou-i"}这份文件里没有一个字提到”先创建 VPC 再创建交换机”,也没有提”如果已经存在就跳过”——这些都由 Terraform 自己推导。
为什么需要 Terraform
不用 IaC 时,基础设施通常是在控制台点出来的,或者用一堆 aliyun / aws CLI 脚本拼出来。规模小的时候还好,一旦要交付第二套环境,问题就会集中爆发:
- 不可复现:测试环境和生产环境”看起来一样”,但没人说得清差了哪几个参数;
- 没有审计:谁在什么时候把安全组的
0.0.0.0/0放开过,日志里查不到意图,只能看到结果; - 环境漂移:线上被临时改过几次,代码(如果有)和现实早就脱节,下一次部署直接踩雷;
- 无法回滚:删错资源的补救方式是”凭记忆再点一遍”;
- 文档会过期:手写的架构文档描述的是”当时的样子”,而配置文件描述的是”现在应该是什么样”,且能被机器校验。
IaC 的本质是把基础设施的变更过程变成可评审、可重复执行的代码:
| 能力 | 手动控制台 | 自写 CLI 脚本 | Terraform 这类 IaC 工具 |
|---|---|---|---|
| 可复现 | 靠人记 | 部分可 | 完全可,同一份代码处处可建 |
| 变更可评审 | 无 | 靠代码评审 | plan 输出即变更单 |
| 幂等 | 天然不幂等 | 要自己判断存在性 | 由工具保证 |
| 依赖顺序 | 靠人 | 靠人(sleep、串行) | 依赖图自动排序 |
| 现状感知 | 只看得到资源列表 | 不知道 | 靠 state 精确追踪 |
| 并发保护 | 无 | 无 | 远程后端 + 状态锁 |
需要强调的是,IaC 不只 Terraform 一种:AWS CloudFormation / CDK、Pulumi、Kubernetes 的 YAML + Helm,甚至 Ansible playbook 都算。Terraform 的差异点在于:多云通用 + 声明式 + 有状态——一套写法覆盖阿里云、AWS、K8s 和 SaaS API,并且能算出”从现状到期望”的最小变更。
声明式与命令式
这两种思路的区别,不在于用什么语言,而在于你描述的是什么。
- 命令式(imperative):描述”怎么做”——一步步的指令序列,执行结果取决于起点;
- 声明式(declarative):描述”要什么”——最终状态,执行结果与起点无关,工具负责算出路径。
同样要创建一个 VPC 和一个交换机,命令式脚本大致长这样:
# 命令式:自己拼每一步,自己保证顺序set -euo pipefail
VPC_ID=$(aliyun vpc CreateVpc --RegionId cn-hangzhou \ --CidrBlock 10.0.0.0/16 --VpcName demo-vpc | jq -r '.VpcId')
VSWITCH_ID=$(aliyun vpc CreateVSwitch --RegionId cn-hangzhou \ --VpcId "$VPC_ID" --CidrBlock 10.0.1.0/24 \ --ZoneId cn-hangzhou-i --VSwitchName demo-vswitch | jq -r '.VSwitchId')
echo "vpc=$VPC_ID vswitch=$VSWITCH_ID"这段脚本能跑通一次,但再跑一次就会建出第二套 VPC 和交换机——因为它是”执行步骤”,不包含”是否已经达成”的判断。真要写幂等,就得自己查存在性、处理部分失败的中间态、按逆序回滚,脚本很快就会膨胀成一个难以维护的小框架。
声明式则把判断交给工具:
# 同样的目标,只写终态,重复 apply 不会重复创建resource "alicloud_vpc" "this" { vpc_name = "demo-vpc" cidr_block = "10.0.0.0/16"}
resource "alicloud_vswitch" "this" { vpc_id = alicloud_vpc.this.id cidr_block = "10.0.1.0/24" zone_id = "cn-hangzhou-i"}两者对比:
| 维度 | 命令式(脚本 / CLI) | 声明式(Terraform) |
|---|---|---|
| 你写的内容 | 执行步骤 | 期望终态 |
| 重复执行 | 通常重复创建 | 已达成则不做事(幂等) |
| 依赖顺序 | 自己安排 | 由引用与依赖图推导 |
| 变更预览 | 无,跑起来才知道 | plan 先算出增删改 |
| 部分失败 | 自己写回滚 | 修好配置再跑一次即可收敛 |
| 已存在的资源 | 无法感知 | state 里的资源不再重复创建 |
| 排错方式 | 翻日志 | 看 plan 差异与 state |
声明式之所以能幂等,关键在于”状态”:Terraform 每次执行都做三方对比——配置文件(期望)、state(上次执行后的记录)、真实 API(现状)。计划 = 期望与 state 的差异 + 刷新回来的现实漂移。没有 state 的声明式工具(比如有的平台只做”提交即创建”)是无法算出”该删什么”的,这也是 state 必须妥善保管的原因。
一次最小的执行流程
以 main.tf 为例,四个命令构成一个闭环:
terraform init # 下载 provider、初始化后端与锁文件terraform fmt # 统一格式(可以放进 CI 检查)terraform validate # 语法与引用检查,不发 API 请求terraform plan # 只计算差异,不执行terraform apply # 确认后执行plan 的输出就是”变更单”,也是评审时真正要看的对象:
Terraform will perform the following actions:
# alicloud_vpc.this will be created + resource "alicloud_vpc" "this" { + cidr_block = "10.0.0.0/16" + id = (known after apply) + vpc_name = "demo-vpc" }
# alicloud_vswitch.this will be created + resource "alicloud_vswitch" "this" { + cidr_block = "10.0.1.0/24" + vpc_id = (known after apply) }
Plan: 2 to add, 0 to change, 0 to destroy.两个细节值得注意:
+表示新增,~表示原地修改,-/+表示销毁重建(重建是有风险的,要看清是哪个属性导致的);(known after apply)说明这个值要创建完才知道(比如id),因为配置文件里并没有、也不该有真实 ID——ID 属于 state,不属于代码。
资源不再需要时,用 terraform destroy 按依赖逆序销毁;只想删其中一部分,就从配置里移除再 plan,确认 - 的条目符合预期后再 apply。
适用与不适用的场景
小结
Terraform 用 HCL 描述终态、用 state 记住现状、用 plan 呈现差异,把基础设施变更变成可评审、可重复执行的代码。理解”声明式描述结果而非过程”这一点,比记住任何命令都重要——所有幂等性、依赖推导、变更预览的能力,都是从这句话里长出来的。
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
换条路线
从其他文章中稳定抽取
最后更新于 ,距今已过 0 天
部分内容可能已过时