Pulumi IaC 命令
Pulumi 命令实战:项目初始化、配置与栈管理、预览部署、状态操作与策略合规。
学习目标
本文是「云计算」模块的第 53 篇,难度定位为入门。重点内容:Pulumi 命令实战:项目初始化、配置与栈管理、预览部署、状态操作与策略合规。
主要章节:
- 安装与配置
- 项目创建
- 配置管理
- 堆栈管理
- 部署与预览
- 输出与查询
- ……共 13 个章节
安装与配置
基本写法:安装 Pulumi CLI
curl -fsSL https://get.pulumi.com | sh
# Linux/macOS 一键安装
curl -fsSL https://get.pulumi.com | sh
基本写法:Windows 安装
winget install pulumi.pulumi
# Windows 通过 winget 安装
winget install pulumi.pulumi
基本写法:查看版本
pulumi version
# 查看当前 Pulumi 版本
pulumi version
基本写法:登录 Pulumi 服务
pulumi login
# 登录 Pulumi 云服务
pulumi login
基本写法:本地后端登录
pulumi login file://<路径>
# 使用本地文件系统作为状态后端
pulumi login file://~/.pulumi
基本写法:自托管后端
pulumi login <URL>
# 登录自托管的 Pulumi 服务
pulumi login https://pulumi.example.com
项目创建
基本写法:创建新项目
pulumi new <模板> [--name <名称>]
# 交互式创建 AWS Python 项目
pulumi new aws-python
基本写法:创建 TypeScript AWS 项目
pulumi new aws-typescript
# 创建 AWS TypeScript 模板项目
pulumi new aws-typescript --name my-app --description "My AWS App"
基本写法:创建 GCP 项目
pulumi new gcp-typescript
# 创建 GCP TypeScript 项目
pulumi new gcp-typescript
基本写法:创建 Azure 项目
pulumi new azure-typescript
# 创建 Azure TypeScript 项目
pulumi new azure-typescript
基本写法:从模板创建
pulumi new https://github.com/<user>/<repo>
# 从 GitHub 仓库创建项目
pulumi new https://github.com/pulumi/examples/tree/master/aws-py-eks
配置管理
基本写法:设置配置值
pulumi config set <键> <值>
# 设置普通配置
pulumi config set aws:region us-east-1
基本写法:设置敏感配置
pulumi config set <键> <值> --secret
# 加密保存数据库密码
pulumi config set dbPassword "MyPass123!" --secret
基本写法:查看所有配置
pulumi config
# 查看当前堆栈所有配置
pulumi config
基本写法:查看配置明文
pulumi config --show-secrets
# 查看包含密钥的配置(谨慎使用)
pulumi config --show-secrets
基本写法:获取单个配置
pulumi config get <键>
# 获取指定配置值
pulumi config get aws:region
基本写法:移除配置
pulumi config rm <键>
# 删除指定配置项
pulumi config rm dbPassword
堆栈管理
基本写法:创建堆栈
pulumi stack init <堆栈名>
# 创建生产堆栈
pulumi stack init production
基本写法:列出堆栈
pulumi stack ls
# 列出项目所有堆栈
pulumi stack ls
基本写法:选择堆栈
pulumi stack select <堆栈名>
# 切换到 dev 堆栈
pulumi stack select dev
基本写法:查看当前堆栈
pulumi stack
# 显示当前堆栈名称
pulumi stack
基本写法:删除堆栈
pulumi stack rm <堆栈名>
# 删除指定堆栈(需先清空资源)
pulumi stack rm staging
基本写法:导出堆栈状态
pulumi stack export --file <文件>
# 导出堆栈状态到本地文件
pulumi stack export --file state.json
部署与预览
基本写法:预览变更
pulumi preview
# 预览将要执行的变更
pulumi preview
基本写法:部署基础设施
pulumi up
# 部署到当前堆栈
pulumi up
基本写法:跳过确认部署
pulumi up --yes
# 自动确认部署(用于 CI/CD)
pulumi up --yes
基本写法:部署指定堆栈
pulumi up --stack <堆栈名>
# 部署到 production 堆栈
pulumi up --stack production --yes
基本写法:显示详细差异
pulumi up --diff
# 部署时显示详细 diff
pulumi up --diff
基本写法:销毁资源
pulumi destroy
# 销毁堆栈中所有资源
pulumi destroy
输出与查询
基本写法:查看输出
pulumi stack output
# 列出所有输出值
pulumi stack output
基本写法:查看单个输出
pulumi stack output <输出名>
# 获取指定输出值
pulumi stack output instanceId
基本写法:输出 JSON 格式
pulumi stack output --json
# JSON 格式输出便于脚本处理
pulumi stack output --json
基本写法:显示输出值的密钥
pulumi stack output <名称> --show-secrets
# 查看敏感输出值
pulumi stack output dbPassword --show-secrets
基本写法:在程序中获取输出
// 在 Pulumi 程序中引用其他堆栈的输出
import * as pulumi from "@pulumi/pulumi";
const infraStack = new pulumi.StackReference("myorg/infra/prod");
const vpcId = infraStack.getOutput("vpcId");
状态管理
基本写法:查看状态资源
pulumi stack --show-ids
# 查看堆栈中所有资源 ID
pulumi stack --show-ids
基本写法:导入资源
pulumi import <类型> <名称> <ID>
# 导入现有 EC2 实例到 Pulumi 管理
pulumi import aws:ec2/instance:Instance my-instance i-1234567890abcdef0
基本写法:导入并生成代码
pulumi import <类型> <名称> <ID> --out <目录>
# 生成导入资源的代码
pulumi import aws:ec2/instance:Instance my-instance i-1234567890abcdef0 --out imported
基本写法:删除状态中的资源
pulumi state delete <资源URN>
# 从状态中移除资源(不删除实际资源)
pulumi state delete "urn:pulumi:dev::my-app::aws:ec2/instance:Instance::my-instance"
基本写法:取消操作
pulumi cancel
# 取消正在进行的更新
pulumi cancel
策略与合规
基本写法:启用 Policy Pack
pulumi up --policy-pack <策略包路径>
# 部署时应用 Policy Pack
pulumi up --policy-pack ./policies
基本写法:强制执行策略
pulumi up --policy-pack <路径> --policy-pack-enforcement-level mandatory
# 强制策略(违规时阻止部署)
pulumi up \
--policy-pack ./policies \
--policy-pack-enforcement-level mandatory
基本写法:查看策略违规
pulumi preview --policy-pack <路径>
# 预览时仅检查策略违规
pulumi preview --policy-pack ./policies
基本写法:创建 Policy Pack
// policies/index.ts
import { PolicyPack } from "@pulumi/policy";
new PolicyPack("my-policy-pack", {
policies: [
{
name: "no-public-ec2",
description: "禁止 EC2 实例直接关联公网 IP",
enforcementLevel: "mandatory",
validateResource: (args, reportViolation) => {
if (args.type === "aws:ec2/instance:Instance") {
if (args.props.associatePublicIpAddress) {
reportViolation("EC2 不应直接关联公网 IP");
}
}
},
},
],
});
提供者配置
基本写法:配置 AWS 提供者
// 配置 AWS 提供者指定区域
import * as aws from "@pulumi/aws";
const provider = new aws.Provider("my-provider", {
region: "us-west-2",
});
基本写法:使用指定 profile
pulumi config set aws:profile <profile名>
# 使用特定 AWS profile
pulumi config set aws:profile production
基本写法:多区域部署
// 为不同区域创建不同提供者
import * as aws from "@pulumi/aws";
const usEastProvider = new aws.Provider("us-east", { region: "us-east-1" });
const usWestProvider = new aws.Provider("us-west", { region: "us-west-2" });
基本写法:自定义提供者端点
// 使用 LocalStack 端点本地测试
import * as aws from "@pulumi/aws";
const localstackProvider = new aws.Provider("localstack", {
endpoints: [{ hostname: "localhost", port: 4566, protocol: "http" }],
skipCredentialsValidation: true,
skipMetadataApiCheck: true,
});
CI/CD 集成
基本写法:设置 GitHub Actions
# .github/workflows/pulumi.yml
name: Pulumi
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 install
- uses: pulumi/actions@v5
with:
command: up
stack-name: production
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 }}
基本写法:使用 access token
pulumi login --token <token>
# 通过 token 非交互登录(用于 CI)
pulumi login --token pul-xxxxxxxxxx
基本写法:CI 模式
pulumi up --yes --non-interactive
# 非交互模式部署
pulumi up --yes --non-interactive
基本写法:使用 GitHub OIDC
# 通过 GitHub OIDC 部署到 AWS
permissions:
id-token: write
contents: read
jobs:
deploy:
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions
aws-region: us-east-1
- uses: pulumi/actions@v5
with:
command: up
stack-name: production
组件与抽象
基本写法:创建组件资源
// 创建可复用的组件资源
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
export class WebServer extends pulumi.ComponentResource {
public readonly instanceId: pulumi.Output<string>;
constructor(name: string, opts?: pulumi.ComponentResourceOptions) {
super("my:module:WebServer", name, {}, opts);
const sg = new aws.ec2.SecurityGroup(`${name}-sg`, {
ingress: [{ protocol: "tcp", fromPort: 80, toPort: 80, cidrBlocks: ["0.0.0.0/0"] }],
}, { parent: this });
const instance = new aws.ec2.Instance(`${name}-instance`, {
instanceType: "t3.micro",
ami: "ami-0c55b159cbfafe1f0",
vpcSecurityGroupIds: [sg.id],
}, { parent: this });
this.instanceId = instance.id;
this.registerOutputs({ instanceId: this.instanceId });
}
}
基本写法:使用组件
// 在主程序中使用自定义组件
import { WebServer } from "./webserver";
const web1 = new WebServer("web1");
const web2 = new WebServer("web2");
export const web1Id = web1.instanceId;
基本写法:导出多资源
// 同时导出多个相关资源
import * as aws from "@pulumi/aws";
const bucket = new aws.s3.Bucket("my-bucket");
const table = new aws.dynamodb.Table("my-table", {
attributes: [{ name: "id", type: "S" }],
hashKey: "id",
billingMode: "PAY_PER_REQUEST",
});
export const bucketName = bucket.id;
export const tableName = table.id;
模板与示例
基本写法:列出所有模板
pulumi new --list-templates
# 查看所有可用项目模板
pulumi new --list-templates
基本写法:使用 Kubernetes 模板
pulumi new kubernetes-typescript
# 创建 K8s TypeScript 项目
pulumi new kubernetes-typescript
基本写法:使用容器模板
pulumi new dockerfile
# 从 Dockerfile 创建项目
pulumi new dockerfile
基本写法:查看示例
pulumi new --list-templates | grep example
# 列出示例模板
pulumi new --list-templates
别名与重构
基本写法:重命名资源
// 通过 alias 重命名资源而不重建
import * as aws from "@pulumi/aws";
const bucket = new aws.s3.Bucket("new-name", {
// 其他属性
}, {
aliases: [{ name: "old-name" }],
});
基本写法:跨类型别名
// 跨类型迁移资源
const instance = new aws.ec2.Instance("my-instance", {
// ...
}, {
aliases: [{ type: "aws:ec2/instance:InstanceV1" }],
});
基本写法:父资源别名
// 修改父资源时保持子资源
const child = new SomeResource("child", { /* ... */ }, {
parent: newParent,
aliases: [{ parent: oldParent }],
});
基本写法:批量别名
// 多个别名同时使用
const resource = new SomeResource("name", { /* ... */ }, {
aliases: [
{ name: "old-name-1" },
{ name: "old-name-2" },
{ type: "old:type:Resource" },
],
});