TT Lab
开始
学习 学习路径 课程

Terraform 实战

加了第二个区域后,模块拿不到 provider 了

在 TT Lab 中继续学习

目标

为同一个 provider 设置两套配置并为每个资源选择其一,把这些配置传给模块,看看漏掉其中一个时是什么在阻止;再逐步收紧版本约束,亲自确认 init 何时通过、何时被拒绝。

为什么重要

基础设施代码停留在同一个区域、同一个账号内的时间,比想象的要短。用于灾难恢复的第二个区域、单独收集审计日志的账号、公共注册表与私有注册表——不管是哪一种,你都会为同一个 provider 准备两套不同的配置。这时,资源是用哪套配置创建的,记录在状态中而不是代码里,这条记录一旦出错,以后就会删错地方。模块则更进一步——如果模块自己创建 provider 配置,这个模块就无法被使用两次,所以应设计成只声明位置,由调用方来填充。版本约束是这一切的基础。约束怎么写,决定了“明天早上 CI 拉取新版本是否可以”,而锁文件会留下这个决定的依据。

步骤

  1. 在 /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。
  2. 新建 /root/tfa-alias/aliased.tf,声明 alias = "seeded" 的第二个 random provider 配置,以及用该配置创建的 random_pet.aliased(length 4)。main.tf 中的 random_pet.root 保持使用默认配置。apply 之后,在状态文件中确认两个资源的 provider 地址有何不同。
  3. 在 /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。
  4. 在 /root/tfa-alias/site.tf 的 providers 映射中只删掉 random.west 这一行,执行 tofu init,并把错误保存到 /root/tfa-alias/missing-provider.txt(不能成功)。然后恢复被删除的那一行,重新 init 和 apply,使计划处于干净的状态。
  5. 在 /root/tfa-alias/probe/too-new/main.tf 中,只放置把 random 约束为 version = ">= 4.0.0" 的 required_providers,并在该目录中执行 init。输出保存到 /root/tfa-alias/probe/too-new/init.txt。这个目录中不能生成锁文件。
  6. 在两个目录中对同一个 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)。
  7. 在 /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。
  8. 在 /root/tfa-alias/report.tsv 中写入四行,每行用制表符分隔两列。lock_version = 根模块锁文件中记录的 random 版本,lock_constraints = 该锁文件中记录的约束字符串,provider_configs = 根模块配置中 random provider 配置块的数量,aliased_resources = 状态中由别名配置创建的资源数量(包括模块内部的)。

参考

钉死核心和 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 数一数。