广告位 · 页头(后台-模块管理-新建模块后替换此区域)

Pulumi 如何用声明式方式管理容器基础设施?

容器基础设施的管理长期被两种范式分割:云资源用 Terraform 或控制台管理,Kubernetes 内部对象用 YAML 或 Helm 管理。两者在变更流程、状态存储和复用能力上并不一致,团队往往需要维护两套流水线。Pulumi 把声明式思想放进通用编程语言里,让 VPC、节点池、镜像仓库、Deployment、Service 甚至 Ingress 都能用同一份代码描述。与直接生成 YAML 不同,Pulumi 的程序在运行时构造资源模型,引擎负责依赖排序和差异计算,因此循环、条件判断、函数抽象都能直接参与基础设施定义。

Pulumi 如何用声明式方式管理容器基础设施?

一、Pulumi 的声明式模型与容器基础设施的契合点

Pulumi 的核心执行方式是:开发者用 TypeScript、Python、Go 或 C# 编写程序,程序运行后产出一个资源图,Pulumi 引擎拿着这个资源图与真实云环境或 Kubernetes 集群做差异比较,然后按依赖顺序创建、更新或删除资源。这个资源图本质上是期望状态的快照,与 Kubernetes 控制器不断调谐实际状态到期望状态的思路非常接近。区别在于,Kubernetes 的调谐发生在运行时,而 Pulumi 的调谐发生在部署阶段,触发点是命令行或 CI 流水线。

正因为模型相似,Pulumi 管理容器基础设施时没有明显割裂感。你可以先定义云提供商的 Kubernetes 集群资源,再把集群对象作为输入传给 Kubernetes Provider,接着在同一个 Stack 中创建命名空间、部署工作负载。传统方案里,集群创建可能需要 Terraform,集群内部对象需要单独的 kubectl apply 或 Helm 命令。Pulumi 把依赖关系保存成一张图,例如节点池扩容完成后才会继续部署依赖新节点的 Pod,避免脚本式的时序猜测。

另外,声明式不等于静态。很多人误以为声明式就是写固定配置,其实 Pulumi 里的条件、循环和函数都只是构造资源图的工具。程序最终输出了哪些资源,取决于输入参数和环境变量。举例来说,同一个 Stack 可以在开发环境只创建最小规格的 Deployment,在生产环境自动加副本数、亲和性规则和监控注解。这种能力来自通用编程语言,而不是 YAML 模板引擎的补丁式扩展。

二、用 Pulumi 代码定义 Kubernetes 工作负载

以一个常见的 Nginx 部署为例,展示如何用 TypeScript 同时定义 Deployment 和 Service。Pulumi 的 Kubernetes Provider 支持官方 API 版本,资源命名空间、标签和副本数都作为对象属性传入。

import * as k8s from "@pulumi/kubernetes";
const appLabels = { app: "nginx" };
const deployment = new k8s.apps.v1.Deployment("nginx", {
  metadata: { name: "nginx", labels: appLabels },
  spec: {
    replicas: 2,
    selector: { matchLabels: appLabels },
    template: {
      metadata: { labels: appLabels },
      spec: {
        containers: [
          {
            name: "nginx",
            image: "nginx:1.25",
            ports: [{ containerPort: 80 }]
          }
        ]
      }
    }
  }
});
const service = new k8s.core.v1.Service("nginx-service", {
  metadata: { name: "nginx-service" },
  spec: {
    selector: appLabels,
    ports: [{ port: 80, targetPort: 80 }],
    type: "ClusterIP"
  }
});

这段代码的关键点在于 appLabels 被 Deployment 的 selector、Pod 模板的 labels 和 Service 的 selector 共同引用。如果用 YAML 维护,三处标签需要手工保持一致,改动时容易漏改。Pulumi 通过变量复用消除了这一隐患。资源名 nginx 和 nginx-service 是 Pulumi 资源标识,实际部署到集群的名称由 metadata.name 决定,两者可以不同,方便同一套代码部署到不同命名空间或不同环境。

