如何升级到 Terraform 0.12

将你的环境更新到 Terraform 0.12 并非易事,但本指南将使其不那么痛苦。
74 位读者喜欢这个。
Green graph of measurements

Internet Archive Book Images。由 Opensource.com 修改。CC BY-SA 4.0

Terraform 入门中,我使用了 Terraform 版本 0.11。正如一些读者指出的那样,此版本缺少一些重要功能,因此在本文中,我将解释如何将 Terraform 更新到功能更丰富的版本 0.12。

你可能会惊讶地发现,许多人仍然使用 Terraform 0.11,包括大型组织和喜欢使用久经考验的技术的固执用户。从 Terraform 0.11 转换为 0.12 可能非常复杂。如果你想利用新功能,但认为你还没准备好更新,本文应该有助于使转换更容易。

在本文中,我将使用我的“入门”文章中的代码和 tfenv 工具,该工具使你可以在同一台机器上使用不同版本的 Terraform。我还假设你熟悉两个版本的 Terraform(你正在使用的版本和你正在迁移到的版本),以及如何使用通用的 terraform 命令。

版本 12 所需的代码更改

有些在 Terraform 版本 0.11 中可以工作但在版本 0.12 中不能工作的东西。这是我在之前的文章中使用过的代码示例

resource "kubernetes_namespace" "1-minikube-namespace" {
  metadata {
	name = "my-first-terraform-namespace"
  }
}

正如一位读者指出的那样,由于最新 Terraform 的工作方式发生变化,此代码块在版本 0.12 中不起作用。这是一个简单的示例,并且会变得更复杂。以下是在生产环境中,事情可能需要改变多少的一些示例。

新的 Terraform 需要略有不同的语法和结构。首先,添加一个 vars.tf 文件

variable "namespace" {
  type = "string"
  default = "helloworld"
}

variable "cluster" {
  type = "string"
  default = "minikube"
}

然后,更改 main.tf 文件的部分内容以包含新的 vars.tf 文件。Kubernetes 提供程序现在看起来像这样

provider "kubernetes" {
  config_context_cluster   = "${var.cluster}"
}

这是修改后的命名空间资源

resource "kubernetes_namespace" "1-minikube-namespace" {
  metadata {
	name = "${var.namespace}"
  }
}

最后,更改后的 Helm 提供程序

provider "helm" {
  kubernetes {
	config_context_cluster   = "${var.cluster}"
  }
}

注意!这些小改动在版本之间切换时很重要。

转换前部署

在继续之前,设置 tfenv 以使用正确版本的 Terraform

jess@Athena:~/terraform_doc$ tfenv list
  0.12.29
  0.11.15-oci
jess@Athena:~/terraform_doc$ tfenv use 0.11.15-oci
Switching default version to v0.11.15-oci
Switching completed
jess@Athena:~/terraform_doc$ terraform --version
Terraform v0.11.15-oci
+ provider.helm v1.2.2
+ provider.kubernetes v1.11.2

Your version of Terraform is out of date! The latest version
is 0.xx.y. You can update by downloading from www.terraform.io/downloads.html

如果你(像我一样)在使用我之前 Terraform 文章中的代码后使用 Terraform 进行了其他工作,则需要重新部署所有内容。通过启动一个新的、干净的集群,为此 Terraform 部署设置 Minikube 集群

$ minikube delete
?  Deleting "minikube" in kvm2 ...
?  Removed all traces of the "minikube" cluster.
jess@Athena:~/terraform_doc$ minikube start
?  minikube v1.14.0 on Ubuntu 18.04

集群设置完成后,你可以部署你修改后的 Terraform 代码。从 init 命令开始

$ terraform init

Initializing provider plugins...

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.

* provider.helm: version = "~> 1.2"
* provider.kubernetes: version = "~> 1.11"

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work. [...]

接下来,运行你的计划

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
helm_release.local: Refreshing state... (ID: buildachart)
kubernetes_namespace.1-minikube-namespace: Refreshing state... (ID: my-first-terraform-namespace) [...]

Terraform will perform the following actions:

  + helm_release.local
  	id:                      	<computed>
  	atomic:                  	"false"
  	chart:                   	"./buildachart"
  	cleanup_on_fail:         	"false"
  	create_namespace:        	"false"
  	dependency_update:       	"false"
  	disable_crd_hooks:       	"false"
  	disable_openapi_validation:  "false"
  	[...]
  + kubernetes_namespace.1-minikube-namespace
  	id:                      	<computed>
  	metadata.#:              	"1"
  	[...]

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

最后,应用 Terraform

$ terraform apply --auto-approve
helm_release.local: Refreshing state... (ID: buildachart)
kubernetes_namespace.1-minikube-namespace: Refreshing state... (ID: my-first-terraform-namespace)
kubernetes_namespace.1-minikube-namespace: Creating...
  metadata.#:              	"" => "1"
  metadata.0.generation:   	"" => "<computed>"
  [...]
