Terraform 入门实战:安装与实施

我刚学 Terraform 时干过一件蠢事:看 terraform.tfstate 像个临时文件就顺手删了,下一次 apply,它把整套 VPC、交换机和 ECS 原封不动又建了一遍,还因重名卡在半空。

这篇把安装、云插件、第一个 ECS 与 init/plan/apply/destroy 四个命令串成一条闭环。走完你会明白 Terraform 到底在「对比」什么,也能拿到一份可反复创建、干净销毁的最小配置。

一、场景:手工点控制台,迟早要还的

删状态文件之所以是事故,根子在于我压根没理解 Terraform 靠什么记住资源------这个状态(State)就是它对真实世界的全部记录。要讲清这件事,得先回到一个更前置的问题:明明控制台点几下就能开机器,为什么还要写代码?

手工运维的痛点,和基础设施即代码(Infrastructure as Code,IaC)的价值,恰好一一对应:

手工点控制台 Terraform 声明式配置
操作只在某个人脑子里,换人就断线 配置即代码,能进 Git、能 Review、能回滚
十台机器点出十种状态,环境漂移 同一份配置在任何环境跑出相同结果
改了什么全凭记忆,出事无法追溯 每次变更先出 diff,看清改什么再执行

Terraform 是 HashiCorp 出品的基础设施即代码工具,用声明式配置描述「想要的最终状态」,再由它把云资源创建、修改、销毁成这个状态。它不关心操作顺序,只关心「最终应该长什么样」。

工具定位先分清,才不会拿它干不擅长的活:

工具 定位 关注对象
Terraform 资源编排,调云厂商 API 资源从生到死的生命周期
Ansible 配置管理,登录机器执行任务 机器内部的状态
CloudFormation 资源编排,能力近似 绑定 AWS 单一云
Pulumi 资源编排,用通用语言写配置 灵活但门槛更高

适用边界也很清楚:

  • 适合:计算、网络、存储、数据库、DNS、权限等资源生命周期,多云统一编排与环境批量复制。
  • 不适合:机器内部的精细化配置(交给 Ansible 或 cloud-init),以及一条命令就能搞定的一次性临时操作。

一句话记住这套心智模型:描述状态、对比差异、执行变更。手工脚本的创建与删除往往要各写一套逻辑,而 Terraform 一份配置双向可用。

二、一个二进制文件,装起来也有三道坎

Terraform 没有依赖库,安装就是解压、放进 PATH,卸载就是删文件。既然这么简单,为什么我敲 terraform 还是提示 command not found?问题几乎都出在「放进 PATH」这一步。

你先到 releases.hashicorp.com 确认最新版本号,替换下面的 1.16.5,再按平台选一种方式。

平台 推荐方式 架构包
Linux 二进制 / apt / yum x86_64 用 amd64,ARM 用 arm64
macOS Homebrew / 二进制 Intel 用 amd64,M 系列用 arm64
Windows 手动解压 / Chocolatey 统一 windows_amd64

Linux:通用二进制

任意发行版都能用,下载、解压进 PATH、清理安装包:

bash 复制代码
wget https://releases.hashicorp.com/terraform/1.16.5/terraform_1.16.5_linux_amd64.zip
sudo unzip terraform_1.16.5_linux_amd64.zip -d /usr/local/bin/
rm terraform_1.16.5_linux_amd64.zip

Linux:包管理器(便于后续升级)

Debian/Ubuntu 添加官方 apt 源,以后 apt upgrade 自动跟随新版本:

bash 复制代码
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform

RHEL/CentOS/Alibaba Cloud Linux 添加官方 yum 源:

bash 复制代码
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo yum install -y terraform

macOS

有 Homebrew 就直接装,它会自动处理架构:

bash 复制代码
brew tap hashicorp/tap
brew install hashicorp/tap/terraform

不装 Homebrew 就手动下载二进制。Apple Silicon(M1/M2/M3)选 arm64,Intel 芯片把 arm64 换成 amd64:

