1.5.2.1. 依赖锁(Dependency Lock)文件

注意:该功能是自 Terraform 0.14 起引入的一项功能。 Terraform 的更早期版本不跟踪依赖项,因此本节与这些早期版本无关。

Terraform 配置文件中可以引用来自其自身代码库之外的两种不同类型的外部依赖项:

  • Providers,它们是 Terraform 的插件,用以与各种外部系统交互。
  • 模块,允许将一组 Terraform 配置(用 Terraform 语言编写)拆分为可重用的抽象组件。

这两种类型的依赖都可以独立于 Terraform 本身以及依赖于它们的代码进行发布和更新。因此,Terraform 必须确定这些依赖项的哪些版本可能与当前配置兼容以及当前选择使用哪些版本。

配置本身内的版本约束决定了哪些版本的依赖项可能与当前配置兼容,但在为每个依赖项选定的特定版本后,Terraform 会记住它在依赖锁文件中所做的决定,以便它可以(默认情况下)在将来再次做出相同的决定。

目前,依赖锁文件仅追踪 Provider 的依赖项。 Terraform 不会保存远程模块的版本选择,因此 Terraform 将始终选择满足指定版本约束的最新可用模块版本。我们可以使用精确的版本约束来确保 Terraform 始终选择相同版本的模块。

1.5.2.1.1. 锁文件的位置

依赖锁文件是记录的是整个配置的依赖,而不是配置代码中使用每个单独的模块的依赖。因此,Terraform 在运行 Terraform 时的当前工作目录中创建该文件,并假设该文件存在于此,同时该目录也包含了配置根模块的 .tf 文件的。

依赖锁文件的名字始终是 .terraform.lock.hcl,此名称旨在表示它是 Terraform 缓存在工作目录的 .terraform 子目录中的各种项目的锁定文件。

每次运行 terraform init 命令时,Terraform 都会自动创建或更新依赖项锁定文件。我们应该将此文件包含在版本控制存储库中,以便我们可以在代码审查过程中对那些外部依赖项的更改进行审查,就像对配置本身的更改进行审查一样。

依赖锁定文件使用与 Terraform 语言相同的语法,但依赖锁定文件本身并不是 Terraform 语言配置文件。它以后缀 .hcl 而不是 .tf 命名,以显示这种差异。

1.5.2.1.2. 安装依赖的行为

terraform init 正在安装配置所需的所有 Provider 程序时,Terraform 会同时参考配置中的版本约束以及锁定文件中记录的版本选择。

如果某个 Provider 程序没有在锁文件中找到对应的记录,Terraform 将选择与给定版本约束匹配的最新可用版本,然后更新锁定文件以保存该选择。

如果某个 Provider 程序已在锁文件中保存了一个版本选择记录,Terraform 将始终重新选择该版本进行安装,即使有更新的版本可用。我们可以通过在运行 terraform init 时添加 -upgrade 选项来重载该行为,在这种情况下,Terraform 将忽略现有选择并再次选择与版本约束匹配的最新可用版本。

如果某次 terraform init 调用对锁文件进行了更改,Terraform 会在其输出中提到这一点:

Terraform has made some changes to the provider dependency selections recorded
in the .terraform.lock.hcl file. Review those changes and commit them to your
version control system if they represent changes you intended to make.

当我们看到这样的消息时,我们可以使用版本控制系统来查看 Terraform 提到的那些在文件中的变更,如果变更的确是我们有意为之的,那我们可以将这些变更发送至团队的常规代码审核流程进行审查。

1.5.2.1.2.1. 验证校验和

Terraform 还将验证它安装的每个包是否与之前在锁定文件中记录的校验和至少一个相匹配(如果有),如果没有校验和匹配,则返回错误:

Error: Failed to install provider

Error while installing hashicorp/azurerm v2.1.0: the current package for
registry.terraform.io/hashicorp/azurerm 2.1.0 doesn't match any of the
checksums previously recorded in the dependency lock file.

此校验和验证采用初次使用时信任原则。当我们第一次添加新的 Provider 程序时,我们可以通过各种方式或相关法规要求的所有方法对其进行验证,然后确信如果未来运行 terraform init 时,在安装相同 Provider 程序时遇到不匹配的包时 Terraform 会返回错误。