helm_release.local: Creating...
  atomic:                 	"" => "false"
  chart:                  	"" => "./buildachart"
  cleanup_on_fail:        	"" => "false"
  create_namespace:       	"" => "false"
 [...]
  version:                	"" => "0.1.0"
  wait:                   	"" => "true"
kubernetes_namespace.1-minikube-namespace: Creation complete after 1s (ID: helloworld)
helm_release.local: Still creating... (10s elapsed)
helm_release.local: Creation complete after 13s (ID: buildachart)

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

现在你有了 Terraform 状态的本地备份

$ ls -lrt

drwxr-xr-x 6 jess jess 4096 May 16 14:15 buildachart
-rw-r--r-- 1 jess jess  363 Oct 24 13:06 main.tf
-rw-rw-r-- 1 jess jess  132 Oct 24 13:17 vars.tf
-rw-rw-r-- 1 jess jess 3897 Oct 24 13:20 terraform.tfstate.backup
-rw-rw-r-- 1 jess jess 3821 Oct 24 13:21 terraform.tfstate

从 Terraform 0.11 转换为 0.12

一旦所有内容都使用 Terraform 0.11 部署,你必须完成转换过程,而不会损坏你在新生产集群中拥有的内容。首先,使用 tfenv 将你的 Terraform 版本更改为 0.12

$ tfenv list
  0.12.29
* 0.11.15-oci (set by /home/jess/.tfenv/version)
jess@Athena:~/terraform_doc$ tfenv use 0.12.29
Switching default version to v0.12.29
Switching completed
jess@Athena:~/terraform_doc$ terraform --version
Terraform v0.12.29
+ provider.helm v1.2.2
+ provider.kubernetes v1.11.2

现在你正在使用 Terraform 0.12,你准备好转换了。幸运的是,Terraform 有一个内置命令用于此,如 Terraform 命令列表中的此代码片段所示

All other commands:
	0.12upgrade    	Rewrites pre-0.12 module source code for v0.12

测试 upgrade 命令以查看将重写什么,然后输入 yes 以执行升级

$ terraform 0.12upgrade

This command will rewrite the configuration files in the given directory so
that they use the new syntax features from Terraform v0.12, and will identify
any constructs that may need to be adjusted for correct operation with
Terraform v0.12.
[...]
Would you like to upgrade the module in the current directory?
  Only 'yes' will be accepted to confirm.

  Enter a value: yes

-----------------------------------------------------------------------------

Upgrade complete!

The configuration files were upgraded successfully. Use your version control
system to review the proposed changes, make any necessary adjustments, and
then commit.

看起来进展顺利。它可能重写了一些东西,但这并不能解释 Terraform 文件中需要更改的所有内容。但是,它在文件中做注释方面做得很好。这是它对 vars.tf 文件所做的更改

variable "namespace" {
  type	= string
  default = "helloworld"
}

variable "cluster" {
  type	= string
  default = "minikube"
}

type 更改为删除单词 string 周围的引号(Terraform 文件中的引号随着时间的推移而发生了变化)。接下来,看看 main.tf 文件

provider "kubernetes" {
  config_context_cluster = var.cluster
}

# TF-UPGRADE-TODO: In Terraform v0.11 and earlier, it was possible to begin a
# resource name with a number, but it is no longer possible in Terraform v0.12.
#
# Rename the resource and run `terraform state mv` to apply the rename in the
# state. Detailed information on the `state move` command can be found in the
# documentation online: https://www.terraform.io/docs/commands/state/mv.html
resource "kubernetes_namespace" "1-minikube-namespace" {
  metadata {
	name = var.namespace
  }
}

provider "helm" {
  kubernetes {
	config_context_cluster = var.cluster
  }
}

resource "helm_release" "local" {
  name  = "buildachart"
  chart = "./buildachart"
}

这里的引号也发生了变化,变量不再用 ${} 字符包裹。最大的部分是转换命令放置在代码中的一个大型 TODO 注释,以显示资源名称中需要更改什么才能在版本 0.12 中被接受。更好的是,它解释了如何解决此问题以及你需要运行的命令。其他主要更改是新的 versions.tf 文件和一个新的备份文件

$ ls -lrt
drwxr-xr-x 6 jess jess 4096 May 16 14:15 buildachart
-rw-rw-r-- 1 jess jess 3897 Oct 24 13:20 terraform.tfstate.backup
-rw-r--r-- 1 jess jess   46 Oct 24 13:28 versions.tf
-rw-rw-r-- 1 jess jess  140 Oct 24 13:55 vars.tf
-rw-r--r-- 1 jess jess  369 Oct 24 13:56 main.tf
-rw-rw-r-- 1 jess jess 3821 Oct 24 13:56 terraform.tfstate.1603562212.backup
-rw-rw-r-- 1 jess jess 3827 Oct 24 13:56 terraform.tfstate

