Pulumi 实战:用 TypeScript 定义云基础设施

Pulumi 让你用 TypeScript、Python 等真实编程语言来定义云基础设施(IaC),告别又长又难维护的 YAML。本文从安装到部署,手把手演示如何用代码管理可复用的云资源,并接入 CI/CD 实现自动化发布。

一、什么是 Pulumi,为什么不用纯 YAML

基础设施即代码(IaC)领域长期被 HCL(Terraform)统治,但 HCL 本质还是一种”配置语言”,表达循环、条件、抽象复用都很别扭。Pulumi 的思路是:直接用你本来就熟悉的通用编程语言写云资源,语言自带的类型系统、函数、模块和包管理全部可用。

这意味着你可以把”创建三个相似但略有不同的环境”写成一个循环,把”一套标准网络拓扑”封装成一个可导入的模块,还能在编译期就发现拼写错误——而不是等到 pulumi up 时才报错。

1.1 Pulumi 与 Terraform 核心差异

维度PulumiTerraform (HCL)
编写语言TS / Python / Go / C# / JavaHCL(专用配置语言)
抽象与复用原生函数、类、模块module + 有限表达式
类型检查编译期(取决于语言)运行时为主
状态后端Local / S3 / Pulumi CloudLocal / S3 / Consul 等
多云支持同构多 Provider同构多 Provider

二、环境准备与安装

Pulumi 本体是一个轻量的 CLI,真正的逻辑跑在各语言的 SDK 里。以 macOS 为例,用 Homebrew 一条命令即可装好 CLI:

# macOS / Linux (Homebrew)
brew install pulumi/tap/pulumi

# Windows (Chocolatey)
choco install pulumi

# 或用 npm 全局安装(适合前端栈团队)
npm install -g @pulumi/pulumi

# 校验版本
pulumi version

随后登录状态后端。本地练习可用 pulumi login --local,团队协作用 Pulumi Cloud 或自建 S3 后端即可。

三、第一个项目:用 TypeScript 定义一个静态站点

下面用 AWS 举例,定义一个 S3 存储桶并开启静态网站托管。注意依赖要先初始化:npm install @pulumi/pulumi @pulumi/aws。

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

// 1. 创建存储桶
const bucket = new aws.s3.BucketV2("site-bucket", {
  bucket: "my-static-site-2026",
});

// 2. 关闭公共访问封锁(演示用,生产请走 CloudFront OAC)
const publicAccess = new aws.s3.BucketPublicAccessBlock("site-public", {
  bucket: bucket.id,
  blockPublicAcls: false,
  restrictPublicBuckets: false,
});

// 3. 配置静态网站托管
const website = new aws.s3.BucketWebsiteConfigurationV2("site-website", {
  bucket: bucket.id,
  indexDocument: { suffix: "index.html" },
});

// 4. 导出可访问的域名
export const websiteEndpoint = website.websiteEndpoint;

保存为 index.ts 后,执行 pulumi up。Pulumi 会先打印一份”预览(preview)”,列出将创建/修改/删除的资源,确认无误再回车落地。这正是 IaC 相比手动点击控制台最安全的地方。

四、用 Component 封装可复用模块

真实项目里,网络拓扑、安全组、日志桶往往是”一套模板到处用”。Pulumi 允许你用类把一组资源打包成 Component Resource,像调用函数一样复用:

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

// 自定义组件:一套标准日志桶
export class LogBucket extends pulumi.ComponentResource {
  public readonly arn: pulumi.Output;

  constructor(name: string, opts?: pulumi.ComponentResourceOptions) {
    super("custom:aws:LogBucket", name, {}, opts);

    const bucket = new aws.s3.BucketV2(name, {
      bucket: `${name}-log`,
      forceDestroy: false,
    }, { parent: this });

    this.arn = bucket.arn;
    this.registerOutputs({ arn: this.arn });
  }
}

// 使用:两行拉起两个环境的标准日志桶
const devLog = new LogBucket("dev-log");
const prodLog = new LogBucket("prod-log");

五、状态管理与多环境

Pulumi 用”栈(Stack)”区分环境,比如 dev 和 prod 各自一份状态与配置。配置文件 Pulumi.prod.yaml 里只放变量,敏感信息走加密的 secret:

# 登录状态后端(以 Pulumi Cloud 为例)
pulumi login

# 新建生产栈
pulumi stack init prod

# 配置普通变量
pulumi config set aws:region ap-southeast-1

# 配置加密密钥(不会明文落盘)
pulumi config set --secret dbPassword "S3cr3t!"

# 切换环境并部署
pulumi stack select prod
pulumi up

六、CI/CD 集成:GitHub Actions 自动部署

IaC 最大的价值是”和代码一起走流水线”。把它接进 GitHub Actions 持续集成 后,合并到 main 分支即自动预览并部署,杜绝”本地能跑、线上没更”的尴尬。下面是一个最小工作流:

# .github/workflows/deploy.yml
name: infra-deploy
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci
      - uses: pulumi/actions@v6
        with:
          command: up
          stack-name: prod
        env:
          PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

七、与容器化部署协同

Pulumi 不只是”建桶建网”,它能直接编排容器与编排平台。你可以用它同时声明 Docker 镜像构建 与 Kubernetes 集群 里的 Deployment,让”镜像从哪来、跑在哪个集群、暴露什么端口”在同一份代码里一目了然,避免基础设施和部署脚本各说各话。

八、常见踩坑与最佳实践

8.1 跳过预览直接 up

pulumi up 默认会先给预览再等确认。在脚本里务必保留这一步,或用 --diff 审查资源变更。误删生产库往往就源于”一路回车”。

8.2 把密钥写进代码

永远用 pulumi config set --secret 或云厂商的 Secret 服务,不要把 AK/SK 硬编码进 index.ts 再提交仓库。Pulumi 的 secret 在状态文件里是密文存储的。

8.3 状态文件损坏或并发冲突

团队共用一个栈时,务必走带锁的后端(Pulumi Cloud / S3+DynamoDB)。若担心状态丢失,可参考 Velero 集群备份 的思路定期兜底快照。出现锁残留时用 pulumi cancel 清理卡住的更新。

九、总结

Pulumi 把云基础设施变成”用真实语言写的程序”,复用、类型安全、测试都不再是奢望。配合多栈配置与 CI/CD,它能在几分钟内把一整套可复现的环境交付到任意云上。对于已经用 TS/Python 的主力团队来说,这是比 HCL 更顺手的下一代 IaC 选择。

上一篇 Web Components 实战:用原生自定义元素构建可复用 UI
下一篇 Kubernetes 准入控制实战:用 ValidatingAdmissionPolicy 守好集群边界
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]