“初次使用时”模型有两个特殊考虑因素:

  • 如果我们从源注册表安装 Provider 程序时,该 Provider 程序提供了使用加密签名算法签名的校验和,那么只要有一个校验和匹配,Terraform 就会将所有已签名的校验和视为有效。因此,锁文件将包含该 Provider 所适用的所有平台的所有不同包的校验和。

在这种情况下,terraform init 的输出将包括对校验和进行签名的密钥的指纹,以及像这样的信息 (signed by a HashiCorp partner, key ID DC9FC6B1FCE47986)。在提交包含签名校验和的锁文件之前,最好先确认我们能够信任给该密钥的持有者,或者检索并验证指定 Provider 版本的所有可用包的集合。

  • 如果我们首次安装 Provider 时使用的是替代安装方法(例如文件系统或网络镜像),Terraform 将无法验证除运行 terraform init 的平台之外的其他所有平台的校验和,因此它不会记录其他平台的校验和,导致该配置将无法在任何其他平台上使用。

为了避免此问题,您可以使用 terraform providers lock 命令在锁文件中预先记录各种不同平台的校验和,这样我们将来可以调用 terraform init 命令来验证我们选择的镜像中可用的包是否与该 Provider 源注册表提供的官方包相匹配。

1.5.2.1.3. 理解锁文件的变更

由于依赖锁文件主要由 Terraform 本身自动维护,而不是手动更新,因此我们的版本控制系统可能会向我们显示该文件已更改。

Terraform 可能会对我们的锁文件进行几种不同类型的变更,我们可能需要了解这些更改才能查看建议的变更。以下各节将描述这些常见变更。

1.5.2.1.3.1. 新 Provider 的依赖

如果我们向配置中任意模块的 Provider 的声明配置节中添加新条目,或者添加了包含新 Provider 程序依赖项的外部模块,terraform init 将通过选择满足配置中所有版本约束的最新版本的 Provider 程序,并将这条决策保存为依赖锁文件中的一个新的 Provider 程序块。

--- .terraform.lock.hcl 2020-10-07 16:12:07.539570634 -0700
+++ .terraform.lock.hcl 2020-10-07 16:12:15.267487237 -0700
@@ -6,6 +6,26 @@
   ]
 }