bash 复制代码
curl -LO https://releases.hashicorp.com/terraform/1.16.5/terraform_1.16.5_darwin_arm64.zip
unzip terraform_1.16.5_darwin_arm64.zip
sudo mkdir -p /usr/local/bin
sudo mv terraform /usr/local/bin/

Windows

手动安装以管理员身份打开 PowerShell:下载 zip、解压到固定目录、写入系统 PATH。

powershell 复制代码
$version = "1.16.5"
$installDir = "C:\Program Files\Terraform"

Invoke-WebRequest "https://releases.hashicorp.com/terraform/$version/terraform_${version}_windows_amd64.zip" -OutFile terraform.zip
New-Item -ItemType Directory -Force -Path $installDir | Out-Null
Expand-Archive -Path terraform.zip -DestinationPath $installDir -Force

$machinePath = [Environment]::GetEnvironmentVariable("Path", "Machine")
if ($machinePath -notlike "*$installDir*") {
    [Environment]::SetEnvironmentVariable("Path", "$machinePath;$installDir", "Machine")
}

不想敲命令也可以走图形界面,到 官方安装页 下载 Windows 版 ZIP 包:解压出 terraform.exe 放进固定目录(如 C:\Program Files\Terraform),再到「设置 → 系统 → 关于 → 高级系统设置 → 环境变量」,把该目录追加到系统变量 Path。已安装 Chocolatey,则以管理员身份运行 PowerShell 后执行:

powershell 复制代码
choco install terraform

无论哪种方式,装完都要重开一个终端,PATH 才会生效。

多版本管理

多个项目用不同版本时,用 tfenv 切换,避免「这台能跑、那台跑不了」的版本漂移:

bash 复制代码
tfenv install 1.16.5
tfenv use 1.16.5

验证安装

bash 复制代码
terraform version
latex 复制代码
Terraform v1.16.5 on linux_amd64

装不上时的报错基本是下面四类,按报错对号入座:

报错信息 原因 处理
command not found 解压目录没进 PATH,或终端没重开 加 PATH 后重开终端
Exec format error 架构包下错(amd64/arm64 不符) 重新下载对应架构包
Permission denied 二进制缺执行权限 chmod +x terraform
unzip: command not found 系统没装 unzip apt install unzip 或 yum install unzip

注意:架构包必须和 CPU 对应------x86_64 用 linux_amd64,鲲鹏、树莓派等 ARM 机器用 linux_arm64,下错了不会在安装时报错,只会在运行时扔下一句 Exec format error。

版本号能打印了,但此时的 Terraform 还只是一台空转的机器。它凭什么能去操作阿里云的 API?

三、原理:提供者才是它操作云的那只手

答案是提供者(Provider)。提供者是 Terraform 与云厂商之间的插件,把阿里云几千个 API 封装成 Terraform 能理解的资源类型。没有提供者,Terraform 就只剩一台空转的「状态对比机」。

新建一个空目录 ~/tf-first,在里面创建 main.tf,在 terraform 块里声明要用的提供者:

plain 复制代码
terraform {
  required_version = ">= 1.5"

  required_providers {
    alicloud = {
      source  = "aliyun/alicloud"
      version = "~> 1.278.0"
    }
  }
}

provider "alicloud" {
  region = "cn-hangzhou"
}

三个字段各有职责:

字段 含义 不锁定的后果
source 提供者在 Registry 上的定位,格式「命名空间/名称」 找不到插件
version 版本约束,~> 1.278.0 允许 1.278.x 补丁升级 大版本升级常带破坏性变更
provider 块 连接参数,region 指定资源默认所在地域 资源落错地域

source 这一项有个时效性很强的坑:阿里云提供者已于 2026 年 3 月从 hashicorp/alicloud 迁移到 aliyun/alicloud,新配置一律用后者;旧配置迁移只需改 source 后重新 init,继续写旧 source 会在 init 阶段报找不到插件。

一台机器上要管理多个账号或地域,用 alias 声明多个实例,再在资源里显式指定:

