加了第二个区域后,模块拿不到 provider 了
目标
为同一个 provider 设置两套配置并为每个资源选择其一,把这些配置传给模块,看看漏掉其中一个时是什么在阻止;再逐步收紧版本约束,亲自确认 init 何时通过、何时被拒绝。
为什么重要
基础设施代码停留在同一个区域、同一个账号内的时间,比想象的要短。用于灾难恢复的第二个区域、单独收集审计日志的账号、公共注册表与私有注册表——不管是哪一种,你都会为同一个 provider 准备两套不同的配置。这时,资源是用哪套配置创建的,记录在状态中而不是代码里,这条记录一旦出错,以后就会删错地方。模块则更进一步——如果模块自己创建 provider 配置,这个模块就无法被使用两次,所以应设计成只声明位置,由调用方来填充。版本约束是这一切的基础。约束怎么写,决定了“明天早上 CI 拉取新版本是否可以”,而锁文件会留下这个决定的依据。
步骤
- 在
/root/tfa-alias/main.tf中写入required_version = ">= 1.6.0"以及required_providers中的 random(source = "hashicorp/random",version = "3.9.0"),再声明一个默认 provider 配置块和random_pet.root(length 2),然后执行 init 和 apply。 - 新建
/root/tfa-alias/aliased.tf,声明alias = "seeded"的第二个 random provider 配置,以及用该配置创建的random_pet.aliased(length 4)。main.tf中的random_pet.root保持使用默认配置。apply 之后,在状态文件中确认两个资源的 provider 地址有何不同。 - 在
/root/tfa-alias/modules/site/main.tf中声明configuration_aliases = [random.east, random.west],并分别用它们创建random_pet.east和random_pet.west(均为 length 2),再放置两个同名的输出。在/root/tfa-alias/site.tf中调用该模块,把providers映射填写为random.east = random和random.west = random.seeded,把模块的两个输出提升为根模块输出site_east和site_west,然后执行 init 和 apply。 - 在
/root/tfa-alias/site.tf的providers映射中只删掉random.west这一行,执行tofu init,并把错误保存到/root/tfa-alias/missing-provider.txt(不能成功)。然后恢复被删除的那一行,重新 init 和 apply,使计划处于干净的状态。 - 在
/root/tfa-alias/probe/too-new/main.tf中,只放置把 random 约束为version = ">= 4.0.0"的required_providers,并在该目录中执行 init。输出保存到/root/tfa-alias/probe/too-new/init.txt。这个目录中不能生成锁文件。 - 在两个目录中对同一个 provider 施加不同的约束,比较结果。
在
/root/tfa-alias/probe/minor/main.tf中,把 random 的约束设为~> 3.8。 在/root/tfa-alias/probe/patch/main.tf中,把 random 的约束设为~> 3.8.0。 两者都 init 之后,在/root/tfa-alias/probe/verdict.tsv中写入minor和patch两行,每行用制表符分隔三列(<이름>(占位符为目录名称)、ok或fail、已安装的版本或none)。 - 在
/root/tfa-alias/probe/core/main.tf中只放置required_version = ">= 999.0.0",然后执行 init,把错误保存到/root/tfa-alias/probe/core/init.txt,并把当前 Pod 实际的核心版本(仅取tofu version第一行的数字,形如1.2.3)用一行写入/root/tfa-alias/probe/core/version.txt。 - 在
/root/tfa-alias/report.tsv中写入四行,每行用制表符分隔两列。lock_version= 根模块锁文件中记录的 random 版本,lock_constraints= 该锁文件中记录的约束字符串,provider_configs= 根模块配置中 random provider 配置块的数量,aliased_resources= 状态中由别名配置创建的资源数量(包括模块内部的)。
参考
- Pod 的 provider mirror 中只有 random 3.9.0 这一个版本。因此,无法满足约束的情况会成为真正的错误。
- 波浪号约束只允许最右边的一位升高。写两位和写三位时,允许的范围不同。
- 常见错误:在资源上使用模块专用的元参数 providers。资源用 provider,模块用 providers。
- 常见错误:在根目录内创建第 5、6、7 步用于探查的目录时,覆盖了根模块的配置文件。请单独创建在 probe/ 之下。
- 锁文件自身的结构和哈希验证在 terraform-basics 中讲解。这里只看别名和约束会在锁文件中留下什么。
- Provider Configuration · Provider Requirements · Providers Within Modules · Version Constraints · Dependency Lock File
钉死核心和 provider 的版本
在 /root/tfa-alias/main.tf 中写入 required_version = ">= 1.6.0" 以及 required_providers 中的 random(source = "hashicorp/random",version = "3.9.0"),再声明一个默认 provider 配置块和 random_pet.root(length 2),然后执行 init 和 apply。
required_version 约束核心(OpenTofu 本身)的版本,required_providers 中的 version 约束插件的版本。两者是不同的东西,所以只写其中一个,init 也能通过。apply 之后,请打开锁文件看看它记录了什么。
为同一个 provider 设置两套配置
新建 /root/tfa-alias/aliased.tf,声明 alias = "seeded" 的第二个 random provider 配置,以及用该配置创建的 random_pet.aliased(length 4)。main.tf 中的 random_pet.root 保持使用默认配置。apply 之后,在状态文件中确认两个资源的 provider 地址有何不同。
资源中用来选择别名配置的元参数是 provider(不是 providers——那是给模块用的)。状态文件中的每个资源都以字符串记录了它是用哪套 provider 配置创建的,只有带别名的一方结尾不同。
把 provider 配置传给模块
在 /root/tfa-alias/modules/site/main.tf 中声明 configuration_aliases = [random.east, random.west],并分别用它们创建 random_pet.east 和 random_pet.west(均为 length 2),再放置两个同名的输出。在 /root/tfa-alias/site.tf 中调用该模块,把 providers 映射填写为 random.east = random 和 random.west = random.seeded,把模块的两个输出提升为根模块输出 site_east 和 site_west,然后执行 init 和 apply。
configuration_aliases 是这样的声明:“这个模块从调用方接收这些名称的配置。”之所以不在模块内部创建 provider 块,是因为如果模块自己决定凭据,就无法复用了。providers 映射的左边是模块内部的名称,右边是根模块的配置。
漏传一个,会被什么阻止
在 /root/tfa-alias/site.tf 的 providers 映射中只删掉 random.west 这一行,执行 tofu init,并把错误保存到 /root/tfa-alias/missing-provider.txt(不能成功)。然后恢复被删除的那一行,重新 init 和 apply,使计划处于干净的状态。
模块要求的配置位置如果调用方没有填上,工具会明确指出哪个名称没有接收到。请看错误消息中是否原样出现了那个名称。输出走的是标准错误,所以必须用 2>&1 一起获取。
尝试要求 mirror 中没有的版本
在 /root/tfa-alias/probe/too-new/main.tf 中,只放置把 random 约束为 version = ">= 4.0.0" 的 required_providers,并在该目录中执行 init。输出保存到 /root/tfa-alias/probe/too-new/init.txt。这个目录中不能生成锁文件。
这个 Pod 的 provider mirror 中 random 只有一个版本。请确认当没有满足约束的版本时,工具是否会如实说出“在找什么却没找到”。失败的 init 不会留下锁文件。
两种波浪号约束允许的内容互不相同
在两个目录中对同一个 provider 施加不同的约束,比较结果。
在 /root/tfa-alias/probe/minor/main.tf 中,把 random 的约束设为 ~> 3.8。
在 /root/tfa-alias/probe/patch/main.tf 中,把 random 的约束设为 ~> 3.8.0。
两者都 init 之后,在 /root/tfa-alias/probe/verdict.tsv 中写入 minor 和 patch 两行,每行用制表符分隔三列(<이름>(占位符为目录名称)、ok 或 fail、已安装的版本或 none)。
波浪号约束只允许最右边的一位升高。写了几位,就决定了“最多可以升到哪里”。已安装的版本从该目录的锁文件中读取。
核心版本约束比插件更早拦截
在 /root/tfa-alias/probe/core/main.tf 中只放置 required_version = ">= 999.0.0",然后执行 init,把错误保存到 /root/tfa-alias/probe/core/init.txt,并把当前 Pod 实际的核心版本(仅取 tofu version 第一行的数字,形如 1.2.3)用一行写入 /root/tfa-alias/probe/core/version.txt。
核心版本约束在下载 provider 之前就会检查——所以即使完全没有 required_providers 也会被阻止。错误消息会原样说出“当前使用的版本”,请比较它与 version.txt 中的数字是否相同。
用表格列出什么是用哪套配置创建的
在 /root/tfa-alias/report.tsv 中写入四行,每行用制表符分隔两列。lock_version = 根模块锁文件中记录的 random 版本,lock_constraints = 该锁文件中记录的约束字符串,provider_configs = 根模块配置中 random provider 配置块的数量,aliased_resources = 状态中由别名配置创建的资源数量(包括模块内部的)。
锁文件中同时记录了所选版本和当时的约束,所以以后可以回溯“为什么选了这个版本”。由别名创建的资源,在状态的 provider 字符串结尾不同——请用 jq 数一数。