Terraform 实战技巧:5个提效方法

devops进阶7 分钟阅读2026/9/16

Terraform 实战技巧:5 个提效方法

从一个惨痛的下午说起

去年我们团队接手了一个 60 多个模块的基础设施项目,光是 terraform apply 一次就要跑 25 分钟。有一次同事在本地跑了个 plan 没注意 state 文件是旧的,直接把一个生产 RDS 实例标记为要替换——幸好我们在 apply 前多看了一眼。那天下午我痛定思痛,花了两周时间把团队的工作流重新梳理了一遍。下面这 5 个技巧,是我实际用下来提效最明显的,每一条都有真实的坑做背书。

技巧一:用 targeted apply 处理大项目,但要小心

我们的项目一次 apply 要 25 分钟,但大部分时候我们只改了一两个模块。Terraform 提供了 -target 参数:

terraform apply -target=module.ecs_web.aws_launch_pattern.web

配合 terraform graph 或者直接看 plan 输出,可以只重建依赖链上的一小部分资源。我们 25 分钟的 apply,用 target 之后经常 3 分钟搞定。

但这里有个我踩过的坑-target 会让 Terraform 跳过依赖检查。有一次我用 target 更新了一个安全组,忘了它上游还有个引用了它 CIDR 的 NACL,结果流量直接被打断了。所以我的规矩是:

  • -target 只用于日常迭代和调试
  • 任何合并到 main 分支之前的最终验证,必须跑一次不带 target 的完整 plan
  • CI 里加一条注释提醒:如果最近的 commit 里有 -target 的痕迹,强制完整 plan

技巧二:用 validate + fmt + lint 组合拳,在 CI 里挡住 90% 的问题

本地手滑太正常了。我们现在的流程是三个工具串起来:

terraform fmt -recursive -check
terraform validate
tflint --recursive

tflint 是真正值得装的。它能查出 validate 查不出来的东西,比如实例类型写错了:

# tflint 会报错:us-west-2 不存在 db.r5.xlarge 这个配置(举例)
resource "aws_db_instance" "main" {
  instance_class = "db.r5.xlagre"  # 手滑
}

terraform validate 对这个完全没意见,直到你 apply 才炸。我们把这三条命令放进 pre-commit hook 和 CI 里,第一天就拦下了一个把 ap-northeast-1 写成 ap-northeast-la 的提交。

技巧三:plan 文件 + 自动化审批,告别“plan 看完 apply 变了”

这是我们踩过最阴的坑。你跑了 terraform plan,看了输出没问题,然后跑 terraform apply——但在这两步之间,有人改了配置或者 state 被更新了,apply 时重新计算的 plan 和你看的那个不是同一个

解法是把 plan 固化成文件:

terraform plan -out=tfplan
terraform apply tfplan

第二个命令会直接应用你刚看的那个 plan,不再重新计算。配合 CI 的话更香:MR 流水线里跑 plan 并把输出贴到 merge request 评论里,审批人看的就是将要执行的 plan,批准后 apply 用同一个文件。

一个小提示:plan 文件里可能包含敏感信息(密码、密钥),不要提交到 git,用完即删或者放在加密的 artifact 存储里。

技巧四:用 moved 块替代手改 state

重构的时候经常需要把资源改名或者挪模块。老办法是 terraform state mv,但这是纯手工操作,不进版本控制,换个环境就得再来一遍。

现在直接写代码:

moved {
  from = aws_instance.web
  to   = module.web.aws_instance.this
}

跑 plan 时你会看到 terraform 报告这些资源将被 move 而不是 destroy/create——这一点非常关键,意味着数据库不会重建。我们上个月把一个单体配置拆成三个模块,靠的就是几十个 moved 块,全程零停机,而且迁移历史清清楚楚地留在 git 里。

同理,import 块(Terraform 1.5+)也很好用:

import {
  to = aws_s3_bucket.legacy_logs
  id = "company-legacy-logs-2021"
}

terraform plan 会生成导入配置,不用再手敲 terraform import 命令。

技巧五:用 workspace 还是多目录?想清楚再选

新手最容易纠结这个。我两个都试过:

workspaces 适合环境差异小的场景:

terraform workspace new staging
terraform workspace select prod

问题是代码里会散布 terraform.workspace == "prod" ? ... : ... 这种三元表达式,环境多了之后可读性雪崩。我们曾经有 5 个环境,每个环境还有细微差异,最后这个方案维护不动了。

多目录 + 共享模块是我们现在的做法:

environments/
  ├── staging/
  │   └── main.tf   # 引用 modules/
  └── prod/
      └── main.tf
modules/
  └── vpc/

每个环境一个独立的 state,爆炸半径小,prod 永远不会被 staging 的误操作波及。代价是有些配置要复制,但这点重复换来隔离性,值。

最后的实话实说

几个诚实的提醒:

  1. -target 是拐杖不是路,长期依赖它说明你的模块拆分有问题
  2. plan 文件方案在 monorepo 很大时会让 CI 变慢,我们目前用缓存 .terraform 目录 + provider 缓存缓解,但 plan 时间还是超过了 10 分钟
  3. moved 块在 1.1 之前的版本不可用,如果你们还在用老版本(我见过不少公司卡在 0.12),只能继续 state mv
  4. tflint 规则要按团队口味裁剪,全开的话告警太多,大家会开始忽略它

这些技巧没有一个是黑科技,全是笨办法,但基础设施这东西,笨办法往往就是最可靠的办法。如果你有 5 分钟,先把 -out=tfplan 这个习惯养起来——它是这五条里投入产出比最高的。

相关 Agent

D

Docker

Docker 是一个容器化平台,允许开发者将应用及其依赖项打包成轻量级、可移植的容器。它简化了跨环境的开发、测试和部署过程。该描述强调它最适合容器化和开发环境,提供免费方案和团队方案,团队方案从每用户每月5美元起。

了解更多 →