使用您最喜欢的编程语言来配置基础设施即代码

使用 Node.js 或其他编程语言配置启动基础设施所需的一切。
87 位读者喜欢这篇文章。
Puzzle pieces coming together to form a computer screen

Opensource.com

当您在 IT 和科技领域中探索时,会反复遇到一些术语。其中一些术语难以量化,并且随着时间的推移可能会呈现不同的含义。“DevOps” 就是一个例子,这个词语(在我看来)似乎会随着使用它的人而改变;最初的 DevOps 先驱者甚至可能认不出我们今天所称的 DevOps。

如果您是一名软件开发人员,“基础设施即代码”(IaC)可能是其中一个术语。IaC 是使用与编写面向用户的特性相同的软件开发实践来声明应用程序运行的基础设施。这通常意味着使用诸如 GitMercurial 进行版本控制,以及 Puppet、Chef 或 Ansible 进行配置管理。在基础设施配置层,最常见的技术是 CloudFormation(专门用于 AWS)或 Terraform,作为用于为您的应用程序创建混合云资源的开源替代方案。

在配置管理领域,有很多很好的选项可以将 IaC 编写为配置文件或首选编程语言,但是这种选择在基础设施配置领域并不为人所知。

Pulumi 提供了一个选项,可以使用标准编程语言来定义基础设施。它支持一系列语言,包括 JavaScriptTypeScriptGoPythonC#。与 Terraform 非常相似,Pulumi 对许多熟悉的云提供商提供一流的支持,例如 AWSAzureGoogle Cloud其他提供商

在本文中,我将向您展示如何使用 Pulumi 在 Node.js 中编写基础设施。

先决条件

首先,请确保您已准备好使用 Pulumi。Pulumi 支持所有主要的操作系统,因此您用于安装其先决条件的方法取决于您使用的操作系统。

首先,安装您首选编程语言的解释器。我将使用 TypeScript,因此我需要安装 node 二进制文件。有关您的操作系统的信息,请参阅 Node 的安装说明。您可以使用 HomebrewMacLinux 上安装

brew install node

在 Linux 上,您可以选择使用常用的软件包管理器,例如 aptdnf

$ sudo dnf install nodejs

无论哪种情况,结果都应该是 node 二进制文件在您的 $PATH 中可用。要确认它是否可访问,请运行

node --version

接下来,安装 Pulumi 命令行界面 (CLI)。您可以在 Pulumi 的文档中找到特定于操作系统的 安装说明。在 Mac 或 Linux 上使用 brew

brew install pulumi

或者,您可以使用安装脚本。首先下载并查看它,然后执行它

$ curl -fsSL --output pulumi_installer.sh \
https://get.pulumi.com/ 
$ more  pulumi_installer.sh 
$ sh ./pulumi_installer.sh

同样,期望的结果是 pulumi 二进制文件在您的路径中可用。检查版本以确保您已准备好继续

pulumi version
v2.5.0

配置 Pulumi

在开始配置任何基础设施之前,请为 Pulumi 提供一个位置来存储其状态

Pulumi 将其状态存储在后端。默认后端是 Pulumi 的软件即服务(为个人用户提供免费计划),但在此示例中,请使用备用文件后端。文件后端将在您的本地文件系统上创建一个文件,用于存储状态,命令如下:

pulumi login --local

如果您计划与其他人共享此项目,则文件后端可能不是一个好的起点。Pulumi 也可以将其状态存储在云对象存储中,例如 AWS S3。要使用它,请创建一个 S3 存储桶并登录

pulumi login --cloud-url s3://my-pulumi-state-bucket

现在您已经登录到状态后端,您可以创建一个项目和一个堆栈!

在开始创建 Pulumi 项目之前,请先了解以下 Pulumi 术语,您将在本教程中看到这些术语。

项目

项目是一个包含 Pulumi.yaml 文件的目录。此文件包含 Pulumi 需要知道的元数据才能执行其操作。您将在 Pulumi.yaml 文件中找到的示例字段包括:

  • 运行时(例如,Python、Node、Go、.Net)
  • 项目的描述(例如,“我的第一个 Pulumi 项目”)
  • 项目的名称

项目是一个松散定义的概念,可以满足您的需求。通常,一个项目包含许多资源,这些资源是您想要配置和控制的事物。您可以选择拥有资源很少的小型 Pulumi 项目,也可以选择包含您需要的所有资源的大型项目。随着您越来越熟悉 Pulumi,您将更清楚地了解如何布局您的项目。

堆栈

Pulumi 堆栈允许您根据可配置的值来区分您的 Pulumi 项目。常见的用法是将项目部署到不同的环境(如开发或生产)或不同的区域(如欧洲、中东和非洲以及美国)。

刚开始使用时,您不太可能需要复杂的堆栈设置,因此本演练使用默认堆栈名称 dev

使用 TypeScript 进行 IaC

您可以使用方便的 pulumi new 命令来引导 Pulumi 项目。new 命令有很多标志和选项,应该可以帮助您开始使用 Pulumi,因此请继续创建您的第一个项目

