Chapter 05

Stack 与多环境配置管理

用 Stack 隔离环境,用加密配置管理敏感信息,用 Stack Reference 共享基础设施输出

Stack:环境隔离的核心机制

什么是 Stack?

在 Pulumi 中,Stack 是同一套代码的一个独立部署实例。一套 Pulumi 程序可以对应多个 Stack,每个 Stack 代表一个隔离的环境(dev/staging/prod)。每个 Stack 有:

同一套代码 → 多个 Stack my-infra/ ├── __main__.py ← 一套代码 ├── Pulumi.yaml ├── Pulumi.dev.yaml ← dev Stack 配置 ├── Pulumi.staging.yaml← staging Stack 配置 └── Pulumi.prod.yaml ← prod Stack 配置 Pulumi Cloud State: ├── org/my-infra/dev ← dev 的 State(独立) ├── org/my-infra/staging ← staging 的 State(独立) └── org/my-infra/prod ← prod 的 State(独立)

Stack 管理命令

# 创建新 Stack
pulumi stack init dev
pulumi stack init staging
pulumi stack init prod

# 列出所有 Stack(* 号标注当前激活的 Stack)
pulumi stack ls
# NAME      LAST UPDATE    RESOURCE COUNT  URL
# dev *     2h ago         12              https://app.pulumi.com/...
# staging   3d ago         12              https://app.pulumi.com/...
# prod      1w ago         12              https://app.pulumi.com/...

# 切换到 prod Stack
pulumi stack select prod

# 查看当前 Stack 信息
pulumi stack

# 查看当前 Stack 的所有输出值
pulumi stack output

# 导出 Stack State(备份或调试)
pulumi stack export > stack-backup.json

# 删除 Stack(必须先 destroy 所有资源)
pulumi stack rm dev

pulumi config:Stack 配置管理

设置与读取配置值

# 切换到 dev Stack,设置配置
pulumi stack select dev

pulumi config set environment dev
pulumi config set aws:region us-east-1
pulumi config set instanceType t3.micro    # dev 用小机型
pulumi config set minNodes 1

# 切换到 prod,设置不同配置
pulumi stack select prod

pulumi config set environment prod
pulumi config set aws:region us-east-1
pulumi config set instanceType c5.xlarge   # prod 用大机型
pulumi config set minNodes 3

# 查看当前 Stack 的所有配置
pulumi config
# KEY           VALUE
# environment   prod
# instanceType  c5.xlarge
# minNodes      3

# 查看特定值
pulumi config get instanceType

加密的 Secret 配置

# 设置加密 Secret(值会被加密存储在 Pulumi.dev.yaml 中)
pulumi config set --secret dbPassword "sup3r-s3cret"
pulumi config set --secret awsSecretKey "EXAMPLEKEY"

# 查看 Pulumi.dev.yaml 中的 secret(已加密)
# config:
#   myapp:dbPassword:
#     secure: AAABADwAB...(AES-256 加密)

# 获取解密后的值(仅在有授权的机器上)
pulumi config get dbPassword  # 输出明文

# 默认加密方式是 Pulumi Cloud 管理密钥
# 也可以使用 passphrase:
PULUMI_CONFIG_PASSPHRASE="my-passphrase" pulumi up

# 或使用 AWS KMS:
pulumi stack init prod --secrets-provider="awskms://alias/pulumi-secrets"

在代码中读取配置

import pulumi
import pulumi_aws as aws

# 读取当前 Stack 的配置
config = pulumi.Config()

# get:如果不存在返回 None
env = config.get("environment") or "dev"

# require:如果不存在则报错(强制要求)
instance_type = config.require("instanceType")

# get_int/get_float/get_bool:类型转换
min_nodes = config.get_int("minNodes") or 1

# require_secret:读取加密值(返回 Output[str])
db_password = config.require_secret("dbPassword")

# get_object:读取 JSON 对象配置
tags_config = config.get_object("tags") or {}

# 根据环境决定资源规格
is_prod = env == "prod"
db_instance = aws.rds.Instance(
    "app-db",
    instance_class="db.r5.large" if is_prod else "db.t3.micro",
    multi_az=is_prod,               # prod 开启多可用区
    allocated_storage=100 if is_prod else 20,
    username="admin",
    password=db_password,           # Output[str] Secret
    skip_final_snapshot=not is_prod,
    opts=pulumi.ResourceOptions(protect=is_prod),  # 生产环境保护
)

Pulumi.dev.yaml 配置文件示例

# Pulumi.dev.yaml — dev Stack 配置
# 可以提交到 Git(secrets 已加密)
config:
  aws:region: us-east-1
  myapp:environment: dev
  myapp:instanceType: t3.micro
  myapp:minNodes: 1
  myapp:dbPassword:
    secure: AAABAKoRkU3JV...

