terraform —— 基础设施即代码

运维工具手册 07-自动化与版本 6.5 KB 原文

terraform —— 基础设施即代码

分类:07 自动化与版本 适用:Terraform 1.x(0.12 之前语法差异大) 一句话:用声明式配置描述云资源,plan 看差异、apply 落地、state 记现状。

1. 它解决什么问题

  • 云上资源靠控制台点,谁改了什么没人知道。
  • 同一套环境要在多个区域/账号复刻。
  • 变更前想先知道"会改什么、会不会删库"。
  • 资源要能版本化、评审、回滚。

2. 安装与初始化

# 官方仓库
apt install -y terraform
# 或下载二进制
curl -LO https://releases.hashicorp.com/terraform/1.9.5/terraform_1.9.5_linux_amd64.zip
unzip terraform_1.9.5_linux_amd64.zip -d /usr/local/bin/

terraform version
terraform init            # 下载 provider、初始化 backend
terraform init -upgrade   # 升级 provider

目录结构:

infra/
├── main.tf
├── variables.tf
├── outputs.tf
├── versions.tf
├── terraform.tfvars
└── modules/

3. 核心工作流

terraform init          # 初始化(首次/换目录/改 backend 后)
terraform fmt           # 格式化
terraform validate      # 语法校验
terraform plan          # 看将要发生什么
terraform apply         # 执行
terraform destroy       # 销毁(⚠️)

标准动作永远是 plan → 人工确认 → apply,CI 里可以把 plan 结果作为评审附件。

terraform plan -out=tfplan
terraform apply tfplan          # 执行上次的 plan,避免漂移

用 -out 能保证"评审的计划"和"执行的计划"一致,这是生产环境的规范做法。

4. 变量与输出

# variables.tf
variable "region" {
  type        = string
  default     = "cn-hangzhou"
  description = "地域"
}

variable "instance_count" {
  type    = number
  default = 2
}

variable "tags" {
  type = map(string)
  default = {}
}

传值优先级(低 → 高):

  1. 环境变量 TF_VAR_region
  2. terraform.tfvars
  3. *.auto.tfvars
  4. -var-file=prod.tfvars
  5. -var 'region=x'
terraform plan -var-file=prod.tfvars -var 'instance_count=5'

输出:

output "public_ips" {
  value = module.ecs.public_ips
}
terraform output
terraform output -json public_ips
terraform output -raw public_ips

5. State(最重要)

State 记录"代码"与"真实资源"的映射,丢了就等于失去管理能力。

本地 vs 远程

本地 terraform.tfstate 只适合个人试验。团队必须用远程后端:

terraform {
  backend "s3" {
    bucket         = "my-tfstate"
    key            = "prod/network/terraform.tfstate"
    region         = "cn-hangzhou"
    encrypt        = true
    dynamodb_table = "terraform-locks"     # 状态锁
  }
}

阿里云 OSS:

terraform {
  backend "oss" {
    bucket = "my-tfstate"
    prefix = "prod/network"
    region = "cn-hangzhou"
  }
}

State 含明文敏感信息(密码、密钥),bucket 必须私有 + 加密 + 开版本控制。

常用 state 命令

terraform state list                       # 所有资源
terraform state show aws_instance.web      # 单资源详情
terraform state mv A B                     # 重命名/移动(重构时保命)
terraform state rm A                       # 从 state 移除(不删真实资源)
terraform state pull > backup.tfstate      # 备份
terraform state push backup.tfstate        # 恢复(谨慎)

state rm 是"放手不管",destroy 才是"真删"。

导入已有资源

terraform import aws_instance.web i-1234567890abcdef0
# 1.5+ 可用 import block
import {
  to = aws_instance.web
  id = "i-1234567890abcdef0"
}

导入后要 plan 对照,把代码改成与现状一致(否则下次 apply 会"修正"成代码描述的样子)。

6. 常用命令

terraform plan -target=module.network      # 只针对某模块(临时排障用)
terraform apply -auto-approve              # 跳过确认(仅 CI)
terraform refresh                          # 用真实资源刷新 state
terraform apply -replace=aws_instance.web  # 强制重建某资源(替代已弃用的 taint)
terraform workspace list
terraform workspace new prod
terraform workspace select prod
terraform providers
terraform graph | dot -Tpng > graph.png    # 依赖图

-target 会破坏依赖一致性,只用于救火,之后必须跑一次完整 plan 校正。

