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

Terraform 实战

销毁和放手是两回事

在 TT Lab 中继续学习

一句话总结

从配置中删掉资源块,意思是“销毁它”。“我们不再管理它了”这层意思,要用 removed 块另外说明。

为什么需要它——删除与放手曾经是同一句话

假设一个团队管理着三台数据库,现在要把其中一台移交给另一个团队。从代码中删掉那个块,看起来很自然。然后运行 plan,出现的却是 destroy 1。那是正在运行的数据库。

过去,这种情况是用命令解决的:手动敲一次把该地址从状态中移除的命令,然后再删除代码。这样能用,但有两个缺点。第一,没有记录。谁在什么时候把什么从状态中移除了,提交里留不下。第二,顺序一错就完了。如果先删代码就敲 apply,事情就到此为止了。

removed 块把这件事变成了声明。它写在代码中,所以要接受评审,会留在提交里,也不会有顺序颠倒的余地。

工作原理

# 1) 리소스 블록을 지운다
# 2) 그 자리에 removed 블록을 둔다
removed {
  from = local_file.legacy
}

运行计划后,输出是这样的。

# local_file.legacy will be removed from the OpenTofu state
# but will not be destroyed
Plan: 0 to add, 0 to change, 0 to destroy.

有两处需要读懂。第一,指示动作的词不是 destroy。第二,因此 Plan: 行的三个数字都是 0——意思是对实际资源什么都不做。

每个版本接收的参数不同。这是本模块必须带走的习惯:removed 块是否接收 lifecycle 这样的子块,取决于你所使用的版本。文档网站默认显示最新版本,所以文档里有,并不能保证你的工具也接受。确认的方法很简单——放进去,运行一次 plan,工具会自己回答。在实验的第 3 步,你要亲自询问这个 Pod 中的版本接收什么、不接收什么,并记录成表格。

只改名称是 moved 的工作。它很容易与 removed 混淆,但目的相反。moved 表示“是同一个对象,只是地址变了”,把状态条目迁移到新地址。没有它,工具就会把它读成旧名称消失、新名称出现,从而销毁后重新创建。是迁移了还是重新创建了,比较一下标识符就能立刻知道。

只想重新创建一个时,用 -replace。配置保持不变,把“只销毁并重新创建这个地址”放进计划。过去用 taint 做同样的事,区别很重要。taint 是先修改状态文件,再让下一次计划去读取它。如果标记之后忘了,就会让不相干的人在不相干的时间点应用这个替换。-replace 只作用于那一次执行,所以不会留下痕迹。即便如此,还是要了解 taint 和 untaint,是为了遇到别人标记过的状态时不会慌张。

在现场相遇的样子

最常见的事故是不清理 removed 块,就这么留着。事情做完后,那个块就是死代码,可它就这样一直留着,直到某天那个地址的资源又被需要时,它就复活了。“遗忘”与“创建”同时存在于同一个地址,计划就会被阻止。更讽刺的是,这个错误会通过配置验证(validate),只在计划中出现。因为把地址与状态对照,是计划阶段的工作。如果 CI 只运行 validate,就发现不了这个问题。

第二种是组织拆分。团队分家时,需要把其中一方的份额整个移交出去,而 removed 的 from 中也可以放模块地址,所以可以一次性放手一整个模块。不过,接收方把那些实际资源导入到自己的状态中是另一项工作,而且需要 provider 支持该资源的导入——并不是所有资源都支持。

第三种是所有权空缺的期间。从放手的时刻起,到接收方接手为止,那个实际资源不受任何代码管理。要让这期间没有人去动它,就必须用文档和日程把移交工作包起来。这不是工具能帮你做的事。

第四种是能否找回。把从状态中移除的内容重新纳入代码管理,需要导入(import),而如果 provider 没有实现该资源的导入,就没有办法。本实验中的文件资源正是这种情况。所以在放手之前,先确认一下“这是可以撤回的决定吗”更安全——如果无法撤回,就要留下副本,或者让移交方和接收方在同一时刻操作。

下一项实验要做什么

在 /root/tfa-removed 中,对移交过来的一个文件用 removed 只从状态中移除,并确认实际文件仍然保留。亲自询问这个版本的 removed 接收什么并整理成表格,用 moved 改名并与第 1 步的副本比较,确认标识符得到保持;接着收到“遗忘声明”与“创建声明”重叠时的错误,然后用 -replace 和 taint 只重新创建一个,最后把整个模块放手。