Pulumi堆栈管理Skill pulumi-stacks

Pulumi堆栈管理技能用于通过Pulumi基础设施即代码工具管理多个环境(如开发、测试、生产)的部署和配置。它支持环境隔离、配置管理、秘密管理、堆栈引用和云原生架构,适用于DevOps和云部署场景。关键词:Pulumi, 堆栈管理, 多环境部署, 基础设施即代码, DevOps, 云原生, 配置管理, 秘密加密, 堆栈引用, 状态管理。

云原生架构 0 次安装 0 次浏览 更新于 3/25/2026

名称: pulumi-stacks 用户可调用: false 描述: 使用Pulumi堆栈管理多个环境,用于开发、测试和生产部署。 允许工具: [Bash, Read]

Pulumi 堆栈

使用Pulumi堆栈管理多个环境和配置,实现跨开发、测试和生产的一致基础设施。

概述

Pulumi堆栈是Pulumi程序的隔离、独立可配置实例。每个堆栈都有自己的状态、配置和资源,使您能够将相同的基础设施代码部署到多个环境。

堆栈基础

创建和选择堆栈

# 初始化新项目
pulumi new aws-typescript

# 创建新堆栈
pulumi stack init dev

# 列出所有堆栈
pulumi stack ls

# 选择堆栈
pulumi stack select dev

# 显示当前堆栈
pulumi stack

# 移除堆栈
pulumi stack rm dev

堆栈配置

# 设置配置值
pulumi config set aws:region us-east-1
pulumi config set instanceType t3.micro

# 设置秘密值(加密)
pulumi config set --secret dbPassword mySecurePassword123

# 获取配置值
pulumi config get aws:region

# 列出所有配置
pulumi config

# 移除配置
pulumi config rm instanceType

堆栈配置文件

Pulumi.yaml(项目文件)

name: my-infrastructure
user-invocable: false
runtime: nodejs
description: 多环境基础设施

config:
  aws:region:
    description: 部署的AWS区域
    default: us-east-1
  instanceType:
    description: EC2实例类型
    default: t3.micro
  environment:
    description: 环境名称

Pulumi.dev.yaml(堆栈配置)

config:
  aws:region: us-east-1
  my-infrastructure:instanceType: t3.micro
  my-infrastructure:environment: 开发
  my-infrastructure:minSize: "1"
  my-infrastructure:maxSize: "3"
  my-infrastructure:enableMonitoring: "false"

Pulumi.staging.yaml

config:
  aws:region: us-east-1
  my-infrastructure:instanceType: t3.small
  my-infrastructure:environment: 测试
  my-infrastructure:minSize: "2"
  my-infrastructure:maxSize: "5"
  my-infrastructure:enableMonitoring: "true"

Pulumi.prod.yaml

config:
  aws:region: us-west-2
  my-infrastructure:instanceType: t3.medium
  my-infrastructure:environment: 生产
  my-infrastructure:minSize: "3"
  my-infrastructure:maxSize: "10"
  my-infrastructure:enableMonitoring: "true"
  my-infrastructure:backupRetention: "30"

在代码中读取配置

TypeScript配置

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

// 获取配置
const config = new pulumi.Config();
const instanceType = config.get("instanceType") || "t3.micro";
const environment = config.require("environment");
const minSize = config.getNumber("minSize") || 1;
const maxSize = config.getNumber("maxSize") || 3;
const enableMonitoring = config.getBoolean("enableMonitoring") || false;

// 获取秘密
const dbPassword = config.requireSecret("dbPassword");

// 使用配置
const instance = new aws.ec2.Instance("web-server", {
    instanceType: instanceType,
    ami: "ami-0c55b159cbfafe1f0",
    tags: {
        Name: `web-server-${environment}`,
        Environment: environment,
    },
    monitoring: enableMonitoring,
});

// 导出堆栈名称
export const stackName = pulumi.getStack();
export const instanceId = instance.id;

Python配置

import pulumi
import pulumi_aws as aws

# 获取配置
config = pulumi.Config()
instance_type = config.get("instanceType") or "t3.micro"
environment = config.require("environment")
min_size = config.get_int("minSize") or 1
max_size = config.get_int("maxSize") or 3
enable_monitoring = config.get_bool("enableMonitoring") or False

# 获取秘密
db_password = config.require_secret("dbPassword")