plain 复制代码
provider "alicloud" {
  alias  = "bj"
  region = "cn-beijing"
}

resource "alicloud_instance" "web" {
  provider = alicloud.bj
}

连接云需要凭据,AccessKey 的给法按安全等级分三档:

方式 做法 适用场景
环境变量 export ALICLOUD_ACCESS_KEY=... 本地练习、临时操作
RAM 角色 给 ECS 挂角色,ecs_role_name 免密钥 云上生产主机
OIDC 临时凭证 CI 通过 OIDC 换取短期凭证 流水线,根上消灭常驻密钥

环境变量注入凭据的命令:

bash 复制代码
export ALICLOUD_ACCESS_KEY="你的 AccessKey ID"
export ALICLOUD_SECRET_KEY="你的 AccessKey Secret"

AccessKey 绝对不能写进 .tf 文件------文件要进 Git,硬编码等于公开泄露,没有补救,只能吊销。

init 要从 registry.terraform.io 下载提供者,国内网络可能超时。在 ~/.terraformrc 配置阿里云官方镜像即可提速:

plain 复制代码
provider_installation {
  network_mirror {
    url = "https://terraform-mirror.aliyuncs.com/"
  }
}

资源参数不需要背:打开 registry.terraform.io,找到 aliyun/alicloud 文档页,左侧 Resources 栏列出全部资源类型,点进去就有完整参数表、必填项和官方示例,照文档填是正常姿势。

手已经接上了,却还没告诉它「想要什么」。一台 ECS,到底该怎么声明出来?

四、方案:把「想要的状态」写进 resource

声明一台 ECS,靠的是资源(resource)块;同样一份配置能部署开发、测试、生产三套环境,前提是先把「环境相关」和「逻辑相关」分离------这是基础设施即代码的基本素养,也是 Terraform 环境复现能力的根基。

标准单模块布局用三个文件各司其职:

latex 复制代码
tf-first/
├── main.tf        # 资源定义(逻辑相关)
├── variables.tf   # 可变输入(环境相关)
└── outputs.tf     # 对外暴露的值

你在 variables.tf 里声明交换机 ID 这类随环境变化的值:

plain 复制代码
variable "vswitch_id" {
  description = "目标交换机 ID,可在 VPC 控制台查看"
  type        = string
}

你在 main.tf 追加资源定义,这是全篇最核心的一段:

plain 复制代码
resource "alicloud_instance" "web" {
  instance_name              = "my-first-ecs"
  instance_type              = "ecs.t6-c1m1.large"
  image_id                   = "aliyun_2_1903_x64_20G_alibase_20230911.vhd"
  vswitch_id                 = var.vswitch_id
  internet_charge_type       = "PayByTraffic"
  internet_max_bandwidth_out = 5
  password                   = var.instance_password
}

你在 outputs.tf 声明建完后想看到的值:

plain 复制代码
output "instance_id" {
  value = alicloud_instance.web.id
}

output "public_ip" {
  value = alicloud_instance.web.public_ip
}

resource "alicloud_instance" "web" 是最核心的三段式语法:

latex 复制代码
resource  "alicloud_instance"  "web"
  │             │                 │
  │             │                 └─ 本地名称:当前配置内的唯一标识,用于互相引用
  │             └─────────────────── 资源类型:提供者给出的能力,格式「云产品_资源」
  └───────────────────────────────── 关键字:声明一个资源

块内 var.vswitch_id 引用变量,alicloud_instance.web.id 引用另一资源的属性。Terraform 会自动分析引用关系、构建依赖图,按正确顺序创建,不需要你手动排先后。

几个参数的真实含义:

参数 含义
instance_type 实例规格,决定 CPU/内存
image_id 镜像,决定操作系统
internet_charge_type = "PayByTraffic" 按流量计费
internet_max_bandwidth_out = 5 出网带宽峰值 5 Mbps,不设则默认不分配公网 IP

