运维笔记

Terraform 迁移到 Pulumi 实战指南:我为什么放弃 HCL 拥抱 Python

Cloud & DevOps 技术可视化

先说结论:谁在逼你迁移?

去年我们团队接手了一个烂摊子——300 多个 Terraform 模块,HCL 写得像天书,变量依赖能绕晕人。每次加个新环境,都要改一堆 .tfvars,而且 Terraform 的 countfor_each 逻辑,说实话,写多了真头疼。

Pulumi 不是银弹,但如果你团队里有 Python/TypeScript 工程师,迁移后效率提升是实打实的。我亲自带队从 Terraform 迁移到 Pulumi,踩了无数坑,今天把血泪经验全倒出来。

核心差异:一张表说清楚

维度TerraformPulumi
语言HCL (领域特定语言)Python/TypeScript/Go/C#/Java
状态管理本地/远程 state 文件云后端 (Pulumi Cloud/自托管)
Provider 生态2000+ 官方+社区1800+ (含 Terraform Provider 桥接)
循环/条件count, for_each, 模板原生语言 for/if/函数
调试体验terraform console + 日志IDE 断点 + print
学习曲线中等 (HCL 语法)低 (通用语言)
测试框架Terratest (第三方)原生测试框架 + 沙箱
企业支持HashiCorp (商业版)Pulumi (商业版)
开源协议BSL (非完全开源)Apache 2.0

迁移策略:两条路我们都试过

方案一:全量重写(推荐,但累)

适合小项目或模块化好的项目。我们第一个试点是 CI/CD 流水线,就 10 个资源。

Terraform 代码(HCL):

resource "aws_s3_bucket" "data" {
  bucket = "my-app-data-${var.env}"
  tags = {
    Environment = var.env
    ManagedBy   = "Terraform"
  }
}

resource "aws_s3_bucket_versioning" "data" {
  bucket = aws_s3_bucket.data.id
  versioning_configuration {
    status = "Enabled"
  }
}

Pulumi 代码(Python):

import pulumi
from pulumi_aws import s3

env = pulumi.get_stack()

bucket = s3.Bucket("data",
    bucket=f"my-app-data-{env}",
    tags={
        "Environment": env,
        "ManagedBy": "Pulumi",
    })

s3.BucketVersioning("data-versioning",
    bucket=bucket.id,
    versioning_configuration=s3.BucketVersioningVersioningConfigurationArgs(
        status="Enabled",
    ))

直观感受:Python 里 if/elsefor 循环随便用,不用学 HCL 那套蹩脚的条件语法。

方案二:状态迁移(pulumi-terraform-migrate)

这个工具我们试了,效果还行,但有限制。

# 导出 Terraform 状态
terraform state pull > terraform.tfstate

# 使用 Pulumi 迁移工具
pulumi terraform-migrate \
  --state-file terraform.tfstate \
  --stack-name production

# 检查迁移后的状态
pulumi stack export

坑点:

  • 只支持单层状态,嵌套 module 会翻车
  • 资源别名映射不是 100% 准确,需要手动修复
  • 我们生产环境 50 个资源迁移后,有 3 个需要手动 import

真香时刻:Pulumi 让我爽到的点

1. 动态配置不再需要 HCL 模板

Terraform 里生成一堆类似资源要写死循环,Pulumi 里直接 Python 生成:

def create_ec2_instances(env, count):
    instances = []
    for i in range(count):
        name = f"web-{env}-{i:03d}"
        instances.append(aws.ec2.Instance(name,
            ami="ami-0c55b159cbfafe1f0",
            instance_type="t3.micro",
            tags={"Name": name, "Env": env}))
    return instances

# 生产环境开 10 台,测试环境开 2 台
instances = create_ec2_instances("prod", 10) if env == "prod" else create_ec2_instances("test", 2)

2. 调试体验碾压

Terraform 调试基本靠 terraform consoleTF_LOG=debug 日志,Pulumi 直接在 IDE 里打断点,变量值一目了然。有一次我们排查一个 IAM 策略问题,Pulumi 下 10 分钟定位,Terraform 得半小时。

3. 测试框架原生支持

import pulumi
from pulumi_aws import ec2

def test_vpc_created():
    # 创建资源但不实际部署
    vpc = ec2.Vpc("test-vpc", cidr_block="10.0.0.0/16")
    # 断言
    assert vpc.cidr_block == "10.0.0.0/16"

Terraform 下你得装 Terratest,写 Go 测试,门槛高不少。

翻车实录:迁移中遇到的坑

1. Provider 版本不一致

Pulumi 的 AWS Provider 版本和 Terraform 的 AWS Provider 版本号不同步。我们迁移时发现 aws_s3_bucket_public_access_block 在 Pulumi 里参数名略有变化,排查了 2 小时。

解决方案: 先在小环境测试,逐个资源验证,别一次性全量迁移。

2. 状态锁冲突

Pulumi 默认用 Pulumi Cloud 管理状态,如果团队多人同时操作,状态锁机制不如 Terraform 的 DynamoDB 锁成熟。我们遇到过两次锁释放失败的情况。

解决方案: 自建 Pulumi 后端(S3 + DynamoDB),配置和 Terraform 类似。

3. 社区 Provider 质量参差

Terraform 的社区 Provider 经过 HashiCorp 审核,Pulumi 的桥接 Provider 质量不一。我们用的 pulumi-kubernetes 有个 bug,导致 Ingress 配置一直报错。

解决方案: 优先用官方 Provider,社区 Provider 先在测试环境跑一周。

FAQ:你们问得最多的问题

Q:迁移成本高吗?需要全员重学? A:如果团队有 Python/TypeScript 基础,学习成本很低。我们团队 10 人,2 周上手。但迁移代码本身耗时,300 个模块我们花了 3 周。

Q:Pulumi 的 Provider 比 Terraform 少? A:数据上 Terraform 有 2000+ 官方+社区 Provider,Pulumi 是 1800+。但 Pulumi 有 Terraform 桥接,理论上能用大部分 Terraform Provider。实际体验:桥接的稳定性不如原生。

Q:生产环境迁移有风险吗? A:有。我们建议分批次迁移——先迁移非关键资源(如开发环境 VPC、测试数据库),稳定后再动生产。状态迁移工具不是万能的,一定要手动验证。

Q:Pulumi 比 Terraform 快吗? A:部署速度差不多,但开发效率 Pulumi 明显快。我们写同个模块,Pulumi 比 Terraform 快 30-40%。调试效率提升更明显。

Q:Pulumi 的定价如何? A:开源版免费,商业版按用户收费。Terraform Cloud 也类似。小团队用开源版完全够用,我们目前就用的开源版+自建状态后端。

最后建议

如果你团队全是 HCL 老手,项目不大也不复杂,别折腾迁移。但如果你团队有通用语言背景,或者项目开始变得复杂(条件逻辑多、循环嵌套深),Pulumi 值得一试。

我的选择: 新项目一律 Pulumi,旧项目逐步迁移。别一次性全量迁移,分批来,先吃螃蟹的团队先享受效率提升。