# 使用配置
instance = aws.ec2.Instance(
    "web-server",
    instance_type=instance_type,
    ami="ami-0c55b159cbfafe1f0",
    tags={
        "Name": f"web-server-{environment}",
        "Environment": environment,
    },
    monitoring=enable_monitoring,
)

# 导出输出
pulumi.export("stack_name", pulumi.get_stack())
pulumi.export("instance_id", instance.id)

环境特定资源

条件资源创建

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

const config = new pulumi.Config();
const environment = config.require("environment");
const enableHighAvailability = config.getBoolean("enableHA") || false;

// 创建VPC
const vpc = new aws.ec2.Vpc("main", {
    cidrBlock: "10.0.0.0/16",
    tags: {
        Name: `vpc-${environment}`,
        Environment: environment,
    },
});

// 生产环境获取多个可用区
const azCount = environment === "production" ? 3 : 1;
const subnets: aws.ec2.Subnet[] = [];

for (let i = 0; i < azCount; i++) {
    const subnet = new aws.ec2.Subnet(`subnet-${i}`, {
        vpcId: vpc.id,
        cidrBlock: `10.0.${i}.0/24`,
        availabilityZone: `us-east-1${String.fromCharCode(97 + i)}`,
        tags: {
            Name: `subnet-${environment}-${i}`,
            Environment: environment,
        },
    });
    subnets.push(subnet);
}

// 仅在生产环境创建NAT网关
let natGateway: aws.ec2.NatGateway | undefined;
if (environment === "production") {
    const eip = new aws.ec2.Eip("nat-eip", {
        vpc: true,
    });

    natGateway = new aws.ec2.NatGateway("nat", {
        allocationId: eip.id,
        subnetId: subnets[0].id,
        tags: {
            Name: `nat-${environment}`,
            Environment: environment,
        },
    });
}

// 创建RDS,仅生产环境使用多AZ
const db = new aws.rds.Instance("database", {
    engine: "postgres",
    engineVersion: "14.7",
    instanceClass: environment === "production" ? "db.t3.medium" : "db.t3.micro",
    allocatedStorage: environment === "production" ? 100 : 20,
    dbName: "myapp",
    username: "admin",
    password: config.requireSecret("dbPassword"),
    multiAz: environment === "production",
    backupRetentionPeriod: environment === "production" ? 30 : 7,
    skipFinalSnapshot: environment !== "production",
    tags: {
        Name: `db-${environment}`,
        Environment: environment,
    },
});

export const vpcId = vpc.id;
export const subnetIds = subnets.map(s => s.id);
export const dbEndpoint = db.endpoint;

堆栈引用

跨堆栈引用

// 基础设施堆栈(infra/index.ts)
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const vpc = new aws.ec2.Vpc("shared-vpc", {
    cidrBlock: "10.0.0.0/16",
    tags: {
        Name: "shared-vpc",
    },
});

const subnet = new aws.ec2.Subnet("shared-subnet", {
    vpcId: vpc.id,
    cidrBlock: "10.0.1.0/24",
    tags: {
        Name: "shared-subnet",
    },
});

// 为其他堆栈导出
export const vpcId = vpc.id;
export const subnetId = subnet.id;
export const vpcCidr = vpc.cidrBlock;
// 应用堆栈(app/index.ts)
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

// 引用基础设施堆栈
const infraStack = new pulumi.StackReference("myorg/infra/prod");

// 从基础设施堆栈获取输出
const vpcId = infraStack.getOutput("vpcId");
const subnetId = infraStack.getOutput("subnetId");

// 使用引用值
const securityGroup = new aws.ec2.SecurityGroup("app-sg", {
    vpcId: vpcId,
    description: "应用的安全组",
    ingress: [{
        protocol: "tcp",
        fromPort: 80,
        toPort: 80,
        cidrBlocks: ["0.0.0.0/0"],
    }],
});

const instance = new aws.ec2.Instance("app-server", {
    instanceType: "t3.micro",
    ami: "ami-0c55b159cbfafe1f0",
    subnetId: subnetId,
    vpcSecurityGroupIds: [securityGroup.id],
    tags: {
        Name: "app-server",
    },
});

export const instanceIp = instance.publicIp;

堆栈引用命令

# 先部署基础设施堆栈
cd infra
pulumi stack select prod
pulumi up

