写点什么

Terraform 中使用 prevent_destroy 搭配 override 文件防止误删资源

发布于: 2021 年 05 月 26 日

Terraform 是一个非常强大的工具,强大到有时候有些可怕。想象一下,假如我们在虚拟机中安装了一个 NOSQL 数据库并存储了一些数据,结果在后续的代码修改中我们不小心错误地修改了虚拟机的可用区参数的值,这将会导致一个意外的虚拟机删除重建操作,代价则是存储的数据全部不可逆地丢失,这是一个运维所能想象到最恐怖的场景了。虽然我们可以制定相关的流程,规定必须由专人审核变更计划,通过后方可执行来避免这种意外发生,但只要是人类把关的环节,终究还是有疏失的可能性。

我们在讲解资源的章节中介绍了lifecycle中的prevent_destroy参数。这个参数是一个保险措施,只要它被设置为true时,Terraform 会拒绝执行任何可能会销毁该基础设施资源的变更计划。该功能非常适合用来防止执行错误的变更计划导致关键资源被删除的问题。

例如,我们有这样一段简单的 Terraform 代码:

data "ucloud_images" "centos" {  name_regex        = "^CentOS 7"  availability_zone = var.az}
resource "ucloud_instance" "nosql" { availability_zone = var.az image_id = data.ucloud_images.centos.images[0].id instance_type = "n-basic-1" charge_type = "dynamic" lifecycle { prevent_destroy = true }}
复制代码

我们在资源的lifecycle当中添加了prevent_destroy = true的声明。让我们执行以下命令来创建资源:

$ terraform apply -var 'az=cn-bj2-03' -auto-approveucloud_instance.nosql: Creating...ucloud_instance.nosql: Still creating... [10s elapsed]ucloud_instance.nosql: Creation complete after 18s [id=uhost-4p4j3wdb]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
复制代码

我们在命令行中显式指定azcn-jb2-03,执行后成功创建资源。接下来我们再次执行apply,只不过这一次我们修改一下az的值:

$ terraform apply -var 'az=cn-bj2-04' -auto-approveucloud_instance.nosql: Refreshing state... [id=uhost-4p4j3wdb]
Error: Instance cannot be destroyed
  on main.tf line 41:  41: resource "ucloud_instance" "nosql" {
Resource ucloud_instance.nosql has lifecycle.prevent_destroy set, but the plancalls for this resource to be destroyed. To avoid this error and continue withthe plan, either disable lifecycle.prevent_destroy or reduce the scope of theplan using the -target flag.
复制代码

Terraform 成功地发现,az 的变化会引发被保护资源的删除操作,所以果断拒绝了执行。

但是我们又面临着另外一个问题,那就是当我们真的想要删除资源时,会由于prevent_destroy而失败:

$ terraform destroy -force
Error: Instance cannot be destroyed
  on main.tf line 41:  41: resource "ucloud_instance" "nosql" {
Resource ucloud_instance.nosql has lifecycle.prevent_destroy set, but the plancalls for this resource to be destroyed. To avoid this error and continue withthe plan, either disable lifecycle.prevent_destroy or reduce the scope of theplan using the -target flag.
复制代码

在自动化环境中,修改代码删除prevent_destroy参数或是设置为false都是非常麻烦的,幸好我们有重载文件。我们可以在项目中创建名为override.tf.ondestroy的文件:

  resource "ucloud_instance" "nosql" {    lifecycle {      prevent_destroy = false    }  }
复制代码

在文件中我们重载了资源的prevent_destroy的值到false,但由于平时该文件后缀名是 ondestroy,所以不会影响到资源声明上的prevent_destroy保险;当我们确信要删除基础设施资源时,我们可以先把.ondestry后缀名去掉,将文件重命名为override.tf,随后执行 destory:

$ terraform destroy -forceucloud_instance.nosql: Destroying... [id=uhost-4p4j3wdb]ucloud_instance.nosql: Still destroying... [id=uhost-4p4j3wdb, 10s elapsed]ucloud_instance.nosql: Destruction complete after 13s
Destroy complete! Resources: 1 destroyed.
复制代码

删除成功。

那如果我们是在模块中声明了重要资源呢?例如我们把上文描述的代码放在 github 上,然后通过模块来引用:

provider "ucloud" {  public_key  = var.public_key  private_key = var.private_key  project_id  = var.project_id  region      = var.region}
module "nosql" { source = "github.com/lonegunmanb/sample_for_prevent_destroy_and_override" az = "cn-bj2-03"}
复制代码

在执行了terraform apply操作后,模块代码中的prevent_destroy就开始如预期般保护我们不会在后续的apply中错误删除资源。该项目中也同时包含有一个override.tf.ondestroy文件。如果我们希望执行destroy操作,我们可以创建这样一个名为before_destroy.sh的脚本文件:

for file in $(find . -name 'override.tf.ondestroy')do  mv $file $(echo "$file" | sed -r 's|.tf.ondestroy|.tf|g')done
复制代码

执行该脚本会递归检查当前目录及所有子目录下的override.tf.ondestroy文件,将之重命名为override.tf,然后执行terraform destroy

$ terraform destroy -forcemodule.nosql.ucloud_instance.nosql: Destroying... [id=uhost-mnklkfwa]module.nosql.ucloud_instance.nosql: Still destroying... [id=uhost-mnklkfwa, 10s elapsed]module.nosql.ucloud_instance.nosql: Destruction complete after 12s
Destroy complete! Resources: 1 destroyed.
复制代码

即可成功删除。

发布于: 2021 年 05 月 26 日阅读数: 19
用户头像

还未添加个人签名 2017.10.17 加入

还未添加个人简介

评论

发布
暂无评论
Terraform中使用prevent_destroy搭配override文件防止误删资源