跳到主要内容

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 的坑

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.tfvarsterraform 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)

  1. 对一个简单模块 terraform workspace new staging 与 new prod
  2. 用 terraform.workspace 控制 count,staging 1 台、prod 3 台
  3. terraform workspace select 切换并分别 plan,确认 state 互不干扰

练习 B(目录分流)

  1. 按方案二搭 envs/staging 与 envs/prod,各自 backend.tf
  2. 用 module 调用同一份 modules/vpc,传不同 cidr_block
  3. 分别 init 两个目录,确认 state 落到不同 key

验收清单

  • 会用 required_providers 锁定 provider 版本
  • 会用 alias 操作多地域 / 多账号
  • 能说清 workspace 与目录分流的隔离差异,并按场景选型
  • 能用 tfvars 收敛环境差异
  • 资源带统一 tagging,便于归因

下一章:State 管理 —— 把状态从本地搬上远程 backend,解决共享、加锁与敏感信息。