Terraform 与 Pulumi 跨境运行不稳,隐患集中在三处
基础设施即代码(IaC)工具在跨境网络下很少一上来就整体失败,更常见的是某一个环节偶尔卡住:terraform init 下载 provider 到一半超时、plan 读远程 state 报错、apply 中途断线后状态锁没有释放。把这些现象归纳一下,隐患其实只集中在三处:provider 和插件的下载、远程 state 后端的读写、状态锁的获取与释放。本文对照 Terraform 与 Pulumi 的官方文档,逐一说明各自可以调整的配置。这里的参数名、默认值来自查阅时的文档,你使用的版本可能不同,请在自己的环境里再核对。
第一处:provider 下载。缓存、镜像、锁文件各自管什么
plugin_cache_dir 为什么能减少重复下载
每个工作目录执行 terraform init 都会单独下载 provider,体积大的 provider 在不稳定链路下很容易失败。官方 CLI 配置文档提供了 plugin_cache_dir,可以在多个工作目录之间共享同一份下载结果,目录需要预先存在;也可以用环境变量 TF_PLUGIN_CACHE_DIR。
# ~/.terraformrc
plugin_cache_dir = "$HOME/.terraform.d/plugin-cache"
filesystem_mirror 与 network_mirror 怎么选
文档列出了 provider_installation 的三种安装方式:direct 从源头 registry 下载,filesystem_mirror 从本地目录读取,network_mirror 从实现了镜像协议的 HTTPS 服务读取。每种方式都支持 include 与 exclude,且排除规则优先。对团队来说,比较稳妥的做法是用 terraform providers mirror 把锁文件里列出的 provider 预先导出到一个目录,再把这个目录作为 filesystem_mirror 挂在 CI 里,构建时就不再依赖跨境下载。
provider_installation {
filesystem_mirror {
path = "/opt/terraform/providers"
include = ["registry.terraform.io/*/*"]
}
direct {
exclude = ["registry.terraform.io/*/*"]
}
}
这段示例把路径、匹配规则都当作占位,请按你的目录和需求调整;使用镜像目录时仍然会校验依赖锁文件,不要为了省事开启文档里标注为特殊情况才使用的 plugin_cache_may_break_dependency_lock_file。
第二处:远程 state 后端的读写重试
state 存在境外的对象存储里,每次 plan、apply 都要先读、后写。Terraform 的 S3 后端提供了几项与网络直接相关的设置:max_retries 控制 AWS API 请求遇到可重试错误时的重试次数,文档标注默认值为 5;retry_mode 可选 standard 或 adaptive;http_proxy、https_proxy 和 no_proxy 用于指定访问 AWS API 时的代理。这几项设置直接写在 backend 配置里,可以给后端单独配置,不需要影响整个终端环境。
Pulumi 自管后端怎么选
官方文档说明,除默认的 Pulumi Cloud 之外,还支持 S3、Azure Blob、GCS、兼容 S3 的服务、PostgreSQL 和本地文件系统等自管后端,登录命令形如 pulumi login s3://桶名,凭证保存在 ~/.pulumi/credentials.json。选择后端的原则很直接:把 state 放在和你运行环境同区域的存储里,而不是每次跨海读写。
| 环节 | Terraform | Pulumi |
|---|---|---|
| provider 与插件下载 | plugin_cache_dir、镜像目录 | 提前安装插件并在 CI 中缓存插件目录,具体位置以官方文档为准 |
| state 读写 | S3 后端的 max_retries、retry_mode | 选择同区域的自管后端 |
| 状态锁 | use_lockfile、-lock-timeout | 由所用后端类型决定 |
第三处:状态锁,断线之后怎么判断该不该强制释放
Terraform 在可以加锁的后端上,会先获取锁再修改 state。S3 后端的 use_lockfile 文档标注默认值为 false,而使用 DynamoDB 锁的 dynamodb_table 选项已被标注为逐步弃用,所以新建项目要按所用版本的文档选择锁方式。跨境链路下最常见的场景是:apply 执行中网络断了,进程退出,锁没有来得及释放,下一次执行就报 state 已被锁定。
- 先确认真的没有其他人或其他流水线在执行。报错信息里会带锁的持有者和创建时间,先核对。
- 正常等待用
-lock-timeout=60s这类参数给锁一点释放时间,避免并发流水线互相触发报错。 - 确认确实是残留的锁之后,再用
terraform force-unlock 锁ID释放,并且先检查 state 是否完整,不要在有人正在执行时强制释放。 - 释放后先跑
terraform plan看差异是否符合预期,再决定是否继续执行。
CI 里的部署步骤如果老是随机失败,可以对照GitHub Actions 构建随机失败的网络排查,和依赖安装超时时先区分镜像与网络的思路。
把出口固定下来,再判断是不是线路的问题
把缓存、镜像和锁都处理过之后,如果远程 state 读写仍然在固定时段失败,就值得怀疑出口线路。可以用 curl -w "%{time_connect} %{time_appconnect}\n" 请求对象存储的 endpoint,分时段记录。出口线路不稳定的话,不妨让执行机走一条固定的出口。NasaCode 提供面向开发机的稳定出口与独享 IP,适合执行 IaC 的那台机器,也方便云端访问控制只放行一个地址;效果如何,仍建议用前后的耗时记录自己判断。
小结
Terraform 与 Pulumi 的不稳定,分三块处理:provider 用缓存和镜像目录减少跨境下载,远程 state 用后端自带的重试和代理设置,状态锁先核对再释放。具体默认值和参数名以你使用版本的官方文档为准。









