Pulumi架构及使用

Posted by elrond on February 7, 2023

1. pulumi介绍

github项目介绍: Universal Infrastructure as Code. Your Cloud, Your Language, Your Way 🚀

通用基础设施即代码,你云你素,用熟悉的方式、熟悉的语言通过pulumi去管理自己的云平台。 通俗点来说,pulumi是一个基础设施资源编排器,写好编排代码,例如定义的yaml、或者编程语言golang等,通过动作触发即能完成各种云资源的增删改查等基础设施的管理,使用pulumi可以轻松构建自己的多云平台。较于已有的编排器,pulumi有如下几个明显的特点。

  • 高兼容性
    • 用户侧 – 无需学习新编程语言
      • 命令行工具 pulumi
      • 主流语言sdk Node.js Python Go .NET Java YAML
    • 云侧 – 兼容主流厂商与代表性开源软件
      • AWS
      • Azure
      • Google Cloud
      • Kubernetes
      • OpenStack
      • 阿里云
      • 华为云
      • 腾讯云
  • 抽象 – 各云厂商都提供了自己的驱动,作为使用者无需对接云厂商sdk,仅需了解pulumi的通用sdk即可

2. pulumi运行机制

2.1. 术语

  • Project 项目为stack的集合
  • Stack pulumi的服务主体、所有操作围绕stack展开,一个stack可以有多个资源,产品化过程中一个stack对应一个resource相对比较合理
  • Resource 真实的云资源,例如腾讯云主机、COS、OpenStack实例等等
  • Plugin 云厂商插件,resource操作时需要调用云厂商sdk,plugin则是针对云厂商sdk的封装,使用前都需要安装对应的插件,例如aws资源需要提前安装aws插件
  • Program 实际的资源操作模板,program会调用Plugin进行资源操作,被stack调用,使用自己熟悉的语言编写,可以通过pulumi命令触发
  • Pulumi云端 pulumi服务主体是stack,pulumi资源的操作实际上是通过修改stack状态来完成的,stack状态存储在pulumi云端,存储方式决定了 要使用pulumi需要与https://www.pulumi.com/交互,操作记录存储在这里而不是本地
  • pulumi cli pulumi客户端命令行,pulumi所有操作都通过pulumi命令完成
  • pulumi automation API pulumi各语言sdk,是对pulumi命令的封装

2.2. 系统架构

pulumi-programming-model-diagram

这张图说明了 project program resource stack的关系,所有的资源都属于project,stack既可以认为是单个资源的逻辑表示,也能当作环境,program处理实际资源。

2.3. 逻辑架构

how-pulumi-works

  • Language Host 执行program的服务器,即执行自定义的编排代码的服务器,由语言环境,例如go、python等和语言执行器两部分组成,语言环境负责运行program,语言执行器则是pulumi如何调用不同语言来达成特定动作的关键。
  • CLI and Engine CLI即pulumi cli,Engine为状态检查器,例如要创建Stack时首先要检查Stack存不存在,更新Stack时如果实际状态和目标状态一致则不做处理。
  • Last Deployed State 最后一次部署状态,存储在pulumi云端
  • Provides 各云厂商的驱动,包括两部分:resource plugin 和 SDK

要使用pulumi有如下几步,以pulumi cli 操作腾讯云为例:

  • 初始化 pulumi项目 pulumi new
  • 首先写好创建虚拟机的代码,这部分遵循plugin模板格式,调用腾讯云插件sdk
  • 使用pulumi cli或者 automation sdk 运行编排代码 pulumi up 或者 auto.NewStackInlineSource->stack.Up

2.3.1. pulumi cli 示例

首先初始化项目

# 创建文件夹
mkdir pulumi-qcloud
cd pulumi-qcloud
# 初始化项目,需要登陆pulumi,选择go模板即可
pulumi new

编写program代码

package main

import (
    "fmt"
    "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    "github.com/tencentcloudstack/pulumi-tencentcloud/sdk/go/tencentcloud/cos"
    "github.com/tencentcloudstack/pulumi-tencentcloud/sdk/go/tencentcloud/user"
)

