CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
DevOps 入门实战/课时/Terraform
RawBlame
课时目录12 节

DevOps 入门实战

  1. 01DevOps 入门
  2. 02操作系统
  3. 03Git 与 GitHub
  4. 04脚本语言
  5. 05网络协议
  6. 06Docker
  7. 07AWS
  8. 08Terraform
  9. 09Ansible
  10. 10GitHub Actions
  11. 11Nginx
  12. 12综合练习

Terraform

IaC 入门。

30 分钟

Terraform

这一课你将能做到

  1. 用 Terraform 描述并创建 AWS 上的 VPC + EC2 + 安全组,一键拉起环境
  2. 理解 terraform plan、apply、destroy、state 的核心工作流
  3. 用变量和模块管理复用 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),否则别人不知道资源是谁管的。

讲清楚要点

  1. Terraform 是声明式不是命令式:你写「我想要一台 EC2」,Terraform 自己算出来需要做什么(创建 VPC、子网、安全组、EC2),而不是你一条条发创建命令。
  2. 状态文件是核心资产:terraform.tfstate 记录了资源 ID,丢了就等于「房子还在但图纸丢了」,Terraform 不认识这些资源,再 apply 会创建重复资源。团队共用时要存远程后端。
  3. Plan 是安全绳:永远先 plan 再看输出,确认无误再 apply。尤其是 destroy,看清楚哪些资源会被删。
  4. 资源之间有依赖关系:aws_subnet.public 引用了 aws_vpc.main.id,Terraform 自动解析拓扑,先建 VPC 再建子网。
  5. 模块是复用的基石:多个环境(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 回来

卡点急救

  1. plan 报错 Provider configuration not present:.terraform 目录里的 provider 没下全,运行 terraform init -upgrade。
  2. apply 中途失败,状态不一致:运行 terraform plan 看哪些资源实际已创建但 tfstate 没记录 → 用 terraform import aws_instance.web i-xxx 把已有资源导入状态 → 再 plan/apply。
  3. 想改 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 加密)。

自测三问

  1. terraform plan 和 terraform apply 的区别是什么?为什么 plan 不会创建资源?
  2. 为什么 tfstate 文件不能删除?如果团队多人协作,tfstate 应该存在哪里?
  3. 你发现 AWS 控制台里手动改了一个安全组规则,但 Terraform 代码没改。下次 apply 会发生什么?

小结一句话

Terraform 用声明式代码定义基础设施,把「手动点控制台」变成「可 review、可复现、可版本化」的工程实践。

延伸

  • Terraform 官方教程
  • 下一课:Ansible——用代码配置服务器,安装软件、改配置、起服务。
加载本节练习…
← 上一课AWS下一课 →Ansible