+provider "registry.terraform.io/hashicorp/azurerm" {
+  version     = "2.30.0"
+  constraints = "~> 2.12"
+  hashes = [
+    "h1:FJwsuowaG5CIdZ0WQyFZH9r6kIJeRKts9+GcRsTz1+Y=",
+    "h1:c/ntSXrDYM1mUir2KufijYebPcwKqS9CRGd3duDSGfY=",
+    "h1:yre4Ph76g9H84MbuhZ2z5MuldjSA4FsrX6538O7PCcY=",
+    "zh:04f0a50bb2ba92f3bea6f0a9e549ace5a4c13ef0cbb6975494cac0ef7d4acb43",
+    "zh:2082e12548ebcdd6fd73580e83f626ed4ed13f8cdfd51205d8696ffe54f30734",
+    "zh:246bcc449e9a92679fb30f3c0a77f05513886565e2dcc66b16c4486f51533064",
+    "zh:24de3930625ac9014594d79bfa42d600eca65e9022b9668b54bfd0d924e21d14",
+    "zh:2a22893a576ff6f268d9bf81cf4a56406f7ba79f77826f6df51ee787f6d2840a",
+    "zh:2b27485e19c2aaa9f15f29c4cff46154a9720647610171e30fc6c18ddc42ec28",
+    "zh:435f24ce1fb2b63f7f02aa3c84ac29c5757cd29ec4d297ed0618423387fe7bd4",
+    "zh:7d99725923de5240ff8b34b5510569aa4ebdc0bdb27b7bac2aa911a8037a3893",
+    "zh:7e3b5d0af3b7411dd9dc65ec9ab6caee8c191aee0fa7f20fc4f51716e67f50c0",
+    "zh:da0af4552bef5a29b88f6a0718253f3bf71ce471c959816eb7602b0dadb469ca",
+  ]
+}
+
 provider "registry.terraform.io/newrelic/newrelic" {
   version     = "2.1.2"
   constraints = "~> 2.1.1"

新的锁文件条目记录了几条信息:

  • version:Terraform 根据配置中的版本约束所选择的版本。
  • constraints:Terraform 在进行此选择时所遵守的所有的版本约束。 (Terraform 实际上并不使用此信息来做出安装决策,而是保存该信息以帮助向人类读者解释之前的决策是如何做出的。)
  • hashes:给定 Provider 程序在不同平台上有效的安装包所对应的一组校验和。这些哈希值的含义在下面的新 Provider 程序包校验和中有进一步的解释。

1.5.2.1.3.2. 现存 Provider 的新版本

如果我们运行 terraform init -upgrade 来命令 Terraform 在遵守配置的版本约束匹配的前提下将 Provider 升级到更新的版本,那么 Terraform 可能会为 Provider 程序选择较新的版本并更新其现有的 Provider 程序块,以体现该变更。

--- .terraform.lock.hcl 2020-10-07 16:44:25.819579509 -0700
+++ .terraform.lock.hcl 2020-10-07 16:43:42.785665945 -0700
@@ -7,22 +7,22 @@
 }

 provider "registry.terraform.io/hashicorp/azurerm" {
-  version     = "2.1.0"
-  constraints = "~> 2.1.0"
+  version     = "2.0.0"
+  constraints = "2.0.0"
   hashes      = [
-    "h1:EOJImaEaVThWasdqnJjfYc6/P8N/MRAq1J7avx5ZbV4=",
-    "zh:0015b491cf9151235e57e35ea6b89381098e61bd923f56dffc86026d58748880",
-    "zh:4c5682ba1e0fc7e2e602d3f103af1638f868c31fe80cc1a884a97f6dad6e1c11",
-    "zh:57bac885b108c91ade4a41590062309c832c9ab6bf6a68046161636fcaef1499",
-    "zh:5810d48f574c0e363c969b3f45276369c8f0a35b34d6202fdfceb7b85b3ac597",
-    "zh:5c6e37a44462b8662cf9bdd29ce30523712a45c27c5d4711738705be0785db41",
-    "zh:64548940a3387aa3a752e709ee9eb9982fa820fe60eb60e5f212cc1d2c58549e",
-    "zh:7f46749163da17330bbb5293dc825333c86304baa0a7c6256650ac536b4567c8",
-    "zh:8f8970f2df75ac43ffdd112055ee069d8bd1030f7eb4367cc4cf494a1fa802c3",
-    "zh:9ad693d00dc5d7d455d06faba70e716bce727c6706f7293288e87fd7956b8fe0",
-    "zh:b6e3cb55e6aec62b47edd0d2bd5e14bd6a2bcfdac65930a6e9e819934734c57b",
-    "zh:d6a3f3b9b05c28ecf3919e9e7afa185805a6d7442fc4b3eedba749c2731d1f0e",
-    "zh:d81fb624a357c57c7ea457ce543d865b39b12f26c2edd58a2f7cd43326c91010",
+    "h1:bigGXBoRbp7dv79bEEn+aaju8575qEXHQ57XHVPJeB8=",
+    "zh:09c603c8904ca4a5bc19e82335afbc2837dcc4bee81e395f9daccef2f2cba1c8",
+    "zh:194a919d4836d6c6d4ce598d0c66cce00ddc0d0b5c40d01bb32789964d818b42",
+    "zh:1f269627df4e266c4e0ef9ee2486534caa3c8bea91a201feda4bca525005aa0a",
+    "zh:2bae3071bd5f8e553355c4b3a547d6efe1774a828142b762e9a4e85f79be7f63",
+    "zh:6c98dfa5c3468e8d02e2b3af7c4a8a14a5d469ce5a642909643b413a17ca338b",
+    "zh:7af78f61666fd45fbf428161c061ea2623162d601b79dc71d6a5158756853ffa",
+    "zh:883c2df86ae9ba2a5c167cf5c2c7deca0239171a224d6d335f0fd6dd9c283830",
+    "zh:a2028379078577d8ff5ecfca6e8a8b25a25ffb1686de0ee52a7fe8011783488b",
+    "zh:abe6ef399552fd3861a454a839cd978c1d15735658fdc00f9054435aff0f4620",
+    "zh:c30b1bf14077913c3cdf34979b1434dbb1353cb5995eb3956b191c50538b64a9",
+    "zh:ca64ae2ad9793e5631e3b0b9327f7cb22cb5d8e9de57be7d85821791b1d5a375",
+    "zh:fffe56904a38109bb8d613b02808a177c3ddfac19f03b3aac799281fea38f475",
   ]
 }