func main() {
    pulumi.Run(func(ctx *pulumi.Context) error {
        _, err := user.GetInfo(ctx, nil, nil)
        if err != nil {
            fmt.Printf("the first error: %v\n", err)
            return err
        }
        _, err = cos.NewBucket(ctx, "myBucket", &cos.BucketArgs{
            Acl:    pulumi.String("private"),
            Bucket: pulumi.String(fmt.Sprintf("%v%v", "pulumi-created-", info.AppId)),
        })
        if err != nil {
            fmt.Printf("the second error: %v\n", err)
            return err
        }
        return nil
    })
}

运行

pulumi up

2.3.2. automation api

automation-api

automation api是对pulumi cli的封装,通过automation api调用更简单: sdk操作比调用二进制方便,更安全: 不用直接与文件系统文件进行直接访问。 pulumi 不经可以通过 rest api方式、cli方式提供使用,还能继承带ci/cd系统中,方便CD。

使用rest api方式提供服务见 pulumi_over_http

腾讯云 rest api示例 multistack-example

3. 产品化思路

3.1. 需求

  • 管理多云
  • 管理混合云
  • 不需要自己写sdk
  • 可私有化
  • 以rest api方式提供服务

其中可私有化不能满足,因为操作记录都会存储在pulumi云端,其他均满足,如果忽略这点,pulumi可称为完美的多云管理利器

3.2. 研发思路

  • project作为资源隔离单位
  • 一个stack为一个资源,资源的所有增删改查都通过stack操作
  • 使用automation api嵌入已由业务代码 demo

3.3. 可预见问题

  • pulumi 输出大都在文件中,需要读取文件解析
  • pulumi 不能直接列举资源,例如获取一个project的所有资源,需先列举project的stack,在列觉stack中的资源,在通过资源get接口get资源信息
  • pulumi 本身日志输出格式比较花里胡哨,适合人类直接读,但结构化差不适合分析
  • pulumi 文档和示例较少

4. 商业模式

软件开源,但所有操作需要和云端交互,数据状态存储在云端,所以收费也在云端,pulumi的收费模式为限制quota收费,具体如下图

businessmodel

主要按以下纬度收费

  • 人数
  • update并发
  • stacks个数
  • CICD助手功能
  • webhook功能

等基础功能,高级功能见 pricing

收费模式决定了企业要使用pulumi必须付费

5. 与同类软件对比

对比最多的为terraform

5.1. 相似点

  • 都是IaC
  • 都开源
  • 都提供期望资源状态管理模型
  • 都支持主流云厂商
  • 都很流行,备受厂商青睐

5.2. 差异点

这里仅列举了几个在选型中起决定性因素的差异点,详细的对比参照 Pulumi vs. Terraform

功能 pulumi Terraform
语言支持 Python, TypeScript, JavaScript, Go, C#, F#, Java, YAML HashiCorp Configuration Language (HCL)
IDE支持 代码补齐、强类型、错误提示、丰富的资源文档 受限
状态管理 可视化状态管理 功能弱、可视化支持差
嵌入业务代码 可嵌入 不可嵌入
secrets管理

最大的区别在于语言支持、状态管理、可嵌入业务代码中三点

  • terraform需要学习hcl,pulumi则支持多种语言,选择顺手的语言操作即可,无语言学习成本
  • pulumi提供了完备的状态管理,且在云端可视化,terraform状态管理则支持非常不完善
  • terraform不能嵌入到业务代码中,只能作为运维工具使用,pulumi则可以完美嵌入

6. 附录

6.1. 附录1: 简单使用示例

6.1.1. Tencent CLoud

6.1.1.1. 初始化项目

❯ pulumi new --force
Please choose a template (113/205 shown):
 go                                 A minimal Go Pulumi program
This command will walk you through creating a new Pulumi project.

Enter a value or leave blank to accept the (default), and press <ENTER>.
Press ^C at any time to quit.

project name: (pulumitest)
project description: (A minimal Go Pulumi program)
Created project 'pulumitest'

