Chapter 01

Pulumi 简介与快速入门

告别 HCL 的局限,用你熟悉的编程语言管理云基础设施

IaC 的发展与 HCL 的局限性

基础设施即代码(IaC)简史

基础设施即代码(Infrastructure as Code)是将云基础设施的配置用代码文件描述,并通过代码管理其生命周期的实践。IaC 让基础设施像应用代码一样可版本化、可审查、可自动化部署。

Terraform 在 2014 年普及了 IaC 理念,引入了 HCL(HashiCorp Configuration Language)——一种专门为基础设施配置设计的声明式语言。HCL 语法简洁,入门容易,极大降低了 IaC 的学习门槛,迅速成为事实标准。

HCL 在复杂场景下的痛点

然而随着基础设施规模增长,HCL 的局限性开始显现:

循环能力有限
HCL 的 count 和 for_each 功能单一,无法嵌套循环,不能像编程语言一样灵活处理复杂的迭代逻辑。
抽象复用困难
模块(Module)是唯一的抽象机制,但无法像函数/类一样传递复杂行为,模块之间的组合能力有限。
缺乏测试支持
无法用标准测试框架对 HCL 代码进行单元测试,只能依赖真实部署(Terratest)验证,成本高且慢。
IDE 支持弱
HCL 的类型信息有限,IDE 自动补全不如编程语言完善,错误只能在运行时发现。
学习成本
开发者需要额外学习一门专用语言(HCL),而团队中的 Python/TypeScript 开发者无法直接利用已有技能。

Pulumi 的核心理念

用真实编程语言写 IaC

Pulumi 的核心思路是:不发明新语言,用你已经会的语言写基础设施。Pulumi 程序就是一个普通的 Python/TypeScript/Go/C# 程序,只是它调用了 Pulumi SDK 提供的资源构造器,而不是业务逻辑。

Terraform HCL

# 创建多个 S3 Bucket(HCL)
resource "aws_s3_bucket" "buckets" {
  for_each = toset([
    "logs", "assets", "backups"
  ])
  bucket = "my-${each.key}"
}

Pulumi Python

# 创建多个 S3 Bucket(Python)
import pulumi_aws as aws

names = ["logs", "assets", "backups"]
buckets = [
    aws.s3.Bucket(f"my-{name}")
    for name in names
]

Pulumi 与同类工具对比

工具语言状态管理多云支持测试能力
Pulumi Python / TS / Go / C# / Java Pulumi Cloud(免费)或 Self-Hosted 150+ Provider 原生(pytest/jest)
Terraform HCL(专用语言) Terraform Cloud 或 S3+DynamoDB 3000+ Provider Terratest(Go)
AWS CDK Python / TS / Java / C# CloudFormation 仅 AWS 原生
Crossplane YAML / Go(XRD) Kubernetes etcd 多云 有限
Pulumi vs Terraform:如何选择?

选 Pulumi:团队是开发者导向(Python/TS/Go 熟练)、基础设施逻辑复杂(需要函数抽象/测试)、希望享受 IDE 完整支持。选 Terraform:团队运维背景、需要最丰富的 Provider 生态(3000+)、已有大量 HCL 代码存量。

安装 Pulumi CLI 与语言 SDK

安装 Pulumi CLI

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

# Linux / macOS(脚本安装)
curl -fsSL https://get.pulumi.com | sh

# Windows(PowerShell)
winget install pulumi

# 验证安装
pulumi version
# v3.x.x

安装语言运行时与 SDK

# ── Python 环境 ──
# 需要 Python 3.8+
python3 --version

# 创建虚拟环境
python3 -m venv venv
source venv/bin/activate  # Linux/macOS
# Windows: venv\Scripts\activate

# 安装 Pulumi AWS SDK
pip install pulumi pulumi-aws

# ── TypeScript 环境 ──
# 需要 Node.js 18+
node --version

# 安装 Pulumi TypeScript SDK(项目初始化时自动安装)
npm install @pulumi/pulumi @pulumi/aws

配置 Pulumi Cloud(免费状态托管)

# 登录 Pulumi Cloud(免费,托管状态)
pulumi login
# 会打开浏览器访问 app.pulumi.com 完成授权

# 也可以使用本地文件系统(不推荐生产)
pulumi login --local

# 使用自托管 S3 后端
pulumi login s3://my-pulumi-state-bucket

# 查看当前登录状态
pulumi whoami

配置云凭证

# ── AWS 凭证配置 ──
# 方式1:使用 AWS CLI 配置
aws configure
# AWS Access Key ID: AKIAIOSFODNN7EXAMPLE
# AWS Secret Access Key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# Default region name: us-east-1

# 方式2:环境变量(CI/CD 推荐)
export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/..."
export AWS_REGION="us-east-1"

# ── GCP 凭证配置 ──
gcloud auth application-default login

# ── Azure 凭证配置 ──
az login

创建第一个 Pulumi 项目