$ pulumi new typescript
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: (pulumi) my-first-project
project description: (A minimal TypeScript Pulumi program) My very first Pulumi program
Created project 'my-first-project'

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) dev
Created stack 'dev'

Installing dependencies...


> node scripts/postinstall

added 82 packages from 126 contributors and audited 82 packages in 2.84s

13 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

Finished installing dependencies

Your new project is ready to go! ✨

To perform an initial deployment, run 'pulumi up'

这里发生了很多事情,所以我将分解一下

第一部分标识了您的 Pulumi 项目的模板。我选择了通用的 typescript 选项,但是有许多选项可用

new 命令从您的模板存储库中获取模板,并将此文件复制到本地,包括运行时依赖项(在本例中为 package.json)。

new 命令通过在此目录内运行 npm install 来负责安装这些依赖项。然后 npm install 下载并安装了运行您的 Pulumi 程序所需的一切,在本例中,这非常简单:@pulumi/pulumi NPM 包。

您已准备好创建您的第一个资源!

创建您的第一个云资源

资源是由您的基础设施配置软件生命周期管理的事物。资源通常是云提供商对象,例如 S3 存储桶。Pulumi 提供商处理 Pulumi 资源,并且提供商特定于他们管理的云提供商。Pulumi 大约有 40 个提供商 可供您使用,但对于您的第一个资源,请使用最简单的提供商之一——random 提供商

random 提供商的功能正如其名称所示:它幂等地创建一个随机资源(例如,可以是字符串),并将其存储在 Pulumi 状态中。

使用 npm 将其作为依赖项添加到您的 Pulumi 项目中

npm install @pulumi/random

npm 包管理器下载并安装 random 提供商包并为您安装它。现在您已准备好编写您的 Pulumi 程序。

当您之前生成项目时,Pulumi 的引导过程创建了一个 index.ts TypeScript 文件。在您最喜欢的集成开发环境 (IDE) 中打开它并添加您的第一个资源

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

const password = new random.RandomString(`password`, {
    length: 10
})

如果您熟悉 TypeScript 或 JavaScript,这看起来会非常熟悉,因为它使用您知道的编程语言编写。如果您使用 Pulumi 的其他支持语言之一,这也适用。这是与之前相同的 random 资源,但这次是 Python

import pulumi_random as random

password = random.RandomString("password", length=10)

Pulumi 项目目前仅支持一种语言,但每个项目都可以引用以其他语言编写的其他项目——对于多语言团队的成员来说,这是一个有用的技巧。

您已经编写了您的第一个 Pulumi 资源。现在您需要部署它。

离开您的编辑器并返回到命令行。从您的项目目录中,运行 pulumi up 并观看奇迹发生

pulumi up 

Previewing update (dev):
     Type                          Name                  Plan
 +   pulumi:pulumi:Stack           my-first-project-dev  create
 +   └─ random:index:RandomString  password              create

Resources:
    + 2 to create

Do you want to perform this update? yes
Updating (dev):
     Type                          Name                  Status
 +   pulumi:pulumi:Stack           my-first-project-dev  created
 +   └─ random:index:RandomString  password              created

Resources:
    + 2 created

Duration: 2s

Permalink: file:///Users/lbriggs/.pulumi/stacks/dev.json

太棒了,您拥有了您的第一个 Pulumi 资源!虽然您可能正在享受这种成就感,但不幸的是,这个 random 资源并不是那么有用——它只是一个随机字符串,您甚至看不到它是什么。首先解决这一部分:修改您之前的程序并将 export 添加到您创建的常量中

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

export const password = new random.RandomString(`password`, {
    length: 10
})

重新运行 pulumi up 并查看输出

pulumi up
Previewing update (dev):
     Type                 Name                  Plan
     pulumi:pulumi:Stack  my-first-project-dev

Outputs:
  + password: {
      + id             : "&+r?{}J$J7"
      + keepers        : output<string>
      + length         : 10
      + lower          : true
      + minLower       : 0
      + minNumeric     : 0
      + minSpecial     : 0
      + minUpper       : 0
      + number         : true
      + overrideSpecial: output<string>
      + result         : "&+r?{}J$J7"
      + special        : true
      + upper          : true
      + urn            : "urn:pulumi:dev::my-first-project::random:index/randomString:RandomString::password"
    }

Resources:
    2 unchanged

Do you want to perform this update? yes
Updating (dev):
     Type                 Name                  Status
     pulumi:pulumi:Stack  my-first-project-dev

Outputs:
  + password: {
      + id        : "&+r?{}J$J7"
      + length    : 10
      + lower     : true
      + minLower  : 0
      + minNumeric: 0
      + minSpecial: 0
      + minUpper  : 0
      + number    : true
      + result    : "&+r?{}J$J7"
      + special   : true
      + upper     : true
      + urn       : "urn:pulumi:dev::my-first-project::random:index/randomString:RandomString::password"
    }

Resources:
    2 unchanged

Duration: 1s
Permalink: file:///Users/lbriggs/.pulumi/stacks/dev.json

现在您可以在 Outputsresult 部分下看到一个随机生成的字符串。您创建的资源现在有许多您可以查看的属性。

