安装与工作区规范
本章目标:在本地装好 Terraform(或它的开源分支 OpenTofu),把版本锁死,并搭好后续章节共用的仓库骨架与 backend 选型心智模型。
完成后你应具备:
terraform version显示锁定版本(如1.9.x)- 仓库里有一份
.terraform-version与required_version双重锁定 - 跑通
terraform init/fmt/validate - 清楚本地 state 与远程 backend 的取舍
1. Terraform 还是 OpenTofu
2023 年起 HashiCorp 将 Terraform 的许可证改为 BSL(不再宽松开源),社区随即 fork 出 OpenTofu(Linux 基金会托管,真正的 MPL 开源)。两者在 HCL 语法与命令上几乎完全兼容,本系列示例以 Terraform 命令为准,把 terraform 换成 tofu 通常也能直接跑。
仅少数命令/特性分叉(如 OpenTofu 更早支持 import 块、for_each 的 import、状态加密等)。涉及差异处本系列会标注。日常 init / plan / apply / fmt / validate 完全一致。
同一个仓库、同一个环境,所有人(含 CI)必须用同一版本。否则 required_version 一旦约束到某小版本,版本不符的人连 plan 都跑不起来——这正是下一节要解决的。
2. 安装
2.1 macOS(推荐 Homebrew)
# Terraform
brew tap hashicorp/tap
brew install hashicorp/tap/terraform
# 或 OpenTofu
brew install opentofu
terraform version
# OpenTofu v1.8.x / Terraform v1.9.x
2.2 Linux(官方 zip 包,最通用)
TERRAFORM_VERSION=1.9.5
curl -fsSL "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip" -o /tmp/tf.zip
sudo unzip -o /tmp/tf.zip -d /usr/local/bin/
terraform version
部分发行版自带极老的 terraform(甚至 0.12),语法天差地别。生产仓库一律用官方二进制 + 版本锁定,别依赖系统版本。
2.3 用 tfenv 管多版本(强烈推荐)
和 rbenv / nvm 一个思路,按项目切换:
# macOS
brew install tfenv
tfenv install 1.9.5
tfenv use 1.9.5
# 在仓库根放一份锁定文件,进入目录自动切换
echo "1.9.5" > .terraform-version
tfenv use # 读取 .terraform-version
3. 版本锁定:两层保险
只靠口头约定版本迟早翻车。用两层锁定:
3.1 仓库级:.terraform-version
给 tfenv / tofuenv 用,控制二进制本身的版本(见上)。
3.2 代码级:required_version
写在 terraform {} 块里,控制配置能接受的解释器版本:
terraform {
required_version = ">= 1.5.0, < 1.10.0" # 允许 1.5~1.9,封住 1.10 的潜在破坏
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.40" # 允许 >= 5.40 且 < 6.0;若只允许 5.40.x,写 ~> 5.40.0
}
}
}
| 约束写法 | 含义 |
|---|---|
>= 1.9.0 | 不低于 1.9.0 |
~> 1.9.0 | 1.9.x,允许补丁/小版本,不跳主版本 |
~> 5.40 | >= 5.40, < 6.0;两位版本号会允许同一主版本内的后续 minor |
~> 5.40.0 | 5.40.x;三位版本号才只允许补丁升级 |
>= 1.5.0, < 1.10.0 | 区间,最稳 |
1.9.5 | 精确锁定(不推荐,CI 升级麻烦) |
required_providers 里的 version 同样关键。Provider 升级可能改资源 schema,导致 state 不兼容。provider 版本必须和 terraform 版本一起进版本控制,并用 -upgrade=false(默认)避免误升。
4. 仓库骨架
推荐一个「模块与环境分离」的结构,后续章节会持续用到:
terraform/
├── modules/ # 可复用模块(与具体环境解耦)
│ ├── vpc/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── outputs.tf
│ │ └── versions.tf
│ └── eks/
├── envs/ # 每个环境一套 root module
│ ├── staging/
│ │ ├── main.tf # 调用 ../../modules/*
│ │ ├── backend.tf # 该环境的远程 state 配置
│ │ ├── variables.tf
│ │ └── staging.tfvars # 环境特有变量
│ └── prod/
├── _global/ # 跨环境资源(IAM、Route53、组织策略)
└── .terraform-version
要点:
- 模块放
modules/,只描述「如何造一个东西」,不含账号/地域等环境信息。 - 环境放
envs/<env>/,用module调用模块,并把账号、地域、规格通过tfvars注入。 - 这套结构天然支持「同一套模块,多套环境各跑各的 state」。第 4、6 章会展开。
5. 第一个 init / fmt / validate
进入任一 envs/* 目录,先 init(下载 provider、连接 backend):
cd terraform/envs/staging
terraform init
# Initializing the backend...
# Terraform has been successfully initialized!
常用三板斧:
terraform fmt -recursive # 格式化全部 .tf(提交前必跑)
terraform validate # 校验语法与变量约束(不需要真实凭证)
terraform plan # 真正连云、算差异(需要凭证)
| 命令 | 是否连云 | 是否需要凭证 | 用途 |
|---|---|---|---|
terraform fmt | 否 | 否 | 统一代码风格 |
terraform validate | 否 | 否 | 静态校验 |
terraform plan | 是 | 是 | 预览变更 |
terraform apply | 是 | 是 | 执行变更 |
validate 不连云、不花钱,适合在每次提交时秒级拦住低级语法错误;plan 才真正连云。CI 顺序: fmt --check → validate → init -input=false → plan。
6. backend 选型概览(详细见第 5 章)
terraform init 时若没配 backend,state 默认落在本地 .terraform.tfstate——只适合你一个人、一台电脑的玩具。团队协作必须上远程 backend,它解决三件事:
| 诉求 | 本地 state | 远程 backend |
|---|---|---|
| 多人共享同一份真实状态 | ❌ | ✅ |
| 并发写锁(防止两人同时 apply 互相覆盖) | ❌ | ✅(如 S3+DynamoDB、GCS、Azure Blob) |
| 状态加密、版本、备份 | ❌ | ✅ |
常见 backend 选项:
- AWS S3 + DynamoDB:最主流,S3 存 state、DynamoDB 加锁,均支持服务端加密
- GCS(Google Cloud Storage):原生状态锁
- Azure Blob Storage:原生状态锁
- Terraform Cloud / Enterprise:托管 backend + 远程执行 + 策略
- Consul / etcd:自建场景
选型先有个概念即可,第 5 章会把 S3 这套完整配通。
7. 练习与验收
练习 A
- 用
tfenv安装并锁定1.9.x,确认terraform version输出符合预期 - 新建仓库骨架(上面第 4 节),在
envs/staging/放一个最小versions.tf - 跑
terraform fmt -check与terraform validate均通过
练习 B
- 故意把
required_version写低(如= 1.0.0),观察terraform validate如何拦截 - 改回正确约束
验收清单
-
terraform version显示锁定版本 - 仓库含
.terraform-version与required_version双重锁定 -
terraform fmt -check通过(或已格式化) -
terraform validate通过 - 已清楚本地 vs 远程 backend 的取舍
下一章:HCL 基础 —— 真正动手写 resource / variable / output,并用 count / for_each 批量生成资源。