Terragrunt backend migrate 命令详解:跨单元安全迁移 OpenTofu/Terraform 远端状态
2026/9/15 17:40:53 网站建设 项目流程

Terragrunt backend migrate 命令详解:跨单元安全迁移 OpenTofu/Terraform 远端状态

【免费下载链接】terragruntTerragrunt is a flexible orchestration tool that allows Infrastructure as Code written in OpenTofu/Terraform to scale.项目地址: https://gitcode.com/GitHub_Trending/te/terragrunt

terragrunt backend migrate是 Terragrunt 提供的远端状态迁移命令,用于将 OpenTofu/Terraform 的 state 从一个单元(unit)迁移到另一个单元。本指南围绕 官方命令参考 展开,结合仓库源码说明命令的典型使用场景、参数含义、两种迁移机制的底层原理与安全注意事项,读完即可在真实项目中安全地完成"重命名单元"类的 state 搬迁。

命令概览

命令的完整语法为(见 cli.go 中的usageText):

terragrunt backend migrate [options] <src-unit> <dst-unit>

其中<src-unit><dst-unit>分别为源单元和目标单元的相对路径,两者都是必填参数;若缺省,命令会直接打印 usage 提示并返回错误。该命令的定位是"把远端 state 从一个单元迁移到另一个单元",因此它既不改动 OpenTofu/Terraform 的工作目录内容,也不负责删除源单元目录——目录的复制与清理需要由你自行完成。

典型场景:基于 path_relative_to_include 的单元重命名

命令的核心使用场景,是当remote_state块的key使用了path_relative_to_include函数,而你恰好需要重命名某个单元目录时。

考虑如下目录结构:

- old-unit-name - terragrunt.hcl - root.hcl

其中root.hcl定义了远端状态后端:

# root.hcl remote_state { backend = "s3" generate = { path = "backend.tf" if_exists = "overwrite" } config = { bucket = "my-tofu-state" key = "${path_relative_to_include()}/tofu.tfstate" region = "us-east-1" encrypt = true dynamodb_table = "my-lock-table" } }

old-unit-name/terragrunt.hcl只是简单引入了根配置:

# old-unit-name/terragrunt.hcl include "root" { path = find_in_parent_folders("root.hcl") }

此时如果直接把old-unit-name目录改名为new-unit-name并运行terragrunt applypath_relative_to_include()的求值结果会随之改变,从而在new-unit-name下生成一个全新的 state key——旧 state 不会被自动带过来,两个单元会各自拥有独立的状态,这与直接重命名的直觉相悖。

正确做法是使用backend migrate显式迁移 state:

cp -R old-unit-name new-unit-name terragrunt backend migrate old-unit-name new-unit-name rm -rf old-unit-name

执行过程分三步:

  1. cp -R复制单元目录(此时new-unit-name还没有任何历史状态);
  2. backend migrateold-unit-name的远端 state 搬迁到new-unit-name的 key 下;
  3. rm -rf删除旧单元目录。

迁移完成后,新单元即可正常terragrunt apply,state 与旧单元保持一致。

命令行参数详解

backend migrate除两个位置参数外,还支持以下 flags(定义见 cli.go):

Flag环境变量类型说明
--forceTG_FORCEbool即使源 bucket 未开启版本控制,也强制执行 state 迁移(危险操作,详见下文)
--configTG_CONFIGstring指定使用的 Terragrunt 配置文件路径;注意该路径相对于每个源/目标单元自身的目录,而非当前工作目录(见 backend-migrate-config.mdx)
--download-dirTG_DOWNLOAD_DIRstring覆盖.terragrunt-cache下载目录位置(继承自通用 shared flags)

--force 的危险性

--force是官方明确标注为dangerous的开关(见 backend-migrate-force.mdx)。默认情况下,命令执行前会检查源 bucket 是否开启了版本控制:

  • 版本控制已开启:允许迁移。S3 对象移动 + DynamoDB 锁表条目搬迁的组合操作是可恢复的,旧对象版本仍保留在 bucket 中。
  • 版本控制未开启:命令直接拒绝执行,并提示src bucket is not versioned, refusing to migrate backend state. If you are sure you want to migrate the backend state anyways, use the --force flag(见 migrate.go)。

Gruntwork 官方建议始终为 state 存储资源开启版本控制,因为在无版本控制的情况下强制迁移,一旦迁移过程出现问题,可能导致不可逆的数据丢失。因此仅在确认风险可控时才使用--force

两种迁移机制

Terragrunt 会根据后端类型与两端配置的差异,在两条迁移路径中选择其一(见 migrate.mdx 与 remote_state.go 的实现)。

机制一:同后端 SDK 直移(首选)

源与目标单元的后端类型相同(例如都是 S3 或都是 GCS)时,Terragrunt 会直接调用对应云厂商 SDK,在两端之间移动 state 对象,全程不经过 OpenTofu/Terraform CLI。这是首选路径,速度更快、副作用更少。

