Terraform 实战:用代码管理云基础设施

团队早期靠在云控制台鼠标点选创建 ECS、SLB、VPC:今天张三建一台、明天李四改个安全组,环境慢慢长得不一样,出了事没人说得清「现在线上到底长什么样」,回滚更是无从下手。基础设施即代码(IaC)就是把这一切写进版本化的配置文件,让「创建服务器」变得像「提交代码」一样可审查、可回滚、可复制。本文用最主流的 Terraform 带你入门,它与 K8s 入门:Pod、Deployment、ServiceDocker 网络模式详解:bridge/host 生产选型 的容器/K8s 编排是不同层的事——IaC 管「资源从哪来」,编排管「应用在资源上怎么跑」。

一、为什么不直接用控制台点

控制台点选有三个硬伤:环境漂移(测试和生产对不上)、不可审计(谁改了什么只有云厂商的操作日志,且语义模糊)、无法批量复用(开 10 台机器点 10 次)。IaC 把基础设施变成 .tf 文件,进 Git 管理,每次变更走 PR 评审,和写业务代码用同一套工程纪律。配合 GitHub Actions 实战:从零搭建 CI/CD 流水线 的 CI/CD,甚至可以做到「合并 PR 自动 apply」,把人工操作彻底踢出关键路径。

一个常见惨案:测试环境为了排查临时把安全组 22 端口对全网开放,事后忘了收回;生产环境照着「记忆里」的配置点了一遍,结果两边规则悄悄不一致。某天压测打到生产,才发现生产少开了一个内网端口,链路直接断。如果用 IaC,测试和生产是同一份代码加不同变量,这种「手滑漂移」从根上就不会发生——代码即真相。

二、Terraform 的三个核心概念

理解这三者就不会被术语绕晕:**HCL** 是 Terraform 的声明式配置语言,你只写「想要什么状态」,不写「怎么达到」;**Provider** 是各云厂商(阿里云/腾讯云/AWS)的插件,负责把 HCL 翻译成对应云API;**State** 是 Terraform 记录「当前真实资源与配置映射关系」的文件,它是 plan/apply 能「算出 diff」的依据。换句话说,你改了 .tf,Terraform 拿它和 state 比对,才知道要新增还是销毁哪些资源。

三、第一个例子:用代码建一台 ECS

下面这段 main.tf 声明了一台 2C4G 的 Web 服务器并开放 22 端口。注意它是「声明式」的——你不用写「先创建再绑定安全组」的步骤,只描述终态,Terraform 自己推导依赖顺序:

# main.tf:声明式描述「要一台 2C4G 的 ECS + 开放 22 端口」
terraform {
  required_providers {
    alicloud = { source = "aliyun/alicloud", version = "~> 1.0" }
  }
}
provider "alicloud" {
  region = "cn-hangzhou"
}
resource "alicloud_instance" "web" {
  instance_name = "web-01"
  instance_type = "ecs.c6.large"        # 2C4G
  image_id      = "ubuntu_22_04_x64"
  security_groups = [alicloud_security_group.ssh.id]
}
resource "alicloud_security_group_rule" "ssh" {
  type = "ingress"; port_range = "22/22"; protocol = "tcp"; cidr_ip = "0.0.0.0/0"
}

相比控制台十几步点击,这一份文件既能复用(改个变量就是另一台),也能进 Git 留痕。安全组规则这类边界配置,建议和 服务器安全加固:SSH / 防火墙 / Fail2ban 实战 的 SSH/防火墙加固策略一起评审,别把 0.0.0.0/0 的敏感端口随手打开。

四、标准工作流:plan 再 apply

Terraform 最安全的设计是「先预览、再执行」。plan 会告诉你这次会动哪些资源但不真正改;确认无误才 apply;真出乱子 destroy 一键回收,杜绝「忘了删机器默默扣费」:

# 标准工作流:先看计划,再执行,出错可销毁回滚
terraform init      # 拉取 provider 插件、初始化
terraform plan      # 预览「将创建/修改/删除哪些资源」,不落地
terraform apply     # 确认无误后真正创建(交互输入 yes)
terraform destroy   # 一键回收全部资源,避免控制台忘了删而持续计费

# 团队环境:把 state 存远端(OSS/S3),并开状态锁防并发覆盖
terraform {
  backend "oss" { bucket = "tf-state-xxx"; key = "prod/web.tfstate"; }
}
命令作用是否改动真实资源
init拉取 provider、初始化工作目录
plan预览将要创建/修改/删除的资源
apply按 plan 真正落地变更
destroy删除 state 中管理的全部资源是(回收)

团队场景一定要把 state 放远端存储(如 OSS/S3)并开启状态锁,否则两个人同时 apply 会互相覆盖、甚至把对方刚建的机器误删。

两个新手最易踩的坑:一是把 state 文件随手提交进 Git——state 里可能含资源 ID、甚至明文密钥,既泄露又因本地 state 和远端不一致引发「幽灵变更」;二是用 -auto-approve 跳过 plan 直接 apply,少了那次人工确认,一次手误就能把数据库实例删了。正确做法是 state 存远端并加密、apply 前必须过 plan 评审。另外,对已经在控制台手动建好的资源,可用 terraform import 把它「收编」进 state,逐步把存量环境也纳入代码管理,而不是推倒重来。

五、从「一台」到「一套」:模块与环境

真正省时间的是抽象。把「一台标准 Web 机」封装成 module,之后开 10 台、开不同环境都只是改几个参数:

# 用 module 把「一台标准 Web 机」抽象成可复用单元
module "web" {
  source      = "./modules/ec2"   # 或远程 git 仓库
  instance_type = "ecs.c6.large"
  env         = "prod"            # 同一份代码,改 env 即切环境
}
# 或用 workspace 区分 dev / staging / prod,共享同一套配置
terraform workspace select prod

module 解决「复用」,workspace 解决「多环境隔离」——dev/staging/prod 共用一份配置、只换变量,既保证一致,又互不干扰。这正是 IaC 相比控制台点选最大的复利:前期写一次,后面每次扩容都是几行改动。

六、它和 K8s/Docker 是什么关系

常有人混淆:IaC(Terraform)负责「把虚拟机、网络、负载均衡这些底层资源造出来」;Docker 网络模式详解:bridge/host 生产选型 负责「容器怎么连网」;K8s 入门:Pod、Deployment、Service 负责「应用在集群里怎么调度」。三者是上下游接力,不是替代。典型链路是 Terraform 造好 K8s 集群所在的节点和网络,再交给 K8s 编排业务 Pod。前端流量进来则由 Nginx 反向代理完整配置:负载均衡 + HTTPS 这类反向代理做负载均衡与 TLS 终结。

小结

基础设施即代码不是炫技,而是把「运维操作」变成「工程活动」:可版本化、可评审、可回滚、可批量。Terraform 用 HCL 声明终态、用 Provider 对接各家云、用 State 计算 diff,配合 plan→apply→destroy 的安全工作流和 module/workspace 的复用抽象,让「开机器」和「写代码」一样可靠。把它和 GitHub Actions 实战:从零搭建 CI/CD 流水线 的 CI/CD 串起来,你就能做到合并即交付、回滚有凭据——这才是现代 DevOps 该有的基础设施底座。

上一篇 在线改大表实战:pt-osc 与 gh-ost 零停机
下一篇 性能压测工具选型实战:k6 vs JMeter vs Locust