# 然后部署应用堆栈
cd ../app
pulumi stack select prod
pulumi up

# 查看引用堆栈的输出
pulumi stack output --stack myorg/infra/prod

堆栈输出

导出堆栈输出

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

const config = new pulumi.Config();
const environment = config.require("environment");

// 创建资源
const vpc = new aws.ec2.Vpc("main", {
    cidrBlock: "10.0.0.0/16",
});

const bucket = new aws.s3.Bucket("app-bucket", {
    bucket: `myapp-${environment}-bucket`,
});

const db = new aws.rds.Instance("database", {
    engine: "postgres",
    instanceClass: "db.t3.micro",
    allocatedStorage: 20,
    dbName: "myapp",
    username: "admin",
    password: config.requireSecret("dbPassword"),
    skipFinalSnapshot: true,
});

// 导出输出
export const vpcId = vpc.id;
export const vpcCidr = vpc.cidrBlock;
export const bucketName = bucket.id;
export const bucketArn = bucket.arn;
export const dbEndpoint = db.endpoint;
export const dbPort = db.port;

// 导出计算值
export const dbConnectionString = pulumi.interpolate`postgresql://admin@${db.endpoint}/myapp`;

// 导出堆栈元数据
export const stackName = pulumi.getStack();
export const projectName = pulumi.getProject();
export const region = aws.getRegion().then(r => r.name);

访问堆栈输出

# 查看所有输出
pulumi stack output

# 获取特定输出
pulumi stack output vpcId

# 以JSON格式获取输出
pulumi stack output --json

# 在shell脚本中使用
VPC_ID=$(pulumi stack output vpcId)
echo "VPC ID: $VPC_ID"

# 导出到环境变量
export $(pulumi stack output --json | jq -r 'to_entries[] | "\(.key)=\(.value)"')

堆栈转换

全局资源转换

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

const config = new pulumi.Config();
const environment = config.require("environment");

// 注册全局转换以添加标签
pulumi.runtime.registerStackTransformation((args) => {
    if (args.type.startsWith("aws:")) {
        args.props.tags = {
            ...args.props.tags,
            Environment: environment,
            ManagedBy: "Pulumi",
            Stack: pulumi.getStack(),
        };
    }
    return {
        props: args.props,
        opts: args.opts,
    };
});

// 所有AWS资源自动获取标签
const vpc = new aws.ec2.Vpc("main", {
    cidrBlock: "10.0.0.0/16",
    // 标签将通过转换自动添加
});

const bucket = new aws.s3.Bucket("data", {
    // 标签将通过转换自动添加
});

资源特定转换

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

const config = new pulumi.Config();
const environment = config.require("environment");

// 转换以强制加密
const enforceEncryption = (args: pulumi.ResourceTransformationArgs) => {
    if (args.type === "aws:s3/bucket:Bucket") {
        args.props.serverSideEncryptionConfiguration = {
            rule: {
                applyServerSideEncryptionByDefault: {
                    sseAlgorithm: "AES256",
                },
            },
        };
    }

    if (args.type === "aws:rds/instance:Instance") {
        args.props.storageEncrypted = true;
    }

    return {
        props: args.props,
        opts: args.opts,
    };
};

pulumi.runtime.registerStackTransformation(enforceEncryption);

// 资源将自动加密
const bucket = new aws.s3.Bucket("data");
const db = new aws.rds.Instance("database", {
    engine: "postgres",
    instanceClass: "db.t3.micro",
    allocatedStorage: 20,
});

堆栈标签和组织

标签策略

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

const config = new pulumi.Config();
const environment = config.require("environment");
const project = pulumi.getProject();
const stack = pulumi.getStack();

// 定义常见标签
const commonTags = {
    Project: project,
    Environment: environment,
    Stack: stack,
    ManagedBy: "Pulumi",
    CostCenter: config.get("costCenter") || "engineering",
    Owner: config.get("owner") || "platform-team",
};

// 辅助函数合并标签
function mergeTags(resourceTags?: { [key: string]: string }): { [key: string]: string } {
    return {
        ...commonTags,
        ...resourceTags,
    };
}

// 使用一致标签
const vpc = new aws.ec2.Vpc("main", {
    cidrBlock: "10.0.0.0/16",
    tags: mergeTags({
        Name: `vpc-${environment}`,
        Type: "network",
    }),
});