选择新的 Provider 程序版本的对锁文件进行的修改主要是更改了 provider 块中 version 的值。如果升级伴随着对配置文件中声明的版本约束的变更,Terraform 还将在 constraints 中记录该变更。

由于每个版本都有自己的一组分发包,因此切换到新版本也往往会替换 hashes 中的所有值,以体现新版本包的校验和。

1.5.2.1.3.3. 新 Provider 程序包校验和

我们可能在 provider 块中看到的一个很细微的变化是添加了以前未记录的新校验和,即使 provider 块中的所有其他内容都没有更改:

--- .terraform.lock.hcl 2020-10-07 17:24:23.397892140 -0700
+++ .terraform.lock.hcl 2020-10-07 17:24:57.423130253 -0700
@@ -10,6 +10,7 @@
   version     = "2.1.0"
   constraints = "~> 2.1.0"
   hashes = [
+    "h1:1xvaS5D8B8t6J6XmXxX8spo97tAzjhacjedFX1B47Fk=",
     "h1:EOJImaEaVThWasdqnJjfYc6/P8N/MRAq1J7avx5ZbV4=",
     "zh:0015b491cf9151235e57e35ea6b89381098e61bd923f56dffc86026d58748880",
     "zh:4c5682ba1e0fc7e2e602d3f103af1638f868c31fe80cc1a884a97f6dad6e1c11",

hashes 中添加的新的校验和代表 Terraform 在不同哈希方案之间逐渐过渡。这些值上的 h1:zh: 前缀代表不同的哈希方案,每个方案都代表使用不同的算法计算校验和。如果现有方案存在某种局限性或者新方案提供了一些相当明显的收益,有时我们可能会引入新的哈希方案。

目前支持两种哈希方案:

  • zh:: 代表“zip hash”,这是一种遗留哈希格式,是 Terraform Provider 程序注册表协议的一部分,因此用于直接从源注册表安装的 Provider 程序。

此哈希方案捕记录下注册表中索引的每个官方 .zip 包的 SHA256 哈希值。这是验证从注册表安装的官方发布包的有效方案,但它无法验证来自其他 Provider 程序安装方法的包,例如文件系统中指向解压后的安装包的镜像。

  • h1:: 代表“hash schema 1”,它是当前首选的哈希方案。

Hash Schema 1 也是 SHA256 哈希,但它是根据提供 Provider 安装包内包含的内容计算得出的,而不是根据其包含的 .zip 文件的内容计算得出的。因此,该方案的优点是可以针对官方 .zip 文件、具有相同内容的解压目录或包含相同文件但可能具有不同元数据或压缩方案的重新压缩 .zip 文件计算出相同的校验和。

由于 zh: 方案的局限性,Terraform 将在计算出相应的 h1: 校验和时适时添加它们,这就是导致在上面所示的示例更改中添加第二个 h1: 校验和的原因。

Terraform 只会在安装包与已有哈希值之一相匹配时,才会把新计算出的哈希值添加到锁文件中。在上面的示例中,Terraform 在与生成原始 h1: 校验和的平台不同的平台安装了 hashcorp/azurerm 包,但能够匹配到之前记录的 zh: 校验和的其中之一,确认 zh: 校验和匹配后,Terraform 保存相应的 h1: 校验和,以便逐步从旧方案迁移到新方案。

首次安装某个 Provider 程序时(没有已有的 provider 块),Terraform 将使用 Provider 插件开发人员的加密签名(通常涵盖所有可用包)所涵盖适用于所有受支持平台上的该 Provider 程序版本校验集合来预先填充 hashes。但是,由于 Provider 程序注册表协议仍然使用 zh: 方案,因此初始集将主要包含使用该方案的哈希值,然后当我们在不同平台上安装软件包时,Terraform 将适时升级该哈希值。

如果我们希望避免在新目标平台上使用配置时不断添加新的 h1: 哈希值,或者如果因为我们是从镜像安装的 Provider 程序,因此无法提供官方签名的校验和,您可以使用 terraform providers lock 命令要求 Terraform 预填充一组选定平台的哈希值:

terraform providers lock \
  -platform=linux_arm64 \
  -platform=linux_amd64 \
  -platform=darwin_amd64 \
  -platform=windows_amd64

上述命令将下载并验证所有四个给定平台上所有我们指定的 Provider 程序的官方软件包,然后在锁文件中记录每个软件包的 zh:h1: 校验和,从而避免 Terraform 在之后只能得到 h1: 校验和这种情况。有关此命令的更多信息,请参阅 terraform providers lock 命令。

1.5.2.1.3.4. 那些不再被使用的 Provider

为了确定是否仍然存在对某个 Provider 程序的依赖,Terraform 使用两个事实来源:配置代码和状态。如果我们从配置和状态中删除对某个 Provider 程序的最后一个依赖项,则 terraform init 将从锁文件中删除该 Provider 的现存记录。

--- .terraform.lock.hcl 2020-10-07 16:12:07.539570634 -0700
+++ .terraform.lock.hcl 2020-10-07 16:12:15.267487237 -0700
@@ -6,26 +6,6 @@
   ]
 }