7. 代码要点

count 与 for_each

resource "aws_instance" "web" {
  count         = var.instance_count
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.micro"
  tags = { Name = "web-${count.index}" }
}

# 推荐 for_each(增删不牵连其他资源)
resource "aws_instance" "web" {
  for_each      = toset(["web01", "web02"])
  ami           = data.aws_ami.ubuntu.id
  instance_type = "t3.micro"
  tags = { Name = each.key }
}

count 删除中间一个会导致后面全部重建;for_each 不会。生产优先 for_each。

依赖

resource "aws_eip" "web" {
  instance = aws_instance.web.id      # 隐式依赖
}

resource "aws_instance" "web" {
  depends_on = [aws_security_group.web]   # 显式依赖
}

生命周期

lifecycle {
  create_before_destroy = true
  prevent_destroy       = true      # 防误删(数据库必备)
  ignore_changes        = [tags["LastModified"]]
}

prevent_destroy 是数据库、对象存储桶的保命符。

模块

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.8.1"
  name    = "prod-vpc"
  cidr    = "10.0.0.0/16"
}

模块必须锁版本,否则某天上游更新导致 plan 全变。

8. 实战场景

8.1 安全的日常变更

git pull
terraform init -upgrade
terraform fmt -check
terraform validate
terraform plan -out=tfplan
# 人工评审 tfplan(重点看 destroy / replace)
terraform apply tfplan

plan 输出里出现 -/+ 表示"销毁重建",看到 - 要格外警惕。

8.2 资源被手工改过(漂移)

terraform plan
# 出现意外 diff → 判断:
# a) 接受手工改动:把代码改成一致
# b) 恢复代码描述:直接 apply
# c) 只是想同步 state:terraform refresh

8.3 重构目录结构不重建资源

# 把资源从 root 移到 module
terraform state mv aws_instance.web module.app.aws_instance.web
terraform plan      # 应该显示 No changes

8.4 多环境隔离

terraform workspace new prod
terraform workspace select prod
terraform apply -var-file=env/prod.tfvars

或按目录拆分(更清晰):envs/prod/、envs/dev/ 各自独立 state。

8.5 紧急阻止删除数据库

resource "aws_db_instance" "main" {
  lifecycle {
    prevent_destroy = true
  }
}

此时 destroy 会报错中止,必须先从代码移除该块。

9. 常见坑

  1. State 冲突:两人同时 apply,没有锁会互相覆盖。远程后端必须配锁(S3 + DynamoDB,OSS 自带)。
  2. State 进 Git:含明文密码,绝对不能提交。加 .gitignore。
  3. terraform destroy 无差别删除:先 plan -destroy 看清楚,重要资源加 prevent_destroy。
  4. count 下标陷阱:删中间元素导致后续重建,用 for_each。
  5. -target 遗留问题:只 apply 了部分资源,state 与代码不一致,之后要完整跑一次。
  6. Provider 版本漂移:不锁版本,init -upgrade 后 plan 大变。用 required_providers 锁住。
  7. ignore_changes 掩盖问题:过度使用会让真实漂移无法发现。
  8. 模块未锁版本:version 必须写,最好用精确版本。
  9. 敏感变量出现在 plan 输出:用 sensitive = true,但仍会存进 state。
  10. terraform.tfvars 提交泄露密钥:用环境变量或 secrets manager,.tfvars 加 gitignore。
  11. local-exec 不可控:破坏幂等,尽量用 provider 原生资源。
  12. 中国区 provider 差异:阿里云/腾讯云用各自 provider,AWS 中国区需单独 region 与 endpoint。
  13. apply 中途失败留下半成品:state 会记录已创建部分,修代码后重新 plan/apply,不要手工删资源后不管 state。

10. 速查表

terraform init [-upgrade]
terraform fmt -recursive
terraform validate
terraform plan -out=tfplan
terraform apply tfplan
terraform plan -destroy
terraform destroy

terraform output [-json|-raw NAME]
terraform state list
terraform state show <addr>
terraform state mv <src> <dst>
terraform state rm <addr>
terraform state pull > backup.tfstate

terraform import <addr> <id>
terraform apply -replace=<addr>
terraform workspace list/new/select
terraform providers

三条铁律:state 远程+加锁;plan 必评审;关键资源 prevent_destroy。

terraform —— 基础设施即代码 - 知识库 - 起航社区