要更新你的生产集群,请从一个更适合此版本的不同名称开始

resource "kubernetes_namespace" "upgrade-minikube-namespace" {
  metadata {
	name = var.namespace
  }
}

消除粗糙的边缘

在更改之后,你必须运行 terraform state mv 命令(如大型 TODO 注释中所引用的)。但首先,运行 state list 以查看你正在使用的内容

$ terraform state list
helm_release.local
Kubernetes_namespace.1-minikube-namespace

命名空间仍然在状态中设置为 1-minikube-namespace,这是你需要移动的状态。这可以通过确保你具有新旧资源名称,然后运行 terraform state mv 命令来完成。但在此之前,你必须切换回 0.11,使用 tfenv 进行这些更改,因为这是使用 Terraform 0.11 部署的,而 0.12 不识别资源名称开头的数字(正如 TODO 所说)。你必须还原 Terraform 升级所做的所有代码更改,除了资源名称更改之外

main.tf

provider "kubernetes" {
  config_context_cluster   = "${var.cluster}"
}

resource "kubernetes_namespace" "upgrade-minikube-namespace" {
  metadata {
	name = "${var.namespace}"
  }
}

provider "helm" {
  kubernetes {
	config_context_cluster   = "${var.cluster}"
  }
}
resource "helm_release" "local" {
  name   	= "buildachart"
  chart  	= "./buildachart"
}

Vars.tf

variable "namespace" {
  type	= "string"
  default = "helloworld"
}

variable "cluster" {
  type	= "string"
  default = "minikube"
}

一旦更改恢复到位,将 tfenv 切换回版本 0.11,并运行 state mv 命令

$ tfenv use 0.11.15-oci
Switching default version to v0.11.15-oci
Switching completed
jess@Athena:~/terraform_doc$ terraform state mv 'kubernetes_namespace.1-minikube-namespace' 'kubernetes_namespace.upgrade-minikube-namespace'
Moved kubernetes_namespace.1-minikube-namespace to kubernetes_namespace.upgrade-minikube-namespace

最后步骤

完成此操作后,将 tfenv 切换回版本 0.12,然后删除你的 versions.tf 文件以确保转换完成。如果你不删除此文件,你将收到错误消息

$ terraform 0.12upgrade
Error: Module already upgraded

  on versions.tf line 3, in terraform:
   3:   required_version = ">= 0.12"

The module in directory . has a version constraint that suggests it has
already been upgraded for v0.12. If this is incorrect, either remove this
constraint or override this heuristic with the -force argument. Upgrading a
module that was already upgraded may change the meaning of that module.

删除文件并运行命令

$ terraform 0.12upgrade

This command will rewrite the configuration files in the given directory so
that they use the new syntax features from Terraform v0.12, and will identify
any constructs that may need to be adjusted for correct operation with
Terraform v0.12. [...]

Would you like to upgrade the module in the current directory?
  Enter a value: yes

-----------------------------------------------------------------------------

Upgrade complete!

通过另一次部署测试转换

通过重新运行你的 initplanapply 命令来测试你的新转换(我在这些代码片段中省略了部分输出)

$ terraform init

Initializing the backend...
Initializing provider plugins...
[...]
Terraform has been successfully initialized!

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

helm_release.local: Refreshing state... [id=buildachart]
kubernetes_namespace.upgrade-minikube-namespace: Refreshing state... [id=helloworld]

------------------------------------------------------------------------

No changes. Infrastructure is up-to-date

$ terraform apply
helm_release.local: Refreshing state... [id=buildachart]
kubernetes_namespace.upgrade-minikube-namespace: Refreshing state... [id=helloworld]

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

正如这所示,一旦一切都预先配置好,并且在转换期间移动并重新应用状态,就没有进行其他更改,因为基础设施已到位。

最终想法

代码和应用程序升级很难做,尤其是在实时生产环境中。从 Terraform 0.11 转换为 0.12 时,情况确实如此。我已经在大型规模上做过这件事,它涉及为期两周的广泛计划。

如果你打算在生产环境中执行此操作,请确保你

  • 首先删除任何带有前缀数字的资源或模块。
  • 在运行升级之前移动状态。
  • 将你升级后的 Terraform 文件保存在一个 fork 的仓库中以确保安全。

我希望这篇文章能帮助你比我更快更轻松地前进。

接下来阅读什么
User profile image.
技术游民,从事我能找到的任何工作。IT 领域孤岛预防的倡导者,所有团队信息共享的重要性。相信教育所有人以及开源开发。热爱所有技术事物。一切都关于 K8s、混沌以及我能找到的任何新的和闪亮的东西!Mastodon ID

1 条评论

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 许可。
© . All rights reserved.