☰
Terraform 入门实战:安装与实施
2026/10/14 9:32:21 网站建设 项目流程

我刚学 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 / yumx86_64 用amd64,ARM 用arm64
macOSHomebrew / 二进制Intel 用amd64,M 系列用arm64
Windows手动解压 / Chocolatey统一windows_amd64

Linux:通用二进制

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

wgethttps://releases.hashicorp.com/terraform/1.16.5/terraform_1.16.5_linux_amd64.zipsudounzipterraform_1.16.5_linux_amd64.zip-d/usr/local/bin/rmterraform_1.16.5_linux_amd64.zip

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

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

wget-O- https://apt.releases.hashicorp.com/gpg|sudogpg--dearmor-o/usr/share/keyrings/hashicorp-archive-keyring.gpgecho"deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com$(lsb_release-cs)main"|sudotee/etc/apt/sources.list.d/hashicorp.listsudoaptupdate&&sudoaptinstallterraform

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

sudoyuminstall-yyum-utilssudoyum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.reposudoyuminstall-yterraform

macOS

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

brew tap hashicorp/tap brewinstallhashicorp/tap/terraform

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

curl-LOhttps://releases.hashicorp.com/terraform/1.16.5/terraform_1.16.5_darwin_arm64.zipunzipterraform_1.16.5_darwin_arm64.zipsudomkdir-p/usr/local/binsudomvterraform /usr/local/bin/

Windows

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

$version="1.16.5"$installDir="C:\Program Files\Terraform"Invoke-WebRequest"https://releases.hashicorp.com/terraform/$version/terraform_${version}_windows_amd64.zip"-OutFile terraform.zipNew-Item-ItemType Directory-Force-Path$installDir|Out-NullExpand-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 后执行:

choco install terraform

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

多版本管理

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

tfenvinstall1.16.5 tfenv use1.16.5

验证安装

terraform version
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系统没装 unzipapt 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块里声明要用的提供者:

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声明多个实例,再在资源里显式指定:

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 换取短期凭证流水线,根上消灭常驻密钥

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

exportALICLOUD_ACCESS_KEY="你的 AccessKey ID"exportALICLOUD_SECRET_KEY="你的 AccessKey Secret"

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

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

provider_installation { network_mirror { url = "https://terraform-mirror.aliyuncs.com/" } }

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

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

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

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

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

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

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

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

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

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声明建完后想看到的值:

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

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

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 时交互输入、输出日志自动打码:

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。

terraform init

plan:只读预览

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

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

terraform plan-out=tfplan

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

Plan: 1 to add, 0 to change, 0 to destroy.

apply:执行

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

terraform apply tfplan terraform apply -auto-approve

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

destroy:销毁

terraform destroy

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

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

  • 它是唯一事实来源:删了它,Terraform 会认为所有资源都不存在,再 apply 就全部重建,这正是开头重复建资源的原因。
  • 它不能进 Git:可能含密码、IP,且多人并发提交会互相覆盖。
  • 团队协作必须用远程状态后端:阿里云用 OSS,同时提供存储与锁,在terraform块声明后重新 init 即迁移:
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用代码而非手工操作管理基础设施
提供者ProviderTerraform 与云厂商 API 之间的插件
资源Resource一个被管理的云对象,如一台 ECS
数据源Data Source只读查询已有信息,如按名称查镜像
状态State记录真实世界当前状态的文件,diff 的基准
执行计划Plan变更前的只读预览,含增/改/删符号
生命周期Lifecycle控制资源创建、替换、销毁策略的块
锁文件Lock File锁定提供者版本与校验和,保证构建一致

参考链接:

  • https://developer.hashicorp.com/terraform/install
  • https://registry.terraform.io/providers/aliyun/alicloud/latest/docs
  • https://developer.hashicorp.com/terraform/cli/commands
  • https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/backends/oss

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询