两个细节别踩:镜像 ID 不要手抄,用数据源(data source)按名称动态查;密码用 sensitive 标记,apply 时交互输入、输出日志自动打码:

plain 复制代码
data "alicloud_images" "ubuntu" {
  owners     = "system"
  name_regex = "^ubuntu_24_04"
}

variable "instance_password" {
  type      = string
  sensitive = true
}

多数依赖靠属性引用自动推导,个别「有顺序要求但无属性引用」的关联用 depends_on;生命周期(Lifecycle)块则控制资源变更策略:

生命周期参数 作用 典型场景
prevent_destroy = true 含删除该资源的计划直接失败 数据库等有状态资源保命
create_before_destroy = true 先建新再删旧 无状态应用零中断滚动替换
ignore_changes = [tags] 忽略指定属性差异 容忍控制台手工改动

注意:给数据库这类有状态资源加上 prevent_destroy,是最便宜的后悔药------误跑 destroy 时它会在确认前直接拦下。

配置写完了,你怎么知道它这次会建什么、会不会顺手删掉没打算删的东西?

五、验证:先看 diff 再动手

Terraform 敢让你放心 apply,靠的是把「看清楚」和「动手」拆成两个命令。日常迭代的主循环就是这四板斧:

命令 作用 是否改动云资源 常用时机
init 下载提供者、生成锁文件、初始化状态后端 否 新项目、新增依赖后
plan 对比配置与状态,输出执行计划 否(只读) 每次 apply 前
apply 按计划调用 API 创建/变更资源 是 核对计划无误后
destroy 按依赖逆序删除全部资源 是 清理练习环境

init:初始化

你首次执行 init,它做三件事:下载提供者插件到 .terraform/、生成 .terraform.lock.hcl 锁文件(Lock File)、初始化状态后端。锁文件必须提交 Git,.terraform/ 目录则写进 .gitignore。

bash 复制代码
terraform init

plan:只读预览

执行计划(plan)对比配置与当前状态,用三种符号标出动作:+ 创建、~ 更新(附字段级 diff)、- 删除,一致时输出 No changes。它不调用任何写操作,是应用前唯一的免费安全检查。

你最好把计划固化成文件,apply 时严格按这份计划执行,杜绝「plan 之后配置又被改」的竞态:

bash 复制代码
terraform plan -out=tfplan

计划末尾的汇总数字要逐一核对,多一个删除、少一个创建都和预期不符:

latex 复制代码
Plan: 1 to add, 0 to change, 0 to destroy.

apply:执行

你按固化的计划执行,首次会要求输入 yes 确认;自动化场景才用 -auto-approve 跳过确认:

bash 复制代码
terraform apply tfplan
terraform apply -auto-approve

apply 默认按依赖图并行创建互不依赖的资源(并行度 10)。多人共用远程状态后端时会加锁,你看到 Error acquiring the state lock 就等他人完成再重跑,不要强行删锁文件。失败了也不用慌:修正配置后直接重跑,Terraform 会基于状态继续完成剩余工作,而不是推倒重来。

destroy:销毁

bash 复制代码
terraform destroy

销毁顺序与创建相反:先删依赖别人的资源(实例),再删被依赖的资源(交换机、VPC),保证不报「资源仍被占用」。日常练习建议你练完随手 destroy,账单不心疼,也验证了配置「可生可灭」的完整性。

现在可以解开开头那个删状态文件的坑了。apply 成功后目录里的 terraform.tfstate 是 Terraform 的记忆,记录「真实世界当前是什么样」,plan/apply 都基于它做 diff:

  • 它是唯一事实来源:删了它,Terraform 会认为所有资源都不存在,再 apply 就全部重建,这正是开头重复建资源的原因。
  • 它不能进 Git:可能含密码、IP,且多人并发提交会互相覆盖。
  • 团队协作必须用远程状态后端:阿里云用 OSS,同时提供存储与锁,在 terraform 块声明后重新 init 即迁移:
