大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
Terraform
这一课你将能做到
- 用 Terraform 描述并创建 AWS 上的 VPC + EC2 + 安全组,一键拉起环境
- 理解
terraform plan、apply、destroy、state的核心工作流 - 用变量和模块管理复用 Terraform 代码,避免「复制粘贴式」配置
为什么要学
你在 AWS 控制台点点点创建了一台 EC2,队友问你「这台机器怎么配出来的」,你只能回「我点出来的」。两周后服务器被误删,你想重新创建一台一模一样的——忘了安全组规则、忘了子网配置、忘了绑了哪个 IAM Role。手动操作不可回溯、不可复现、不可版本化。
Terraform 的解决思路:把基础设施(服务器、网络、数据库、DNS)写成代码文件,存进 Git,任何人 terraform apply 一键创建完全一致的环境。这就是 Infrastructure as Code(IaC)。
人话:以前盖房子靠师傅手艺(点控制台),现在用 CAD 图纸(Terraform 文件)直接打印房子。图纸就是代码,改了能 review,能回滚,能复用。
先建立图像
Terraform 工作流:
编写 .tf 文件(描述目标状态)
│
terraform init ← 下载 provider(如 AWS 插件)
│
terraform plan ← 对比「当前状态」和「目标状态」,输出变更预览
│
terraform apply ← 执行变更(创建/修改/删除资源)
│
terraform destroy ← 清理所有 managed 资源
状态文件(terraform.tfstate)是什么?
Terraform 并不实时扫描云厂商(太慢了),而是维护一个本地状态文件,
记录「上次 apply 后有哪些资源、ID 是什么」。下次 plan 时,Terraform 对比:
.tf 文件描述的目标状态
vs
.tfstate 里的实际状态
vs
云厂商的真实状态(用于检测 drift)
计划出的变更 = 让真实状态趋近目标状态所需的操作。
跟着做
步骤 1:安装 Terraform 并初始化
# macOS
brew install terraform
# Linux
curl -fsSL https://get.opentofu.org/install-opentofu.sh | sh
# 或用 HashiCorp 官方包
terraform -v # 确认安装
步骤 2:写第一个 Terraform 文件——创建 VPC + EC2
创建目录 terraform-aws-ec2/,在里面写 main.tf:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "cn-north-1"
}
# 创建 VPC
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "myapp-vpc"
}
}
# 创建公有子网
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "cn-north-1a"
map_public_ip_on_launch = true
tags = {
Name = "myapp-public"
}
}
# 互联网网关(让公有子网能访问公网)
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.main.id
}
# 路由表:把 0.0.0.0/0 指向 IGW
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.gw.id
}
}
# 关联路由表到子网
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
# 安全组:只放行 22、80、443
resource "aws_security_group" "web" {
name = "web-sg"
description = "Allow HTTP HTTPS SSH"
vpc_id = aws_vpc.main.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# 创建 EC2 实例
resource "aws_instance" "web" {
ami = "ami-0c1234567890abcdef" # 换成你的 Region 的最新 Amazon Linux AMI
instance_type = "t3.micro"
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.web.id]
key_name = "myapp-key" # 提前在 AWS 控制台创建好
tags = {
Name = "myapp-web"
}
# 用户数据:实例启动时自动安装 Docker
user_data = <<-EOF
#!/bin/bash
yum update -y
yum install -y docker
systemctl enable --now docker
usermod -aG docker ec2-user
EOF
}
# 输出实例 IP
output "instance_public_ip" {
value = aws_instance.web.public_ip
}
步骤 3:执行计划并创建
cd terraform-aws-ec2
terraform init # 下载 AWS provider
terraform plan # 预览变更(此时不会真创建)
terraform apply # 确认后执行
plan 的输出会显示:
+要创建的资源~要修改的资源-要删除的资源
仔细看过没问题再 apply。执行完后控制台输出 instance_public_ip = "1.2.3.4",这就是你的新服务器。
步骤 4:变量化——不要写死
创建 variables.tf:
variable "region" {
description = "AWS Region"
type = string
default = "cn-north-1"
}
variable "instance_type" {
description = "EC2 instance type"
type = string
default = "t3.micro"
}
variable "key_name" {
description = "SSH key pair name"
type = string
}
创建 terraform.tfvars(加进 .gitignore,不要提交):
key_name = "myapp-key"
把 main.tf 里的 cn-north-1 替换为 var.region,t3.micro 替换为 var.instance_type,myapp-key 替换为 var.key_name。这样同一个项目可以给开发/测试/生产复用,只改 .tfvars。
步骤 5:清理资源
terraform destroy # 根据 tfstate 里记录的资源 ID,全部删除
注意:tfstate 文件必须妥善保管(存在远程后端如 S3 或 Terraform Cloud),否则别人不知道资源是谁管的。
讲清楚要点
- Terraform 是声明式不是命令式:你写「我想要一台 EC2」,Terraform 自己算出来需要做什么(创建 VPC、子网、安全组、EC2),而不是你一条条发创建命令。
- 状态文件是核心资产:
terraform.tfstate记录了资源 ID,丢了就等于「房子还在但图纸丢了」,Terraform 不认识这些资源,再 apply 会创建重复资源。团队共用时要存远程后端。 - Plan 是安全绳:永远先
plan再看输出,确认无误再apply。尤其是 destroy,看清楚哪些资源会被删。 - 资源之间有依赖关系:
aws_subnet.public引用了aws_vpc.main.id,Terraform 自动解析拓扑,先建 VPC 再建子网。 - 模块是复用的基石:多个环境(dev/staging/prod)的代码结构一样,提取成模块(module),只需改传入的变量。不要复制粘贴 3 份。
你可能会懵的地方
-
Terraform 和 Ansible 有什么区别? Terraform 管「创建基础设施」(创建服务器、网络、数据库实例),Ansible 管「配置已有机器」(装 JDK、改 Nginx 配置、起 Java 进程)。一个管房子蓝图,一个管室内装修。二者是黄金搭档。
-
为什么修改 .tf 文件后 apply 会删了重建? 某些属性(如 EC2 的 AMI ID、实例类型)是「创建后不可修改」的,Terraform 只能先删旧资源再建新资源。plan 输出里会明确显示
-/+(先删后建),确认清楚再执行。 -
tfstate 存本地和存 S3 有什么区别? 本地适合个人实验;团队协作必须用远程后端(S3 + DynamoDB 锁),否则两个人同时 apply 会冲突覆盖。S3 后端同时解决了状态备份和并发锁的问题。
-
Terraform Cloud 是什么? HashiCorp 官方托管的 Terraform 运行平台,免费版已支持远程状态、团队协作、计划(plan)评审。不想自己搭 S3+DynamoDB 的话直接用 Terraform Cloud。
-
provider 版本为什么要锁定? AWS provider 更新可能引入破坏性变更。
~> 5.0表示允许 5.x 的补丁更新,但不自动跳到 6.0。锁定版本保证「上周能跑的代码这周还能跑」。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| tfstate 提交到 Git | 泄露资源 ID、密钥引用,且无法协同 | 存远程后端(S3 / Terraform Cloud) |
| 不写 plan 直接 apply | 变更不可见,可能误删资源 | 强制 review plan 输出 |
| 所有环境共用一个 workspace | 改 dev 配置可能影响 prod | 用 workspace 或模块 + 不同 tfvars |
| 资源命名用中文或空格 | AWS tag 支持中文,但资源名可能不支持 | 用 kebab-case 英文 |
| 手动在控制台改了资源 | tfstate 与真实状态不一致,下次 plan 报错 | 只在 Terraform 里改,或 import 回来 |
卡点急救
- plan 报错
Provider configuration not present:.terraform目录里的 provider 没下全,运行terraform init -upgrade。 - apply 中途失败,状态不一致:运行
terraform plan看哪些资源实际已创建但 tfstate 没记录 → 用terraform import aws_instance.web i-xxx把已有资源导入状态 → 再 plan/apply。 - 想改 instance_type 但不想重建:改不了。instance_type 是 immutable 属性,必须重建。如果业务不能中断,先创建新实例、切流量、再销毁旧实例(蓝绿发布思路)。
面试常问
-
Terraform 和 CloudFormation(AWS 原生 IaC)怎么选? Terraform 多云(AWS/阿里云/腾讯云/GCP 一套语法),社区生态强;CloudFormation 只适用于 AWS,但和 AWS 集成最深(控制台直接看到堆栈)。小团队/多云选 Terraform,纯 AWS 且深度集成选 CloudFormation。
-
Terraform state 丢了怎么办? 如果没开远程后端且本地 state 丢了,Terraform 不认识已创建的资源,再 apply 会创建重复资源。紧急处理:用 AWS CLI 查出现有资源 ID,逐个
terraform import导入状态;长远必须配 S3 后端 + 定期备份。 -
如何处理敏感信息(密码、Access Key)? ① Terraform 变量用
sensitive = true标记;② 实际值存在环境变量或 HashiCorp Vault / AWS Secrets Manager;③ 绝不在 .tf 文件里写明文密码;④ tfstate 里的敏感值会加密存储(backend 加密)。
自测三问
terraform plan和terraform apply的区别是什么?为什么 plan 不会创建资源?- 为什么 tfstate 文件不能删除?如果团队多人协作,tfstate 应该存在哪里?
- 你发现 AWS 控制台里手动改了一个安全组规则,但 Terraform 代码没改。下次 apply 会发生什么?
小结一句话
Terraform 用声明式代码定义基础设施,把「手动点控制台」变成「可 review、可复现、可版本化」的工程实践。
延伸
- Terraform 官方教程
- 下一课:Ansible——用代码配置服务器,安装软件、改配置、起服务。