Pulumi 让你用 TypeScript、Python 等真实编程语言来定义云基础设施(IaC),告别又长又难维护的 YAML。本文从安装到部署,手把手演示如何用代码管理可复用的云资源,并接入 CI/CD 实现自动化发布。
一、什么是 Pulumi,为什么不用纯 YAML
基础设施即代码(IaC)领域长期被 HCL(Terraform)统治,但 HCL 本质还是一种”配置语言”,表达循环、条件、抽象复用都很别扭。Pulumi 的思路是:直接用你本来就熟悉的通用编程语言写云资源,语言自带的类型系统、函数、模块和包管理全部可用。
这意味着你可以把”创建三个相似但略有不同的环境”写成一个循环,把”一套标准网络拓扑”封装成一个可导入的模块,还能在编译期就发现拼写错误——而不是等到 pulumi up 时才报错。
1.1 Pulumi 与 Terraform 核心差异
| 维度 | Pulumi | Terraform (HCL) |
|---|---|---|
| 编写语言 | TS / Python / Go / C# / Java | HCL(专用配置语言) |
| 抽象与复用 | 原生函数、类、模块 | module + 有限表达式 |
| 类型检查 | 编译期(取决于语言) | 运行时为主 |
| 状态后端 | Local / S3 / Pulumi Cloud | Local / 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 选择。




