Providers — CodeRoadMapProviders 配置与版本锁定
这一课你将能做到
- 说清楚 Terraform Core 与 Provider 的分工,正确声明 provider 来源和版本约束
- 使用 provider 别名在一份配置里同时操作多个 AWS 区域
- 读懂并维护
.terraform.lock.hcl 锁文件,解释为什么团队必须提交它
为什么要学
Terraform 本体其实「什么云都不懂」,它只负责解析 HCL、画依赖图、管理 state。真正调用 AWS API 建 EC2、调阿里云 API 建 ECS 的,是一个个 provider 插件。本站的基础设施横跨 AWS 东京区域(主业务)和 AWS 美东区域(异地备份桶、CloudFront 证书必须在 us-east-1),还可能要调 Cloudflare 管 DNS——这些全靠 provider 配置串起来。
版本问题是初学者和团队协作最容易踩的坑:你本机上周 apply 成功的配置,同事今天拉下来报一堆属性不存在,往往只是 provider 小版本差异。理解版本约束符号和锁文件,才能让「在谁的机器上跑结果都一致」。
先建立图像
┌────────────────────────────┐
│ Terraform Core │ 解析 HCL、依赖图、state、plan
└─────────────┬──────────────┘
│ gRPC 插件协议
┌──────────┼────────────┐
▼ ▼ ▼
┌──────┐ ┌──────┐ ┌──────────┐
│ aws │ │random│ │cloudflare│ ← 每个 provider 独立版本号
│5.40 │ │3.6 │ │4.20 │
└──┬───┘ └──────┘ └──────────┘
▼
AWS API(真实的 CreateVpc / RunInstances …)
provider 的配置链路:
required_providers(声明「我要谁、什么版本」)
↓ terraform init 下载
provider 块(配置「怎么连、连哪个区域/账号」)
↓
resource / data 块使用
跟着做
步骤 1:声明 required_providers
新建 providers-demo/versions.tf:
terraform {
required_version = ">= 1.7.0"
required_providers {
aws = {
source = "hashicorp/aws" # 注册中心命名空间/类型
version = "~> 5.40" # 允许 5.40 ~ 5.x(不含 6.0)
}
random = {
source = "hashicorp/random"
version = "~> 3.6"
}
}
}
版本约束符号速查:
| 写法 | 含义 |
|---|
>= 5.0 | 5.0 及以上任意版本 |
~> 5.40.0 | 允许 5.40.x,不允许 5.41 |
~> 5.40 | 允许 5.40、5.41……但不允许 6.0 |
= 5.40.0 | 精确锁定(不推荐写死在配置里) |
实践建议:配置里写 ~> 主版本.次版本 给出兼容范围,精确版本交给锁文件。
步骤 2:配置默认 provider
provider "aws" {
region = "ap-northeast-1"
default_tags {
tags = {
Project = "coderoadmap"
}
}
}
AWS provider 的凭证查找顺序:环境变量( 等)→ 共享配置文件(,可用 指定)→ 容器/EC2 实例角色。所以配置里。
AWS_ACCESS_KEY_ID
~/.aws/credentials
profile
通常不写、也绝不应该写 access_key / secret_key
步骤 3:用别名操作第二区域
CloudFront 的 ACM 证书必须签发在 us-east-1,而本站主业务在东京。用 provider 别名解决:
provider "aws" {
region = "us-east-1"
alias = "virginia"
}
resource "aws_acm_certificate" "cdn" {
provider = aws.virginia # 指定这个资源走别名 provider
domain_name = "cdn.coderoadmap.example"
validation_method = "DNS"
lifecycle { create_before_destroy = true }
}
规则:没有 alias 的 provider 是该类型的默认实例;带 alias 的必须在资源上显式用 provider = aws.virginia 选择,否则资源走默认区域。模块内使用多 provider 时,需要在模块块里通过 providers = { aws = aws, aws.virginia = aws.virginia } 显式传入(第 8 课详解)。
步骤 4:init 与锁文件
观察新生成的 .terraform.lock.hcl:
provider "registry.terraform.io/hashicorp/aws" {
version = "5.40.1"
hashes = [
"h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4Ocec......=",
"zh:63937135ded40677d00c56e2f505b9cd...........",
]
}
锁文件记录两件事:init 实际选中的精确版本和该版本的加密哈希。之后任何人 init,即使 5.41 已发布,下载的仍是 5.40.1;哈希不符(被篡改或镜像有问题)直接拒绝。
terraform init -upgrade # 在约束范围内刷新锁
git diff .terraform.lock.hcl # 评审版本变化
terraform plan # 确认升级没有带来意外变更
.terraform.lock.hcl 必须提交 Git;而 .terraform/ 目录(插件实体)不提交,写进 .gitignore。
步骤 5:validate 与 provider 文档查法
terraform fmt # 自动格式化,团队统一风格
terraform validate # 不连云,检查语法和参数名/类型
讲清楚要点
- provider 是「能力包」,按平台独立发布:aws、azurerm、google、kubernetes、cloudflare 各自版本号独立,互不影响。
- 三段式 source:
hashicorp/aws 隐含完整地址 registry.terraform.io/hashicorp/aws;私有 registry 或自建镜像要写全地址。
- 同一 provider 多实例靠 alias:多区域、多账号(跨账号 AssumeRole)都是写多个 provider 块加别名,资源上选择。
- 锁文件管「可复现」,约束管「兼容边界」:约束是你给的允许范围,锁是实际落地版本;少了任何一个,要么升级失控,要么版本漂移。
- provider 版本升级是变更,要走评审:provider 大版本可能带来属性重命名、默认行为变化(AWS provider 4→5 就重构了 S3 桶的独立子资源),升级后必须重新 plan。
你可能会懵的地方
-
为什么资源名里要带 aws_ 前缀?
资源名前缀标明它属于哪个 provider,aws_instance 属于 hashicorp/aws。Terraform 据此找到对应插件,也避免不同 provider 的同名资源冲突。
-
terraform init 每次都要跑吗?
只在第一次、改了 required_providers、增删模块、换后端时需要重跑;日常 plan/apply 不需要。加了新 provider 没 init 会报 Missing required provider。
-
provider 块里能放 assume_role 吗?多账号怎么管?
可以:assume_role { role_arn = "arn:aws:iam::123456789012:role/tf-deploy" }。多账号典型写法是每个账号一个别名 provider,共享同一份凭证、落地时 AssumeRole 到目标账号。
-
锁文件冲突(Git merge conflict)怎么处理?
最稳妥:删掉冲突标记后重新 terraform init,让 Terraform 重新计算锁,再提交。不要手工拼哈希。
-
provider 下载太慢怎么办?
用网络镜像:在 ~/.terraformrc 配置 provider_installation { network_mirror { url = "https://mirror.example/terraform-providers/" } include = ["registry.terraform.io/*/*"] },国内有多家云厂商提供公开镜像。
错法对照
| 错法 | 问题 | 正解 |
|---|
| provider 块里硬编码 access_key/secret_key | 密钥进 Git,泄露 | 靠环境变量/profile/实例角色,代码零密钥 |
.terraform.lock.hcl 不提交 | 各人 init 版本不一,plan 结果不一致 | 锁文件入库,插件目录忽略 |
版本写 >= 5.0 无上限 | 大版本升级可能直接破坏配置 | 用 ~> 锁主版本,升级显式执行 |
给资源忘了 provider = aws.virginia | 资源被建到默认区域 | 别名 provider 必须显式选择,validate 检查 |
| init 报锁哈希不符时删锁重来 | 可能放过被篡改插件 | 先查镜像/代理,确认来源可信再更新 |
卡点急救
Failed to query available provider packages(404/超时):网络不通 registry,先确认代理或配置 network_mirror;source 地址拼写错误(漏了命名空间)也会报这个。
Provider configuration not present / Configuration for aws.virginia is not expected:前者是资源引用了未声明的别名,后者是声明了别名但全配置无人使用(可加 # tflint 例外或删除多余块)。
- 升级 provider 后 plan 出现大量意外变更:对照该 provider 的 Upgrade Guide(registry 文档顶部有链接),AWS provider 常见的是 S3/S3ACL 拆分、默认标签行为变化;必要时回退锁文件版本再评估。
面试常问
-
Terraform core 和 provider 是什么关系?
Core 负责配置解析、依赖图、state 与 plan 编排,provider 以插件形式实现对具体平台 API 的 CRUD;二者通过 RPC 通信、独立发版,所以 Terraform 能跨几百个平台。
-
.terraform.lock.hcl 的作用?要不要提交?
锁定 provider 精确版本和哈希,保证团队与 CI 拿到完全一致的插件、防篡改;必须提交。升级走 init -upgrade 并评审。
-
一份 Terraform 怎么管多个 AWS 区域?
声明多个 provider 块,默认块 + alias 块,资源用 provider 元参数选择;如 CloudFront 证书放 us-east-1、主业务放 ap-northeast-1。
自测三问
~> 5.40 和 ~> 5.40.0 允许的版本范围分别是什么?
- 本站主业务在东京、CDN 证书在美东,配置上具体怎么写?
- 同事说「我这边 plan 和你的不一样」,除了变量差异,你会优先检查哪两个文件?
小结一句话
Provider 是 Terraform 对接云平台的插件层:用 required_providers 划定版本边界、用 alias 连接多区域多账号、用锁文件保证每次 init 拿到一模一样的插件。
延伸