plain 复制代码
terraform {
  backend "oss" {
    bucket = "my-tf-state"
    key    = "prod/ecs.tfstate"
    region = "cn-hangzhou"
  }
}

其余高频命令构成日常排障工具箱:

命令 作用
terraform fmt 自动格式化,提交 Git 前必跑
terraform validate 静态校验语法与引用合法性
terraform state list 查看状态管理着哪些资源
terraform show 查看资源当前属性
terraform console 交互式测试表达式

注意:状态文件不进 Git、不手删,单机练习无所谓,团队协作必须上 OSS 远程状态后端加锁,否则并发 apply 会互相覆盖、损坏状态。

四板斧跑通了,剩下的坑,能不能在动手前用一张表全部避开?

六、总结延伸:避坑清单与术语速查

把全篇的坑和正解汇成一张清单,贴在工位上比临时翻文档快:

坑 正解
版本漂移 锁 required_version 与提供者版本,提交 lock 文件,tfenv 管本机
凭据硬编码 一律环境变量、RAM 角色或 OIDC,绝不进 .tf
状态文件裸奔 多人协作上 OSS 远程状态后端加锁
删状态文件重建 状态是唯一事实来源,不手删、不进 Git
乱跑 destroy 先 plan 看清波及范围,有状态资源加 prevent_destroy
滥用 -auto-approve 只在 CI/脚本用,手敲保留交互确认
控制台手工改资源 下次 apply 会被「纠正」回去,一切变更走 Terraform
滥用 -target 跳过依赖图部分节点,生产环境易建不全,慎用

至此你已经走通最小闭环:安装 → 声明提供者 → 定义资源 → init/plan/apply/destroy。「描述状态、对比差异、执行变更」这套心智模型,就是基础设施即代码的全部精髓。

进阶建议按这个顺序走:

  1. 变量与 locals 表达式
  2. 远程状态后端 OSS
  3. module 模块化复用
  4. CI/CD 流水线集成
  5. 远程状态跨模块共享

把第一台 ECS destroy 掉,再随手建一个,手感就出来了。

术语速查表:

术语 英文 一句话解释
基础设施即代码 Infrastructure as Code 用代码而非手工操作管理基础设施
提供者 Provider Terraform 与云厂商 API 之间的插件
资源 Resource 一个被管理的云对象,如一台 ECS
数据源 Data Source 只读查询已有信息,如按名称查镜像
状态 State 记录真实世界当前状态的文件,diff 的基准
执行计划 Plan 变更前的只读预览,含增/改/删符号
生命周期 Lifecycle 控制资源创建、替换、销毁策略的块
锁文件 Lock File 锁定提供者版本与校验和,保证构建一致

参考链接:

相关推荐
空空dream3 小时前
06 | Prometheus打通FastAPI云原生数据采集
云原生·prometheus·fastapi
池佳齐3 小时前
【软考高级架构师案例】容器化改造:Docker + K8s,如何用云原生视角重构传统多层架构?
docker·云原生·kubernetes
可达智灵4 小时前
堵了门,钥匙还在:一次持续九个月的 AWS 接管修复
云计算·aws·可达智灵
骑着蜗牛撵大象3274 小时前
云原生存储进阶:CSI 演进与 AI 工作负载的存储架构设计
人工智能·ai·云原生·kubernetes·csi·并行文件系统
运维开发王义杰4 小时前
NATS 叶子节点怎么用:从用户认证到 Azure 流量压缩
云原生
云边云科技_云网融合5 小时前
混合云组网难题破解:SD-WAN 如何打通 AWS 与阿里云,让总部分支访问稳如专线
阿里云·云计算·aws
会议咨询5 小时前
2026年信息学、智能通信与云计算国际会议(IICC 2026)
云计算·信息学·智能通信
有什么事5 小时前
云游戏软件与云手机的技术分野,云端渲染底座上的两条产品路线
游戏·云原生·智能手机
ESDWAN6 小时前
跨境投资与全球资产托管数字化解决方案:系统架构、数据治理与合规风控实践
大数据·金融·系统架构·云计算