Day 21-state manipulation 之三:我想 rename 怎麽办?state mv 乾坤大挪移

我想 rename 怎麽办?state mv 乾坤大挪移

课程内容与代码会放在 Github 上: https://github.com/chechiachang/terraform-30-days

赛後文章会整理放到个人的部落格上 http://chechia.net/

追踪粉专可以收到文章的主动推播

https://ithelp.ithome.com.tw/upload/images/20210901/20120327NvpHVr2QC0.jpg


Terraform state mv

这边用范例讲解,同时注意三个部分的差异程度

  • .tf 中描述的 resource block,也就是依据业务需求想达到的 desired state
  • 远端公有云上对应的 resource infrastructure,也就是 actual state
  • terraform state 中存在 resource,也就是 metadata,或是中间产物

terraform 是协助工程师管理上述三组 state,达到状态统一

terraform state 变更 (ex. mv 等) 只管理 terraform state 这一部分,.tf resource 与 remote resource 是维持原状的

state 变更会在三组 state 中产生分歧,这些分歧会产生额外的效果,这是我们在 state manipulation 时须要格外注意的

Terraform state mv

https://www.terraform.io/docs/cli/commands/state/mv.html

根据官网内容 state mv 做以下事情

  • terraform state mv resource 到新的 address

一样回到 azure/foundation/compute_network 的范例

cd azure/foundation/compute_network
terragrunt state list

module.network.data.azurerm_resource_group.network
module.network.azurerm_subnet.subnet[0]
module.network.azurerm_subnet.subnet[1]
module.network.azurerm_subnet.subnet[2]
module.network.azurerm_virtual_network.vnet

rename .tf & state mv

例如:我们因为业务需求变更,希望 rename module.network 的 module name,从 module.network 变成 module.private-network

  • 这边只是举个范例,为改而改
git diff

# https://github.com/Azure/terraform-azurerm-network
-module "network" {
+module "private-network" {
   source = "Azure/network/azurerm"

   resource_group_name = var.resource_group_name
(END)

改完之後,回到 azure/foundation/compute_network ,进行 terraform plan,请问会看到什麽变化?这边大家依据过去所学,预测一下 terraform 的行为

这边给几个提示

  • module 是 terraform 的基本单元,rename module 会有什麽影响?
cd azure/foundation/compute_network

terragtunt plan

╷
│ Error: Module not installed
│
│   on compute_network.tf line 2:
│    2: module "private-network" {
│
│ This module is not yet installed. Run "terraform init" to install all
│ modules required by this configuration.
╵
ERRO[0015] 1 error occurred:
	* exit status 1

跳出 module not installed 的错误,於是这边我们进行 initA

  • 效果是新称 module.private-network
  • 复习可以翻回去前面的第?章
terragrunt init

Initializing modules...
Downloading Azure/network/azurerm 3.5.0 for priavte-network...
- priavte-network in .terraform/modules/private-network

Initializing the backend...

Initializing provider plugins...

於是在进行 plan

  • 如果 output 中友引用原来的 module,会需要跟着修正 output
terragrunt plan

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create
  - destroy

Terraform will perform the following actions:

  # module.network.azurerm_subnet.subnet[0] will be destroyed
  }

  # module.network.azurerm_subnet.subnet[1] will be destroyed
  }

  # module.network.azurerm_subnet.subnet[2] will be destroyed
  }

  # module.network.azurerm_virtual_network.vnet will be destroyed
  }

  # module.private-network.azurerm_subnet.subnet[0] will be created
  }

  # module.private-network.azurerm_subnet.subnet[1] will be created
  }

  # module.private-network.azurerm_subnet.subnet[2] will be created
  }

  # module.private-network.azurerm_virtual_network.vnet will be created
  }

Plan: 4 to add, 0 to change, 4 to destroy.

Changes to Outputs:
  ~ vnet_id      = "/subscriptions/.../resourceGroups/terraform-30-days/providers/Microsoft.Network/virtualNetworks/acctvnet" -> (known after apply)
  ~ vnet_subnets = [
      ...
      + (known after apply),
      + (known after apply),
      + (known after apply),
    ]

plan 的结果,获得 4 to add, 4 to destroy,terraform 认为要移除远端已经存在的 network,并重新建立新的

terraform 为何会有这样的判断?

  • .tf 中,旧的 module.network 已经不复存在,而 remote infra 跟 state 中都存在,所以 terraform 认为需要 destroy
  • .tf 中,多产生一个新的 module.private-network,state 中并不存在,所以 terraform 认为需要 create

完全符合之前讨论过 terraform 运作的逻辑

然而这样符合我们的需求吗?

实务上,我们尽量避免 rename resource / module,可以避免上述情形时常发生,然而这边的 state manipulation 就是在讲非常情形:我们真的被迫要做 module rename,然而不希望远端被 destroy + create

  • 也许远端 subnet 已经正在使用,destroy subnet 会影响其他服务
  • terraform module name 在 azure cloud 上其实是无意义的
    • module / resource name 只是用在 terraform .tf 与 state 中做 resource addressing 的路径名称
    • 对於 azure cloud 而言,resource / module 都没有意义,有意义的事 subnet 本身
    • 因为管理用的 resource addressing 变更,而影响到实际 remote resource 这点,可说是 terraform 的限制,或是说 terraform 本身就不是设计来做这类操作的