Please enter your desired stack name.
To create a stack in an organization, use the format <org-name>/<stack-name> (e.g. `acmecorp/dev`).
stack name: (dev)
Created stack 'dev'

Installing dependencies...

go: downloading github.com/pulumi/pulumi/sdk/v3 v3.53.0
go: finding module for package github.com/mattn/go-isatty
go: found github.com/mattn/go-isatty in github.com/mattn/go-isatty v0.0.17
Finished installing dependencies

Your new project is ready to go! ✨

To perform an initial deployment, run `pulumi up`

6.1.1.2. 编写program代码

package main

import (
    "fmt"
    "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
    "github.com/tencentcloudstack/pulumi-tencentcloud/sdk/go/tencentcloud/cos"
    "github.com/tencentcloudstack/pulumi-tencentcloud/sdk/go/tencentcloud/user"
)

func main() {
    pulumi.Run(func(ctx *pulumi.Context) error {
        _, err := user.GetInfo(ctx, nil, nil)
        if err != nil {
            fmt.Printf("the first error: %v\n", err)
            return err
        }
        _, err = cos.NewBucket(ctx, "myBucket", &cos.BucketArgs{
            Acl:    pulumi.String("private"),
            Bucket: pulumi.String(fmt.Sprintf("%v%v", "pulumi-created-", info.AppId)),
        })
        if err != nil {
            fmt.Printf("the second error: %v\n", err)
            return err
        }
        return nil
    })
}

6.1.1.3. 准备运行

  • 配置腾讯云凭证
❯ pulumi config set tencentcloud:secretId xxxxx --secret
❯ pulumi config set tencentcloud:secretKey xxxxxxx --secret
❯ pulumi config set tencentcloud:region ap-hongkong
  • 下载sdk依赖包
❯ go mod tidy
go: finding module for package github.com/tencentcloudstack/pulumi-tencentcloud/sdk/go/tencentcloud/cos
go: finding module for package github.com/tencentcloudstack/pulumi-tencentcloud/sdk/go/tencentcloud/user
go: found github.com/tencentcloudstack/pulumi-tencentcloud/sdk/go/tencentcloud/cos in github.com/tencentcloudstack/pulumi-tencentcloud/sdk v0.1.2
go: found github.com/tencentcloudstack/pulumi-tencentcloud/sdk/go/tencentcloud/user in github.com/tencentcloudstack/pulumi-tencentcloud/sdk v0.1.2

6.1.1.4. 运行创建

可以提前安装plugin,否则运行pulumi up的时候会安装,命令为 pulumi plugin install resource tencentcloud v0.1.2, 不翻墙的话很慢

❯ pulumi up
Previewing update (dev)

View Live: https://app.pulumi.com/elrondwong/pulumitest/dev/previews/f129cbae-67d8-42aa-b78a-c082e598a08f

# 安装plugin
Downloading plugin: 21.15 MiB / 43.45 MiB [==========>-----------]  48.67% 18m8s
Downloading plugin: 43.45 MiB / 43.45 MiB [=====================] 100.00% 37m32s
                                                                                [resource plugin tencentcloud-0.1.2] installing
     Type                        Name            Plan       
     pulumi:pulumi:Stack         pulumitest-dev             
 +   └─ tencentcloud:Cos:Bucket  myBucket        create     


Resources:
    + 1 to create
    1 unchanged

Do you want to perform this update? yes
Updating (dev)

View Live: https://app.pulumi.com/elrondwong/pulumitest/dev/updates/2

     Type                        Name            Status           
     pulumi:pulumi:Stack         pulumitest-dev                   
 +   └─ tencentcloud:Cos:Bucket  myBucket        created (4s)     


Resources:
    + 1 created
    1 unchanged

Duration: 11s

这里仅展示了资源创建,对资源的销毁可以看下面OpenStack的例子

6.1.2. OpenStack

6.1.2.1. pulumi项目创建

 pulumi new --dir pulumiopenstack
Please choose a template (113/205 shown):
 openstack-go                       A minimal OpenStack Go Pulumi program