以 S3 为例,s3/backend.go 中的Migrate实现展示了该路径的全部细节:

  • 解析源、目标两端的 bucket 与 key(含GetLockTableName()得到的 DynamoDB 锁表名);
  • 通过MoveS3ObjectIfNecessary将 S3 对象从源 bucket/key 移到目标 bucket/key(同 bucket 内为拷贝后删除,跨 bucket 则为复制);
  • 若目标配置声明了锁表,调用CreateTableItemIfNecessary在目标 DynamoDB 表创建锁条目;
  • 若源配置声明了锁表,调用DeleteTableItemIfNecessary删除源锁表条目。

即"对象 + 锁条目"两件事都会被完整迁移,保证目标单元迁移后立即可加锁使用。

机制二:OpenTofu/Terraform CLI 兜底(慢路径)

当满足以下任一条件时,Terragrunt 退化为使用 OpenTofu/Terraform CLI 完成迁移:

  • 源或目标后端类型不在 Terragrunt 原生支持范围内;
  • 两端后端类型不同(例如 S3 → GCS),或两端配置状态不一致。

该路径的实现位于 remote_state.go,逻辑为"拉取 + 推送"两步:

  1. 拉取(pull):在源单元目录下执行tofu state pull,把远端 state 输出写入临时文件(pullState,见 remote_state.go);
  2. 推送(push):在目标单元目录下执行tofu state push <临时文件>,将 state 推入目标后端(pushState,见 remote_state.go)。

需要注意的是,该路径不会删除源单元已有的 state——源对象仍保留在原位置,你需要自行处理旧 state 的清理。同时,整个流程经由 CLI 串行执行,速度一般慢于 SDK 直移。临时 state 文件会在迁移完成后由 defer 自动移除(见 remote_state.go)。

底层执行流程与源码佐证

backend migrate的完整调用链如下(对应 migrate.go):

  1. 路径规范化:通过util.CanonicalPath将源、目标单元路径转为绝对路径;
  2. 构建 runner 并查找单元runner.New构建执行器,随后用FindUnitByPath分别在栈中定位源、目标单元;任一单元缺失都会报错(src unit not found at .../dst unit not found at ...);
  3. 分别解析远端状态:源、目标各自通过configbridge.NewParsingContextconfig.ParseRemoteState解析出自己的remote_state配置;任一单元缺少remote_state块都会直接报错(missing remote state configuration for source/destination module);
  4. 版本控制检查:未指定--force时,调用源后端的IsVersionControlEnabled检查 bucket 版本控制状态;目标不存在(BucketDoesNotExistError)会被放行,但版本控制关闭则拒绝迁移;
  5. 执行迁移:进入srcRemoteState.Migrate,按上文"两种机制"分发。

关于环境隔离的实现细节

从源码注释可以确认一个值得注意的设计:源与目标各自持有独立的 Env 克隆v.WithEnvCloned(),见 migrate.go),解析阶段各单元通过auth-provider-cmd注入的凭证、TF_VAR_*等环境变量不会互相覆盖,迁移的 pull 与 push 阶段也分别在正确的一方环境下执行。这一设计的直接价值是:跨两个 AWS/GCP 账户的同云迁移(例如两侧分别由不同AWS_PROFILE选择)也能用同一套backend migrate命令完成,而不需要手工切换环境。

后端抽象接口

两种迁移机制的分流点位于RemoteState.Migrate(remote_state.go):remote.BackendName == dstRemote.BackendName时走后端原生的Migrate,否则走 CLI 拉推。原生后端需要实现 backend.go 中定义的Backend接口,包括IsVersionControlEnabledMigrateDeleteBootstrap等;当前仓库内置了s3gcsazurerm三类后端(见 remote_state.go 的注册表),其中 S3 与 GCS 提供了原生迁移能力,Azure 及其他后端则会落入 CLI 兜底路径。

使用注意事项与最佳实践

  • 迁移前确认版本控制:S3 bucket 务必开启版本控制;GCS 同理确认版本保留策略。这既是命令默认的前置检查,也是数据安全兜底。
  • 不要手动重命名含path_relative_to_include的单元:除非你确认 state key 不依赖单元路径,否则必须走backend migrate
  • CLI 兜底路径不清理源 state:机制二完成后需自行删除源 state 对象,避免源单元被误用或产生双份计费存储。
  • 注意--config的路径基准:该参数相对于各单元自身目录解析,而不是当前工作目录(见 backend-migrate-config.mdx)。
  • 先复制、再迁移、后删除:严格按照cp -Rbackend migraterm -rf的顺序操作,保证任何一步失败时旧单元仍完整可用,可随时回滚。

总结

terragrunt backend migrate为"重命名单元"这一高频操作提供了安全、自动化的 state 搬迁能力:同后端时走云 SDK 直移(快且完整,含锁表),跨后端或非原生后端时自动降级为 OpenTofu/Terraform CLI 拉推(慢但通用)。理解其参数(尤其--force的危险语义)、两种机制的差异与执行顺序,就能在实际项目中既快速又安全地完成 state 迁移,避免手动操作带来的数据丢失风险。

【免费下载链接】terragruntTerragrunt is a flexible orchestration tool that allows Infrastructure as Code written in OpenTofu/Terraform to scale.项目地址: https://gitcode.com/GitHub_Trending/te/terragrunt

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询