# 使用模板创建新项目(交互式)
mkdir my-infra && cd my-infra
pulumi new aws-python   # Python + AWS
# 或
pulumi new aws-typescript  # TypeScript + AWS

# 项目创建过程中会询问:
# project name: my-infra
# project description: My first Pulumi project
# stack name: dev   ← Stack 是环境的概念
# aws:region: us-east-1

Python 项目结构

my-infra/ ├── __main__.py # 主程序入口(定义所有资源) ├── Pulumi.yaml # 项目元数据(name/runtime/description) ├── Pulumi.dev.yaml # dev Stack 的配置值(非敏感) ├── requirements.txt # Python 依赖(pulumi、pulumi-aws 等) ├── venv/ # Python 虚拟环境(.gitignore 忽略) └── .gitignore

TypeScript 项目结构

my-infra/ ├── index.ts # 主程序入口 ├── Pulumi.yaml # 项目元数据 ├── Pulumi.dev.yaml # dev Stack 配置 ├── package.json # Node.js 依赖 ├── tsconfig.json # TypeScript 配置 ├── node_modules/ # (.gitignore 忽略) └── .gitignore

第一次 pulumi up

以 Python 为例,__main__.py 默认内容已包含一个 S3 Bucket 示例:

# __main__.py(pulumi new 生成的默认内容)
import pulumi
import pulumi_aws as aws

# 创建 S3 Bucket
bucket = aws.s3.BucketV2(
    "my-bucket",
    tags={"Environment": "Dev", "ManagedBy": "Pulumi"},
)

# 导出 Bucket 名称(可在 pulumi stack output 中查看)
pulumi.export("bucket_name", bucket.id)
# 预览变更(不执行)
pulumi preview

# 部署基础设施
pulumi up

# pulumi up 的输出示例:
# Previewing update (dev):
#
#      Type                 Name           Plan
#  +   pulumi:pulumi:Stack  my-infra-dev   create
#  +   aws:s3:BucketV2      my-bucket      create
#
# Do you want to perform this update? yes
#
# Outputs:
#   bucket_name: "my-bucket-a1b2c3d4"
#
# Resources:
#   + 2 created
#
# Duration: 8s

# 查看输出值
pulumi stack output bucket_name

# 销毁资源
pulumi destroy

Pulumi 工作流概览

┌─────────────────────────────────────────────────────┐ │ Pulumi 工作流 │ │ │ │ 1. pulumi login │ │ 登录 Pulumi Cloud(或本地/S3 后端) │ │ ↓ │ │ 2. pulumi new <template> │ │ 创建项目(选择语言+云平台模板) │ │ ↓ │ │ 3. 编写 __main__.py / index.ts │ │ 用 Python/TypeScript 定义资源 │ │ ↓ │ │ 4. pulumi preview │ │ 预览变更(不执行,查看 +/-/~ 操作) │ │ ↓ │ │ 5. pulumi up │ │ 执行变更,创建/修改/删除云资源 │ │ ↓ │ │ 6. pulumi stack output │ │ 查看输出值(IP/DNS/Bucket名等) │ │ ↓ │ │ 7. pulumi destroy(需要时) │ │ 销毁 Stack 中的所有资源 │ └─────────────────────────────────────────────────────┘

核心名词解释

Stack(栈)
同一套 Pulumi 程序的一个独立部署实例,通常对应一个环境(dev/staging/prod)。每个 Stack 有独立的配置值和 State。命令:pulumi stack init devpulumi stack select prod
Program(程序)
你编写的 Python/TypeScript/Go 代码文件,定义了基础设施中应该有哪些资源以及它们的属性。Pulumi 运行时会执行这个程序,记录所有资源注册操作,计算变更计划。
State(状态)
Pulumi 记录的已部署资源的快照(类似 Terraform 的 tfstate)。默认存储在 Pulumi Cloud,也可以存储在 S3、Azure Blob 或本地文件。State 是 Pulumi 计算增量变更的依据。
Preview(预览)
pulumi preview 命令的输出,展示将要执行的所有变更(创建/更新/删除),类似 Terraform 的 terraform plan。执行 pulumi up 前建议先预览。
Resource(资源)
基础设施中的一个对象,如 AWS S3 Bucket、EC2 实例、Kubernetes Deployment。Pulumi 中通过实例化 SDK 提供的资源类来声明,如 aws.s3.BucketV2("my-bucket")
Output<T>
资源创建后才能知道的值(如自动分配的 IP、ID、ARN)。Pulumi 用 Output<T> 类型包装这类值,确保在资源创建完成后才读取。类似 Promise,需要用 apply() 方法转换。
Provider(提供者)
与云平台 API 交互的插件(如 pulumi-aws、@pulumi/kubernetes)。Pulumi 会自动下载项目 requirements.txt 或 package.json 中声明的 Provider。
Pulumi 常用命令速查

本章小结

本章核心要点