这一切都很好,但是如果您想享受 IaC,您将不得不配置一些除了随机字符串之外的东西。试一试!

部署容器

到目前为止,您已经通过安装依赖项并注册一个简单的 random 资源来引导您的 Pulumi 体验。现在部署一些实际的基础设施,尽管是部署到您的本地机器。

首先,将 @pulumi/docker 提供商添加到您的堆栈。使用您选择的包管理器将其添加到项目中

npm install @pulumi/docker

您已经从 npm 中拉取了 Pulumi Docker 提供商包,这意味着您现在可以在您的项目中创建 Docker 镜像。

如果您没有在您的机器上安装 Docker,那么现在是安装它的绝佳时机。说明将取决于您的操作系统,因此请查看 Docker 的安装页面 以获取信息。

再次打开您最喜欢的 IDE 并运行一个 Docker 容器。修改您之前的 index.ts 文件,使其看起来像这样

import * as pulumi from "@pulumi/pulumi";
import * as random from "@pulumi/random";
import * as docker from "@pulumi/docker";

const password = new random.RandomString(`password`, {
    length: 10
})

const container = new docker.Container(`my-password`, {
    image: 'hashicorp/http-echo',
    command: [ pulumi.interpolate`-text=Your super secret password is: ${password.result}` ],
    ports: [{
        internal: 5678,
        external: 5678,
    }]
})

export const id = container.id

这将创建一个容器,该容器创建一个 Web 服务器。Web 服务器的输出是您的随机生成的字符串,在本例中是密码。运行此命令并查看会发生什么

pulumi up

Previewing update (dev):
     Type                       Name                  Plan
     pulumi:pulumi:Stack        my-first-project-dev
 +   └─ docker:index:Container  my-password           create

Outputs:
  + id      : output<string>
  ~ password: {
        id        : "&+r?{}J$J7"
        length    : 10
        lower     : true
        minLower  : 0
        minNumeric: 0
        minSpecial: 0
        minUpper  : 0
        number    : true
        result    : "&+r?{}J$J7"
        special   : true
        upper     : true
        urn       : "urn:pulumi:dev::my-first-project::random:index/randomString:RandomString::password"
    }

Resources:
    + 1 to create
    2 unchanged

Do you want to perform this update? yes
Updating (dev):
     Type                       Name                  Status
     pulumi:pulumi:Stack        my-first-project-dev
 +   └─ docker:index:Container  my-password           created

Outputs:
  + id      : "e73b34aeca34a64b72b61b0b9b8438637ce28853937bc359a1528ca99f49ddda"
    password: {
        id        : "&+r?{}J$J7"
        length    : 10
        lower     : true
        minLower  : 0
        minNumeric: 0
        minSpecial: 0
        minUpper  : 0
        number    : true
        result    : "&+r?{}J$J7"
        special   : true
        upper     : true
        urn       : "urn:pulumi:dev::my-first-project::random:index/randomString:RandomString::password"
    }

Resources:
    + 1 created
    2 unchanged

Duration: 2s
Permalink: file:///Users/lbriggs/.pulumi/stacks/dev.json

您会在 Outputs 部分注意到您输出的值已更改;它只是一个 Docker 容器 ID。检查您的非常简单的密码生成器是否工作

curl http://localhost:5678
Your super secret password is: &+r?{}J$J7

它工作了!您刚刚使用 TypeScript 配置了您的第一个基础设施组件!

关于 Pulumi 输出的快速说明

您会在创建 Docker 容器的代码中注意到它使用了特殊的 pulumi.interpolate 调用。如果您熟悉 TypeScript,您可能会好奇为什么需要它(因为它是 Pulumi 特有的)。这里有一个有趣的原因。

当 Pulumi 创建资源时,有些值在程序执行之前 Pulumi 是不知道的。在 Pulumi 中,这些被称为 Outputs。这些 Outputs 可以在上面的代码中看到;例如,在您的第一个 random 资源中,您使用了 export 关键字来输出 random 资源的属性,并且您还输出了您创建的容器的容器 ID。

由于 Pulumi 在执行时才知道这些 Outputs 的值,因此它需要特殊的助手在操作字符串时使用它们。如果您想了解更多关于这种特殊编程模型的信息,请观看这个短视频

总结

随着混合云基础设施的复杂性增加,IaC 在许多方面得到了发展。在基础设施配置领域,Pulumi 是一个很好的选择,可以使用您最喜欢的编程语言来配置启动基础设施所需的一切,然后您可以标记您最喜欢的配置管理工具来执行后续步骤。

接下来阅读什么
User profile image.
Lee Briggs 是 Pulumi 的 Staff Software Engineer。凭借近 10 年的设计、构建和维护分布式和复杂系统的经验,他身上留下了许多部署工具的伤疤。当他不尝试将单体应用程序放入容器时,他会踢足球、看足球比赛,并和他的狗 Cindy 一起散步。

评论已关闭。

Creative Commons License本作品根据知识共享署名-相同方式共享 4.0 国际许可协议获得许可。
© . All rights reserved.