Stack Reference:跨 Stack 引用输出

使用场景

Stack Reference 解决了"基础设施分层"的问题:将基础网络(VPC)、共享服务(数据库)和应用层部署放在不同的 Pulumi 项目中,通过 Stack Reference 引用底层 Stack 的输出值。

分层基础设施架构 ┌─────────────────────────────────────────────┐ │ network-stack (pulumi/network/prod) │ │ 输出:vpcId, subnetIds, securityGroupId │ └─────────────────────┬───────────────────────┘ │ Stack Reference ┌─────────────────────▼───────────────────────┐ │ platform-stack (pulumi/platform/prod) │ │ 引用:vpcId → 创建 RDS、EKS │ │ 输出:dbEndpoint, clusterName │ └─────────────────────┬───────────────────────┘ │ Stack Reference ┌─────────────────────▼───────────────────────┐ │ app-stack (pulumi/apps/my-service/prod) │ │ 引用:dbEndpoint, clusterName → 部署应用 │ └─────────────────────────────────────────────┘
# app-stack 的 __main__.py
import pulumi

# 引用 network-stack 的输出
network_stack = pulumi.StackReference("myorg/network/prod")
# 格式:"{organization}/{project}/{stack}"

vpc_id      = network_stack.get_output("vpcId")
subnet_ids  = network_stack.get_output("privateSubnetIds")

# 引用 platform-stack 的输出
platform_stack = pulumi.StackReference("myorg/platform/prod")
db_endpoint = platform_stack.get_output("dbEndpoint")

# 使用跨 Stack 引用的值创建资源
import pulumi_aws as aws
import pulumi_kubernetes as k8s

# vpc_id 和 subnet_ids 是 Output[Any],可以直接用作资源属性
sg = aws.ec2.SecurityGroup(
    "app-sg",
    vpc_id=vpc_id,        # Output 直接用
    description="App security group",
)

实战:三环境配置管理

# __main__.py — 统一代码处理三个环境
import pulumi
import pulumi_aws as aws

config = pulumi.Config()
env = config.require("environment")

# 环境配置映射
ENV_CONFIG = {
    "dev": {
        "instance_type": "t3.micro",
        "min_size": 1,
        "max_size": 2,
        "db_class": "db.t3.micro",
        "deletion_protection": False,
    },
    "staging": {
        "instance_type": "t3.small",
        "min_size": 1,
        "max_size": 3,
        "db_class": "db.t3.small",
        "deletion_protection": False,
    },
    "prod": {
        "instance_type": "c5.xlarge",
        "min_size": 3,
        "max_size": 20,
        "db_class": "db.r5.large",
        "deletion_protection": True,
    },
}

cfg = ENV_CONFIG[env]
is_prod = (env == "prod")

# 统一的资源创建代码,配置来自字典
vpc = aws.ec2.Vpc(
    f"vpc-{env}",
    cidr_block="10.0.0.0/16",
    tags={"Environment": env, "ManagedBy": "Pulumi"},
)

# 生产环境创建 Multi-AZ RDS
db_password = config.require_secret("dbPassword")
db = aws.rds.Instance(
    f"db-{env}",
    instance_class=cfg["db_class"],
    engine="postgres",
    engine_version="15",
    allocated_storage=100 if is_prod else 20,
    multi_az=is_prod,
    deletion_protection=cfg["deletion_protection"],
    username="admin",
    password=db_password,
    skip_final_snapshot=not is_prod,
    opts=pulumi.ResourceOptions(protect=is_prod),
)

pulumi.export("vpc_id", vpc.id)
pulumi.export("db_endpoint", db.address)
pulumi.export("environment", env)
# 三环境部署工作流

# Dev 环境
pulumi stack select dev
pulumi config set environment dev
pulumi config set --secret dbPassword "dev-password"
pulumi up

# Staging 环境(dev 测试通过后)
pulumi stack select staging
pulumi config set environment staging
pulumi config set --secret dbPassword "staging-password"
pulumi up

# Prod 环境(staging 验证通过后)
pulumi stack select prod
pulumi config set environment prod
pulumi config set --secret dbPassword "prod-super-secret"
pulumi preview   # 先预览!
pulumi up
Stack 名称建议使用 org/project/stack 格式

Stack Reference 使用 "org/project/stack" 格式(如 "mycompany/network/prod")。建议在团队中统一 Stack 命名规范,并将 Stack 归属到组织(Organization)下而不是个人账号下,以方便团队协作和权限管理。

本章小结

本章核心要点