const bucket = new aws.s3.Bucket("data", {
    tags: mergeTags({
        Name: `data-${environment}`,
        Type: "storage",
        Compliance: "required",
    }),
});

const db = new aws.rds.Instance("database", {
    engine: "postgres",
    instanceClass: "db.t3.micro",
    allocatedStorage: 20,
    tags: mergeTags({
        Name: `db-${environment}`,
        Type: "database",
        BackupRequired: "true",
    }),
});

堆栈导入和导出

导出堆栈状态

# 将堆栈状态导出到JSON
pulumi stack export > stack-state.json

# 导出到文件
pulumi stack export --file stack-backup.json

# 导出秘密为明文(小心使用!)
pulumi stack export --show-secrets > stack-with-secrets.json

导入堆栈状态

# 导入堆栈状态
pulumi stack import --file stack-state.json

# 从标准输入导入
cat stack-state.json | pulumi stack import

堆栈迁移

# 从旧堆栈导出
pulumi stack select old-stack
pulumi stack export --file old-stack.json

# 创建并导入到新堆栈
pulumi stack init new-stack
pulumi stack import --file old-stack.json

# 验证资源
pulumi preview

多区域部署

区域特定堆栈

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

const config = new pulumi.Config();
const awsConfig = new pulumi.Config("aws");
const region = awsConfig.require("region");
const environment = config.require("environment");

// 创建区域特定资源
const vpc = new aws.ec2.Vpc(`vpc-${region}`, {
    cidrBlock: "10.0.0.0/16",
    tags: {
        Name: `vpc-${environment}-${region}`,
        Region: region,
        Environment: environment,
    },
});

// 在us-east-1创建CloudFront分发
const usEast1Provider = new aws.Provider("us-east-1", {
    region: "us-east-1",
});

const certificate = new aws.acm.Certificate("cert", {
    domainName: `${environment}.example.com`,
    validationMethod: "DNS",
    tags: {
        Name: `cert-${environment}`,
        Environment: environment,
    },
}, { provider: usEast1Provider });

// 导出区域信息
export const deploymentRegion = region;
export const vpcId = vpc.id;
export const certificateArn = certificate.arn;

多区域堆栈配置

# Pulumi.us-east-1-prod.yaml
config:
  aws:region: us-east-1
  my-app:environment: 生产
  my-app:isPrimaryRegion: "true"

# Pulumi.us-west-2-prod.yaml
config:
  aws:region: us-west-2
  my-app:environment: 生产
  my-app:isPrimaryRegion: "false"

# Pulumi.eu-west-1-prod.yaml
config:
  aws:region: eu-west-1
  my-app:environment: 生产
  my-app:isPrimaryRegion: "false"

堆栈策略

保护资源

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

const config = new pulumi.Config();
const environment = config.require("environment");

// 保护生产数据库
const db = new aws.rds.Instance("database", {
    engine: "postgres",
    instanceClass: "db.t3.micro",
    allocatedStorage: 20,
    tags: {
        Name: `db-${environment}`,
    },
}, {
    protect: environment === "production",
});

// 保护生产存储
const bucket = new aws.s3.Bucket("data", {
    tags: {
        Name: `data-${environment}`,
    },
}, {
    protect: environment === "production",
});

保留资源

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

const config = new pulumi.Config();
const environment = config.require("environment");

// 在堆栈删除时保留生产数据库
const db = new aws.rds.Instance("database", {
    engine: "postgres",
    instanceClass: "db.t3.micro",
    allocatedStorage: 20,
    finalSnapshotIdentifier: environment === "production"
        ? `final-snapshot-${Date.now()}`
        : undefined,
    skipFinalSnapshot: environment !== "production",
}, {
    retainOnDelete: environment === "production",
});

堆栈秘密管理

使用加密秘密

# 设置加密秘密
pulumi config set --secret dbPassword mySecurePassword123
pulumi config set --secret apiKey sk_live_abc123xyz789

# 查看配置(秘密已加密)
pulumi config

# 查看明文秘密(小心使用!)
pulumi config get dbPassword --show-secrets

代码中的秘密

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

const config = new pulumi.Config();

// 获取秘密值
const dbPassword = config.requireSecret("dbPassword");
const apiKey = config.requireSecret("apiKey");

