手工在控制台上点出几十台服务器、几张网卡和一堆安全组,是运维和基础设施团队最痛苦的经历之一:环境不可复现、配置漂移严重、变更无法追溯。Terraform 用一句“写代码就是建基础设施”的思路解决了这个问题。它允许你用 HCL 语言声明集群的期望状态,由引擎自动计算差异并执行变更,让集群的创建、扩容、升级变成一次代码提交。

Terraform 的核心工作机制
理解 Terraform 的关键在于三个概念:配置、状态与执行计划。配置文件描述的是你想要的基础设施样子,状态文件(terraform.tfstate)记录的是 Terraform 认为当前实际存在的资源,而执行计划则是两者之间的差异计算结果。每次执行 terraform apply 时,Terraform 并不是盲目重建资源,而是先读取状态文件,再向云平台 API 查询真实资源,比对后生成一份计划:哪些资源要新建、哪些要修改、哪些要销毁。
另一个重要概念是 Provider。Terraform 本身不直接对接任何云平台,它通过插件式的 Provider 与 AWS、阿里云、Azure、Kubernetes、VMware 等上百种平台通信。你在配置中声明 required_providers,Terraform 会自动下载对应插件,之后所有资源的创建、查询、删除都由该插件完成。这种架构让同一套 HCL 语法可以管理几乎任何基础设施。
一个最小的 Provider 声明示例如下:
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "cn-north-1"
}值得注意的是版本锁定。生产环境中务必用 ~> 或精确版本号约束 Provider 版本,否则一次无意的插件升级可能带来行为变更,导致计划结果与预期不符。
用模块化封装集群资源
直接把所有资源写在一个 main.tf 里,在集群规模上去之后会迅速失控。Terraform 的 Module 机制允许你把一组资源打包成可复用的组件,比如一个“节点组”模块内部包含启动模板、自动扩缩组、安全组、IAM 角色,外部只需要传入实例规格、节点数量等少量参数。
模块的典型目录结构如下:
modules/
node-group/
main.tf # 资源定义
variables.tf # 输入变量声明
outputs.tf # 输出值
envs/
prod/
main.tf # 调用模块
backend.tf # 状态存储配置
staging/
main.tf在环境目录中调用模块的方式非常直观:
module "worker_nodes" {
source = "../../modules/node-group"
cluster_name = "prod-cluster"
instance_type = "c6i.2xlarge"
node_count = 6
subnet_ids = var.private_subnet_ids
}这种结构的好处是环境间天然隔离:生产与测试各自持有独立的状态文件,同一个模块的不同版本可以分别发布到不同环境,配合 Git 分支管理就能实现“代码合并即环境变更”的流水线。此外,建议把团队沉淀的通用模块发布到私有 Registry,统一版本管理,避免每个人复制一份改得面目全非。
编排 Kubernetes 集群的实战示例
下面以在 AWS 上编排一个 EKS 集群为例,展示从零到可用的完整流程。核心资源包括托管控制面、托管节点组和网络配套:
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "20.8.5"
cluster_name = "prod-eks"
cluster_version = "1.29"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
eks_managed_node_groups = {
default = {
instance_types = ["c6i.2xlarge"]
min_size = 3
max_size = 10
desired_size = 3
}
}
}这里直接使用了社区维护的官方模块,它内部封装了十几项资源与 IAM 权限配置,比自己从零写 IAM 角色绑定可靠得多。写好配置后,标准工作流是三步:terraform init 初始化并下载依赖,terraform plan 预览变更,terraform apply 真正执行。在 CI/CD 环境中,强烈建议把 plan 的输出结果作为合并请求的评论展示出来,让变更在合入主干之前就完成人工审查。
集群建好之后往往还需要部署工作负载。Terraform 的 kubernetes 与 helm Provider 可以在同一份配置里继续编排命名空间、Deployment、Ingress 等对象,实现“基础设施与应用底座一次交付”。不过要注意边界:应用层的频繁迭代更适合交给 ArgoCD 这类 GitOps 工具,Terraform 只负责相对稳定的资源层,避免每次发布代码都触发基础设施计划。
状态管理与团队协作的最佳实践
状态文件是 Terraform 最脆弱也最关键的资产。它默认存放在本地磁盘,一旦团队成员各自维护一份副本,就会出现互相覆盖、资源漂移的灾难。工程化的做法是把状态推送到远程后端,并启用锁与版本化:
terraform {
backend "s3" {
bucket = "mycompany-tfstate"
key = "prod/eks.tfstate"
region = "cn-north-1"
dynamodb_table = "tf-locks"
encrypt = true
}
}这样每次 apply 前 Terraform 会先通过 DynamoDB 抢锁,保证同一时刻只有一个人在变更环境;S3 的版本历史还能在误操作后回滚状态文件。状态中包含敏感信息,务必开启加密并严格限制桶的访问权限。
除此之外还有几条值得坚持的原则:第一,永远不要手改 tfstate 文件,出问题优先用 terraform import 把存量资源导入管理,或用 terraform state rm 移除不再纳管的资源;第二,善用 lifecycle 块控制行为,例如给节点组加上 create_before_destroy 避免更新时出现服务空窗;第三,把所有可变参数抽到 variables 与 tfvars 文件中,敏感凭证通过环境变量或 Vault 注入,绝不写进仓库;第四,定期执行 terraform plan 做漂移检测,及时发现有人绕过流程在控制台上改了配置。
当集群规模继续扩大、多团队共享基础设施时,可以进一步引入 Workspaces 或多状态目录拆分,把网络层、集群层、应用层切分为独立的执行单元,通过 remote state 互相引用输出值。分层的粒度没有标准答案,核心判断依据是变更频率与爆炸半径:变更越频繁、影响面越大的资源,越应该独立成层并单独控制权限。掌握这些方法后,Terraform 就不只是个建资源的工具,而是一套让基础设施具备版本控制、代码审查和可审计能力的完整工程体系。