-provider "registry.terraform.io/hashicorp/azurerm" {
-  version     = "2.30.0"
-  constraints = "~> 2.12"
-  hashes = [
-    "h1:FJwsuowaG5CIdZ0WQyFZH9r6kIJeRKts9+GcRsTz1+Y=",
-    "h1:c/ntSXrDYM1mUir2KufijYebPcwKqS9CRGd3duDSGfY=",
-    "h1:yre4Ph76g9H84MbuhZ2z5MuldjSA4FsrX6538O7PCcY=",
-    "zh:04f0a50bb2ba92f3bea6f0a9e549ace5a4c13ef0cbb6975494cac0ef7d4acb43",
-    "zh:2082e12548ebcdd6fd73580e83f626ed4ed13f8cdfd51205d8696ffe54f30734",
-    "zh:246bcc449e9a92679fb30f3c0a77f05513886565e2dcc66b16c4486f51533064",
-    "zh:24de3930625ac9014594d79bfa42d600eca65e9022b9668b54bfd0d924e21d14",
-    "zh:2a22893a576ff6f268d9bf81cf4a56406f7ba79f77826f6df51ee787f6d2840a",
-    "zh:2b27485e19c2aaa9f15f29c4cff46154a9720647610171e30fc6c18ddc42ec28",
-    "zh:435f24ce1fb2b63f7f02aa3c84ac29c5757cd29ec4d297ed0618423387fe7bd4",
-    "zh:7d99725923de5240ff8b34b5510569aa4ebdc0bdb27b7bac2aa911a8037a3893",
-    "zh:7e3b5d0af3b7411dd9dc65ec9ab6caee8c191aee0fa7f20fc4f51716e67f50c0",
-    "zh:da0af4552bef5a29b88f6a0718253f3bf71ce471c959816eb7602b0dadb469ca",
-  ]
-}
-
 provider "registry.terraform.io/newrelic/newrelic" {
   version     = "2.1.2"
   constraints = "~> 2.1.1"

如果我们稍后为同一 Provider 程序添加新的引用并再次运行 terraform init,Terraform 会将其视为全新的 Provider 程序,因此不一定会选择之前选择的相同版本,并且无法验证校验和是否保持不变。

注意:在 Terraform v1.0 及更早版本中,terraform init 不会自动从锁文件中删除现在不需要的 Provider 程序,而只是忽略它们。如果您之前使用早期版本的 Terraform 时删除了提供 Provider 依赖项,然后升级到 Terraform v1.1 或更高版本,那么您可能会看到错误“missing or corrupted provider plugins”,指向过时的锁文件条目。如果是这样,请使用新的 Terraform 版本运行 terraform init 以整理那些不需要的条目,然后重试之前的操作。

results matching ""

    No results matching ""