尝试将Pulumi SDK与Google Cloud SDK结合使用

这篇文章是NTT通信Advent Calendar 2019的第20天。
昨天是@kirikei先生的谷歌数据可视化与模型分析工具What-if Tool中的泰坦尼克号生存预测。

首先

我在公司工作了六年,主要负责基础设施工程师的工作。这次我想介绍一下最近很受关注的 Infrastructure as Code(IaC)工具 Pulumi的简单说明,并介绍如何利用Pulumi的编程语言特性来进行IaC的用法。

「Pulumi是什么」

在官方的HP网站的Architecture & Concepts页面中,有如下所述。

Pulumi云开发平台是一套工具、库、运行环境和服务的结合,为原生云基础设施提供一致的开发和运营控制平面。Pulumi不仅可以让您将基础设施管理为代码,还可以使用真正的编程语言(以及其所有支持工具)来定义和管理基础设施,而不是使用YAML。

简而言之,Infrastructure as Code是指通过实际的编程语言来定义和管理基础设施。目前支持的语言有以下四种,.NET和Go似乎仍处于预览阶段。从2.0版本开始似乎会正式支持。(来源:Pulumi 2.0 Roadmap)

    • Node.js – JavaScript, TypeScript, or any other Node.js compatible language

 

    • Python – Python 3.6 or greater

 

    • .NET Core – C#, F#, and Visual Basic on .NET Core 3.0 or greater

 

    Go – statically compiled Go binaries (documentation coming soon)

此外,似乎愈来愈多的云服务提供商能够进行部署。

尝试在Kubernetes上部署

这次作为例子,我打算将以下结构部署到Kubernetes上。
这是一个将nginx的web服务器(nginx)负载均衡在3个节点上,并且可以从外部访问的结构。

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: nginx
spec:
  backend:
    serviceName: nginx
    servicePort: 80

---
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: http-port
  selector:
    app: nginx

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers: 
      - name: nginx
        image: nginx:alpine
        ports:
        - name: http-port
          containerPort: 80

使用Pulumi的Kubernetes SDK进行部署。

将Kubernetes清单的内容几乎完全定义为对象,使得通过”new”关键字可以部署一个实例的概念。

import * as k8s from "@pulumi/kubernetes";

const appName = "nginx";
const appLabels = { app: appName };
const deployment = new k8s.apps.v1.Deployment(appName, {
  metadata: { name: appName },
  spec: {
    selector: { matchLabels: appLabels},
    replicas: 3,
    template: {
      metadata: { labels: appLabels},
      spec: { 
        containers: [{ name: appName,
                       image: "nginx:alpine",
                       ports: [{ name: "http-port", containerPort: 80 }]
        }]
      }
    }
  }
});

new k8s.core.v1.Service(appName, {
  metadata: { name: appName,
              labels: deployment.spec.template.metadata.labels
  },
  spec: {
    type: "NodePort",
    ports: [{ port: 80, targetPort: "http-port" }],
    selector: appLabels
  }
});

new k8s.networking.v1beta1.Ingress(appName, {
  metadata: { name: appName },
  spec: { 
    backend: { serviceName: appName, servicePort: 80 }
  }
});

读取并部署YAML格式的清单文件。

我认为在Kubernetes中,有时候整个生态系统都是基于YAML运行的,所以重新使用Pulumi可能会有些麻烦。在这种情况下,可以直接读取YAML并进行部署。

Pulumi的Kubernetes SDK提供了像“Deploying a YAML Manifest”中的示例代码一样的功能,可以读取本地的YAML文件,并自动解析YAML中的资源类型(Kubernetes的Kind)进行部署。因此,我们只需要编写非常简单的代码即可完成部署操作。

import * as k8s as "@pulumi/kubernetes";

const manifest = "nginx.yml"
new k8s.yaml.ConfigFile(`k8s/app/${manifest}`, {
  file: manifest
});

从Google Cloud Storage中读取YAML文件并进行部署。

在生态系统中,如果另一个软件负责处理YAML的创建/验证,并且通过其他存储方式如Google Cloud Storage来交换YAML,那么情况会变得更加复杂。

目前,Node.js的Pulumi SDK只支持从本地文件中读取功能,不支持其他功能。相反,如果有YAML格式的数据,似乎可以通过它来实现类似的功能。
因此,我们将尝试与Google Cloud的SDK结合使用。

import * as gcs from "@google-cloud/storage";
import * as k8s from "@pulumi/kubernetes";

async function readFileFromGcs(bucket: gcs.Bucket, file: string): Promise<string> {
  const remoteFile = bucket.file(file);
  return new Promise((resolve) => {
    let yamlData = '';
    remoteFile.createReadStream()
      .on('data', function(data) {
        yamlData += data;
      }).on('end', function() {
        resolve(yamlData);
      });
  });
};

async function main(): Promise<any> {
  const storage = new gcs.Storage({keyFilename: './key.json'});
  const bucket = storage.bucket('test-pulumi');

  const manifest = "nginx.yml"
  const yamlData = await readFileFromGcs(bucket, manifest)

  new k8s.yaml.ConfigGroup(`k8s/app/${manifest}`, {
    yaml: yamlData });
};

main();

执行结果

使用Google Cloud的SDK组合后的执行结果如下。

$ pulumi up
Previewing update (dev):

     Type                                        Name                 Plan
 +   pulumi:pulumi:Stack                         manifest_on_gcs-dev  create
 +   └─ kubernetes:yaml:ConfigGroup              k8s/app/nginx.yml    create
 +      ├─ kubernetes:core:Service               nginx                create
 +      ├─ kubernetes:networking.k8s.io:Ingress  nginx                create
 +      └─ kubernetes:apps:Deployment            nginx                create

Resources:
    + 5 to create

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

     Type                                        Name                 Status
 +   pulumi:pulumi:Stack                         manifest_on_gcs-dev  created
 +   └─ kubernetes:yaml:ConfigGroup              k8s/app/nginx.yml    created
 +      ├─ kubernetes:networking.k8s.io:Ingress  nginx                created
 +      ├─ kubernetes:core:Service               nginx                created
 +      └─ kubernetes:apps:Deployment            nginx                created

Resources:
    + 5 created

Duration: 17s

YAML成功加载并部署完毕。虽然本次省略了,但前文提到的“使用Pulumi的Kubernetes SDK进行部署”和“加载本地YAML格式的Manifest并部署”的模式也会得到相同的执行结果。

最后

这一次,我们介绍了使用Pulumi将应用部署到Kubernetes的方法。尤其是在最后一部分,我们使用了Pulumi的Kubernetes SDK和纯粹的Node.js Google Cloud SDK来读取Google Cloud Storage中的YAML并进行部署。
我觉得Pulumi与其他工具(如terraform)的不同之处在于,它可以实现纯粹的编程方式无法完成的任务。

通过尝试使用Pulumi,我感受到的是在平常所使用的编程语言中部署基础架构,这是作为开发云应用程序的工具之一的一种方法。在企业的服务和系统中,仍然存在许多被称为基础架构工程师的人,但我认为我们应该不断变得无缝,并将编写软件视为理所当然的事情。在NTT通信中,这种趋势正在逐渐加强,我希望自己也能不断进步。

以上是以上内容。明天是@kanatakita的文章。

广告
将在 10 秒后关闭
bannerAds