Provider 与多环境
本章目标:理解 provider 怎么连云、怎么锁版本、怎么用别名操作多个账号/地域;并掌握两套主流的「多套环境(staging / prod)」组织方式。
完成后你应能独立搭出「同一套代码,多处部署」的骨架。
1. provider 是什么
provider 是 Terraform 与某个云平台 / API 之间的适配器插件。配置里写 required_providers 声明「要用谁、哪个版本」,再用 provider 块配置连接细节:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.40"
}
random = {
source = "hashicorp/random"
version = "~> 3.6"
}
}
}
provider "aws" {
region = "ap-guangzhou"
# 凭证默认走环境变量 / 共享配置 ~/.aws/credentials,不要硬编码
}
access_key / secret_key 绝不能出现在 .tf 里(会被提交进 Git,也可能落进 state)。让 provider 走环境变量 AWS_*、EC2 实例角色、或 CI 的 OIDC 临时凭据。详见第 9 章。
1.1 版本锁定
required_providers 里的 version 和 required_version 一样重要:provider 升级可能改资源 schema,导致 state 不兼容。生产仓库必须锁定,且升级要走评审。
terraform init -upgrade=false # 不主动升级 provider(默认行为,显式写出更稳)
terraform init -upgrade # 仅在有计划升级时才用
2. 多个 provider:别名(alias)
同一套代码要同时操作多个地域、多个账号时,用 alias:
provider "aws" {
region = "ap-guangzhou"
}
provider "aws" {
alias = "us_east"
region = "na-ashburn"
}
resource "aws_s3_bucket" "primary" {
# 默认用主 provider(ap-guangzhou)
bucket = "app-primary"
}
resource "aws_s3_bucket" "dr" {
provider = aws.us_east # 显式指定别名
bucket = "app-dr"
}
规则:
- 无
alias的是默认 provider;有alias的需在每个资源用provider = aws.<alias>显式引用。 - 模块里若要支持别名透传,模块声明时需写
providers参数把外部别名传入(进阶,先用平铺写法即可)。
3. 多环境:两套主流方案
「staging 和 prod 用同一套代码、不同参数」是日常刚需。两条路:
3.1 方案一:terraform workspace(轻量,但隔离弱)
workspace 把同一份代码的 state 分成不同的「命名空间」,state 文件名带前缀:
terraform workspace list
terraform workspace new staging
terraform workspace new prod
terraform workspace select staging
terraform apply -var-file="staging.tfvars"
代码里可读当前 workspace 名,做差异化:
locals {
env = terraform.workspace # "staging" / "prod"
}
resource "aws_instance" "web" {
count = terraform.workspace == "prod" ? 3 : 1
# ...
}
| 优点 | 缺点 |
|---|---|
| 一套代码、切换快 | 共用同一个 backend,隔离不彻底 |
| 概念简单 | 难以给不同环境配不同 provider / 不同账号 |
| 适合个人/小团队 | workspace 名拼错容易误用到别的环境 state |
workspace 本质只是 state 文件名加了前缀(env:/staging/...),后端权限、凭证、provider 配置仍是同一套。要严格隔离账号(如 staging 与 prod 不同云账号、不同 IAM),请用方案二。
3.2 方案二:目录分流(强隔离,推荐生产)
每个环境一个目录,各自有独立的 backend 配置和独立 root module,共享的只是 modules/:
terraform/
├── modules/
│ └── vpc/
├── envs/
│ ├── staging/
│ │ ├── main.tf # module "vpc" { source = "../../modules/vpc" }
│ │ ├── backend.tf # staging 专属 state bucket
│ │ └── staging.tfvars
│ └── prod/
│ ├── main.tf
│ ├── backend.tf # prod 专属 state bucket(独立账号/权限)
│ └── prod.tfvars
envs/staging/main.tf:
module "vpc" {
source = "../../modules/vpc"
cidr_block = "10.10.0.0/16"
env = "staging"
}
envs/prod/main.tf 调同一个模块、传不同参数;两者的 state 完全分开,权限也可分账号管控。
3.3 tfvars:把环境差异集中在变量文件
不论哪种方案,环境差异都应收敛到 *.tfvars,而不是散落在 .tf 里:
| 文件 | 加载方式 | 说明 |
|---|---|---|
terraform.tfvars | 自动加载 | 默认值,通常不放环境敏感差异 |
staging.tfvars | terraform apply -var-file=staging.tfvars | 环境特有 |
命令行 -var | 单次覆盖 | 临时调试 |
terraform -chdir=envs/prod apply -var-file=prod.tfvars
4. 命名与 tagging 约定
环境再多,资源也要可识别、可归因。强制 tags 是低成本高回报的规范:
variable "env" { type = string }
locals {
mandatory_tags = {
Environment = var.env
ManagedBy = "terraform"
Owner = "platform-team"
Project = "rootwiki"
}
}
resource "aws_instance" "web" {
tags = merge(local.mandatory_tags, { Service = "web" })
}
约定要点:
- 每个资源带
Environment/Owner/Project,用于成本核算与责任追溯。 - 资源名加环境前缀(
stg-*/prd-*),避免跨环境撞名。 - 账号、地域、网段等「环境常量」只出现在
tfvars/ backend,不写死在模块里。
5. 练习与验收
练习 A(workspace)
- 对一个简单模块
terraform workspace new staging与new prod - 用
terraform.workspace控制count,staging 1 台、prod 3 台 terraform workspace select切换并分别plan,确认 state 互不干扰
练习 B(目录分流)
- 按方案二搭
envs/staging与envs/prod,各自backend.tf - 用
module调用同一份modules/vpc,传不同cidr_block - 分别
init两个目录,确认 state 落到不同 key
验收清单
- 会用
required_providers锁定 provider 版本 - 会用
alias操作多地域 / 多账号 - 能说清 workspace 与目录分流的隔离差异,并按场景选型
- 能用
tfvars收敛环境差异 - 资源带统一 tagging,便于归因
下一章:State 管理 —— 把状态从本地搬上远程 backend,解决共享、加锁与敏感信息。