除了直接定义资源,Pulumi 也支持包装 Helm Chart。如果团队已经积累了大量 Helm Chart,不需要一次性重写。可以通过 k8s.helm.v3.Chart 或 Release 资源,把 values 参数从 Pulumi 配置中注入。这样 Helm 的模板能力继续保留,而外层依赖、环境切换和云资源联动由 Pulumi 统一处理。例如数据库连接串可以先创建云数据库,再通过 Pulumi 输出传给 Helm values,不用事后手工拼装。

云负载均衡同样能在同一份代码里完成。云提供商的 LoadBalancer 资源与 Kubernetes Service 之间存在依赖,Pulumi 会自动识别这种关系。如果先建 Service 再建云安全组,传统脚本容易出现顺序错误,Pulumi 则根据资源间的引用关系生成拓扑排序,执行顺序可预测且可预览。

三、状态管理、预览与漂移检测

Pulumi 会把每次部署后的资源状态保存在后端中,默认托管在 Pulumi Cloud,也可以选择 S3、Azure Blob 或本机文件。状态文件不止记录资源是否存在,还保存输入的属性、输出值和依赖边。这让 pulumi preview 能精确展示将要发生的变更,包括哪些字段会从旧值变成新值。对于容器基础设施来说,一次 Deployment 镜像升级可能只改 image 字段,preview 会明确显示 image: "nginx:1.25" 变为 "nginx:1.26",不会笼统提示整个资源被替换。

漂移检测通过 pulumi refresh 实现。它会读取云端或集群中的实际状态,与状态文件中的期望值做比对。如果有人在 kubectl 里手动修改了副本数,或通过控制台调整了节点池规模,refresh 会把这些差异带回到状态中。之后执行 preview 时,Pulumi 就能显示代码期望状态与实际状态的不一致。需要注意的是,refresh 本身不会立即纠正漂移,它只是更新状态记录。真正的修复需要再执行一次 pulumi up,让引擎把资源调回代码描述的状态。

在 Kubernetes 环境中,漂移更隐蔽。例如 Horizontal Pod Autoscaler 会动态修改 Deployment 的 replicas 字段,控制器会更新 Pod 状态。若把这些字段全部纳入 Pulumi 管理,容易出现反复修正。实践中通常用 ignoreChanges 忽略运行时控制器会修改的字段,或者将这些动态调整交给集群控制器,Pulumi 只管理基线副本数和 HPA 策略。这样既能享受声明式基线管理,又不会与 Kubernetes 自身的控制循环打架。

四、测试与 CI/CD 中的落地实践

Pulumi 程序是真实代码,因此可以像普通软件一样做单元测试。以 TypeScript 为例,可以用 pulumi.runtime.mocks 模拟资源创建过程,验证资源图是否包含预期的 Deployment、Service 以及副本数。测试不需要连接真实集群,速度快且可重复。对于 Python 项目,可以结合 pytest 写断言。策略测试则使用 Pulumi 的 Policy as Code 框架,检查 Deployment 是否必须设置资源限制、镜像是否来自允许的仓库等合规要求。

CI/CD 集成时,建议把 pulumi preview 放在合并请求阶段,把 pulumi up --yes 放在合并后的发布阶段。preview 输出的 diff 可以直接归档到流水线日志,供评审人员查看。对于高风险变更,可以启用 --diff 获取更细粒度的字段级差异。多环境部署可以用 Stack 隔离,例如 dev、staging、prod 三个 Stack 共享同一套程序,但使用不同的 Pulumi 配置文件。配置项可以是镜像 tag、副本数、域名等,通过 pulumi config set 写入,程序用 config.require 读取。

从 Terraform 或 Helm 迁移时,不要一次性重写所有资源。可以先从新增的容器基础设施入手,用 Pulumi 管理新集群或新服务,老资源仍然留在原工具中。Pulumi 支持 pulumi import 把已有资源纳入状态,之后逐步接管。迁移过程中要特别关注 Kubernetes 资源的注解和默认字段,因为 Provider 在 import 时可能补全大量默认值,产生首次 preview 的大量 diff。建议先在测试集群跑通 import 流程,再推广到生产环境。整体而言,Pulumi 的声明式容器基础设施方案,最大的价值不是替换 YAML,而是把基础设施、平台服务和容器工作负载放进同一个可测试、可预览、可审计的代码仓库里。

广告位 · 文章底部(后台-模块管理替换此区域)