// 在资源中使用秘密
const db = new aws.rds.Instance("database", {
    engine: "postgres",
    instanceClass: "db.t3.micro",
    allocatedStorage: 20,
    username: "admin",
    password: dbPassword,
});

// 从秘密创建SSM参数
const dbPasswordParam = new aws.ssm.Parameter("db-password", {
    name: "/app/database/password",
    type: "SecureString",
    value: dbPassword,
});

const apiKeyParam = new aws.ssm.Parameter("api-key", {
    name: "/app/api/key",
    type: "SecureString",
    value: apiKey,
});

// 秘密在状态中加密
export const connectionString = pulumi.secret(
    pulumi.interpolate`postgresql://admin:${dbPassword}@${db.endpoint}/myapp`
);

堆栈刷新和状态

刷新堆栈状态

# 刷新堆栈以匹配实际云状态
pulumi refresh

# 自动批准刷新
pulumi refresh --yes

# 刷新特定资源
pulumi refresh --target urn:pulumi:dev::myapp::aws:s3/bucket:Bucket::my-bucket

# 刷新并显示差异
pulumi refresh --diff

堆栈状态管理

# 查看堆栈状态
pulumi stack --show-urns

# 查看特定资源
pulumi stack --show-urns | grep my-bucket

# 从状态移除资源(不删除云资源)
pulumi state delete 'urn:pulumi:dev::myapp::aws:s3/bucket:Bucket::my-bucket'

# 在状态中重命名资源
pulumi state rename 'urn:pulumi:dev::myapp::aws:s3/bucket:Bucket::old-name' \
                     'urn:pulumi:dev::myapp::aws:s3/bucket:Bucket::new-name'

何时使用此技能

使用pulumi-stacks技能当您需要:

  • 将基础设施部署到多个环境(开发、测试、生产)
  • 管理环境特定配置
  • 创建相同基础设施的隔离实例
  • 在项目之间共享基础设施输出
  • 实现多区域部署
  • 分离基础设施关注点(网络、数据库、应用)
  • 按环境管理秘密
  • 按环境跟踪基础设施状态
  • 实施渐进式部署策略
  • 将复杂基础设施组织为可管理单元
  • 应用环境特定策略和保护
  • 跨环境维护一致基础设施

最佳实践

  1. 命名约定:使用一致堆栈命名,如<env><region>-<env>(例如,produs-east-1-prod
  2. 配置文件:将堆栈配置文件保存在版本控制中(除秘密外)
  3. 环境隔离:切勿在环境之间共享状态;每个环境都有自己的堆栈
  4. 堆栈引用:使用堆栈引用而不是复制基础设施代码
  5. 秘密管理:始终对敏感值使用--secret标志
  6. 渐进式部署:先部署到开发,然后测试,最后生产
  7. 状态备份:定期导出堆栈状态以进行灾难恢复
  8. 资源保护:为关键生产资源启用protect选项
  9. 标签策略:在所有环境应用一致标签以进行成本跟踪
  10. 堆栈输出:导出其他堆栈或外部系统所需的所有值
  11. 配置验证:在创建资源前验证配置值
  12. 环境平等:尽可能保持环境相似,仅规模不同
  13. 自动化:使用CI/CD管道进行堆栈部署
  14. 文档:记录堆栈依赖关系和所需配置
  15. 状态加密:对敏感基础设施使用加密状态后端

常见陷阱

  1. 硬编码值:硬编码环境特定值而不是使用配置
  2. 共享状态:尝试在环境之间共享堆栈状态
  3. 缺失配置:部署到新堆栈而未设置所需配置
  4. 未加密秘密:将秘密存储为纯文本在配置中
  5. 不一致命名:在堆栈中使用不同命名约定
  6. 损坏引用:堆栈引用指向不存在堆栈或输出
  7. 缺失导出:未导出依赖堆栈所需的值
  8. 配置漂移:手动更改配置文件未反映在版本控制中
  9. 无资源保护:忘记保护关键生产资源
  10. 堆栈扩散:创建过多堆栈而无清晰组织
  11. 缺失验证:部署前未验证配置
  12. 循环依赖:创建循环堆栈引用
  13. 无备份策略:未导出堆栈状态以进行灾难恢复
  14. 环境差异:生产环境与其他环境显著不同
  15. 差秘密管理:将加密秘密检入公共仓库而无适当密钥管理

资源