This command will walk you through creating a new Pulumi project.

Enter a value or leave blank to accept the (default), and press <ENTER>.
Press ^C at any time to quit.

project name: (pulumiopenstack)
project description: (A minimal OpenStack Go Pulumi program)
Created project 'pulumiopenstack'

Please enter your desired stack name.
To create a stack in an organization, use the format <org-name>/<stack-name> (e.g. `acmecorp/dev`).
stack name: (dev)
Created stack 'dev'

Installing dependencies...

Finished installing dependencies

Your new project is ready to go! ✨

To perform an initial deployment, run 'cd pulumiopenstack', then, run `pulumi up`

6.1.2.2. program代码编写

package main

import (
    "fmt"
    "github.com/pulumi/pulumi-openstack/sdk/v3/go/openstack/blockstorage"
    "github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
    pulumi.Run(func(ctx *pulumi.Context) error {
      _, err := blockstorage.NewVolume(ctx, "volume1", &blockstorage.VolumeArgs{
          Description: pulumi.String("first test volume"),
          Region:      pulumi.String("RegionOne"),
          Size:        pulumi.Int(3),
      })
      if err != nil {
          return err
      }
      return nil
    })
}

6.1.2.3. 运行准备

  • 安装plugin
pulumi plugin install resource openstack v3.9.0
  • 安装依赖包
go mod tidy
  • 配置凭证
export OS_USERNAME=admin
export OS_PASSWORD=passwd
export OS_PROJECT_NAME=admin
export OS_USER_DOMAIN_NAME=Default
export OS_PROJECT_DOMAIN_NAME=Default
export OS_AUTH_URL=http://172.16.10.10:5000/v3
export OS_IDENTITY_API_VERSION=3
export PS1='[\u@\h \W(keystone_admin)]\$ '

6.1.2.4. 运行

  • 创建卷
❯ pulumi up
Previewing update (dev)

View Live: https://app.pulumi.com/elrondwong/pulumiopenstack/dev/previews/cc39ba12-d292-43ad-b962-a9d5d66653a5

Downloading plugin openstack v3.9.0: 17.95 MiB / 17.95 MiB [=====] 100.00% 7m24s
     Type                              Name                 Plan
 +   pulumi:pulumi:Stack               pulumiopenstack-dev  create
 +   └─ openstack:blockstorage:Volume  volume1              create


Resources:
    + 2 to create

Do you want to perform this update? yes
Updating (dev)

View Live: https://app.pulumi.com/elrondwong/pulumiopenstack/dev/updates/1

     Type                              Name                 Status
 +   pulumi:pulumi:Stack               pulumiopenstack-dev  created (0.62s)
 +   └─ openstack:blockstorage:Volume  volume1              created (11s)


Resources:
    + 2 created

Duration: 15s
  • 销毁资源
❯ pulumi destroy
Previewing destroy (dev)

View Live: https://app.pulumi.com/elrondwong/pulumiopenstack/dev/previews/4673ac44-90a7-438a-8166-01e7cdf1f89d

     Type                              Name                 Plan
 -   pulumi:pulumi:Stack               pulumiopenstack-dev  delete
 -   └─ openstack:blockstorage:Volume  volume1              delete


Resources:
    - 2 to delete

Do you want to perform this destroy? yes
Destroying (dev)

View Live: https://app.pulumi.com/elrondwong/pulumiopenstack/dev/updates/2

     Type                              Name                 Status
 -   pulumi:pulumi:Stack               pulumiopenstack-dev  deleted
 -   └─ openstack:blockstorage:Volume  volume1              deleted (11s)


Resources:
    - 2 deleted

Duration: 13s

The resources in the stack have been deleted, but the history and configuration associated with the stack are still maintained.
If you want to remove the stack completely, run `pulumi stack rm dev`.
  • 销毁stack
❯ pulumi stack rm dev
This will permanently remove the 'dev' stack!
Please confirm that this is what you'd like to do by typing `dev`: dev
Stack 'dev' has been removed!

# https://app.pulumi.com/elrondwong/projects 
stack里面少了一个

7. 参考