Fix rename with state mv

在回忆一下三个部分

  • .tf 被我们 rename module.network -> module.private-network
  • state 仍然是原先的 module.network
  • remote 透过 state 中的 module.network 的 metadata 做对应

我们只要能够 rename state 中的 module.network 变成 state module.private-network

  • state module. 的内容完全不变,但是名称变成 module.private-network
  • .tf 已经是 module.private-network,这样就能符合 state 内容
  • 由於 state module.private-network 的 metadata 与 module.network 完全一样,因此仍然是对应到原先的 azure cloud resource

於是我们进行 state mv,尝试看看

  • 养成能够 dry-run 就 dry-run 的好习惯,毕竟这边有 .tf 变更
  • 如果 .tf 写错,mv 也搬错 address 之後要在搬回来很麻烦
terragrunt state mv --dry-run SOURCE DESTINATION

terragrunt state mv --dry-run module.network.data.azurerm_resource_group.network module.private-network.data.azurerm_resource_group.network

Would move "module.network.data.azurerm_resource_group.network" to "module.private-network.data.azurerm_resource_group.network"

看起来跟我们预想的状况相符,於是我们为 module 底下所有 resource 进行 state mv

  • 记得 [0] 在 bash 中需要 quote
terragrunt state mv module.network.data.azurerm_resource_group.network module.private-network.data.azurerm_resource_group.network
terragrunt state mv "module.network.azurerm_subnet.subnet[0]" "module.private-network.azurerm_subnet.subnet[0]"
terragrunt state mv "module.network.azurerm_subnet.subnet[1]" "module.private-network.azurerm_subnet.subnet[1]"
terragrunt state mv "module.network.azurerm_subnet.subnet[2]" "module.private-network.azurerm_subnet.subnet[2]"
terragrunt state mv module.network.azurerm_virtual_network.vnet module.private-network.azurerm_virtual_network.vnet

由於这个范例刚好 mv 一整个 module,我们也可以

terragrunt state mv module.network module.network-private

检视 state list 中的最新状况

terragrunt state list

module.private-network.data.azurerm_resource_group.network
module.private-network.azurerm_subnet.subnet[0]
module.private-network.azurerm_subnet.subnet[1]
module.private-network.azurerm_subnet.subnet[2]
module.private-network.azurerm_virtual_network.vnet

plan & review

how state mv angers your colleage

从成功更改 state 这边开始,会产生多人协作问题

  • 本地的 .tf 已经 rename
  • remote 的 state 已经 state mv
  • 很有可能本地的 .tf 还没有 merge 到 master,而是在另外一个 branch 上执行 state manipulation
    • 因为如果 master 有做 auto plan & auto apply 的话,merge 进去会直接执行 4 destroy, 4 create 的 plan,这不是我们想要的

在本地 .tf merge 进去之前,master branch 上 plan,会出现 4 destroy, 4 create,这是为何?

  • state 已经变成本地 branch 的形状了,也就是变成 module.private-network
  • master 上面仍然是 module.network,这时他会找不到 state module.network,而发现多了 module.private-network,於是产生 4 destroy, 4 create

卡在这里的风险,就是其他同事刚好也在 plan 的话,看到 plan 一定超困惑,不晓得发生什麽事情

如何用 terraform 惹怒同事:改 state 不讲

所以 state manipulation PR 需要走特别的开发流程

  • nofity all team: 通知全 team 是第一步
  • 通知要更改的 root module path,别的成员不要来 plan / apply
  • rename 的 branch 与 PR 先发,进行 code review
  • review 完後,不走 PR merge + auto plan 与 auto apply,原因上面说过,会爆炸
  • 直接使用 PR 中的 branch,进行 state manipulation
  • state 变更完成後,执行 PR merge
  • auto plan 与 auto apply 会正常通过,因为 state 已经 rename 成新的名称了

Homework

  • 再把 module.private-network rename 回 module.private-network XD,我们是为改而改,现在请把它改回来
    • 需要重新 init 吗?
    • state list 与 plan 确定结果

<<:  系统弱点扫描工具-Tenable Nessus(中)

>>:  #5-中秋月亮晕起来!不规则Blob球球(CSS)

图的储存结构 - 十字链结串列 - DAY 22

前言 来到了困住我好几天的储存结构,希望可以让大家很快地看明白,假如看不懂可以再参考大话资料结构的7...

Day 5 用 Ruby 印东西

写在前面 虽然写铁人赛文章很有趣很有挑战性,但是常常很多时候都写得有点怕怕的,怕真实的状况跟自己写的...

[Day 15] Reactive Programming -Reactor(COLD VS HOT) -PART 2

前言 其实有一个特别的例子是just,直觉会认为just就是产生一个publisher等人来subs...

30 | WordPress 区块编辑器 | 延伸相关参考资料列表:

为了让这个教学系列可以适合持续变化的社会变化,会这最後这篇预留更新扩展的资源列表,会不停时更新在这个...

第八天:安装 IntelliJ IDEA

为了在後续章节里示范 TeamCity 可以怎麽协助我们建置专案及一系列的自动化,我们需要有一个可以...