この記事は NTTコミュニケーションズ Advent Calendar 2019 の20日目です。
昨日は @kirikei さんの Googleのデータ可視化&モデル分析ツール What-if Toolで覗いてみるTitanic生存者予測 でした。
はじめに
入社6年目、主にインフラエンジニアな仕事をしています。
今回は、最近盛り上がりつつあるPulumiという Infrastructure as Code(IaC)のツールの簡単な説明と、プログラミング言語でIaCができるというPulumiの特性を生かした利用方法について、紹介したいと思います。
Pulumiとは
本家HPのArchitecture & Conceptsには、以下のように書かれてます。
The Pulumi Cloud Development Platform is a combination of tools, libraries, runtime, and service that delivers a consistent development and operational >?control plane for cloud-native infrastructure. Not only does Pulumi enable you to manage your infrastructure as code, it also lets you define and manage your infrastructure using real programming languages (and all of their supporting tools) instead of YAML.
要するに、Infrastructure as Codeを実際のプログラミング言語を用いて、定義・管理できるよ、ということのようです。現在サポートされている言語は、以下の4種類で、.NETとGoはまだPreviewのようです。バージョン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)
また、デプロイできるCloud Provider は続々と増えているようです。
Kubernetesにデプロイしてみる
今回は例として、以下のような構成をKubernetesにデプロイしてみようと思います。
nginxのweb server(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 の Manifestの内容を、ほぼそのままObjectとして定義し、newでinstanceがデプロイされるようなイメージです。
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形式のManifestを読み込んでデプロイする
Kubernetesでは、ある程度YAMLでエコシステムが回ってる場合もあると思うので、再度Pulumiで焼き直しをすることが面倒なこともあると思います。その場合、YAMLをそのまま読み込んでデプロイすることも可能です。
Deploying a YAML Manifestにあるサンプルコードのように、PulumiのKubernetes SDKには、ローカルにある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のCreate/Validationを担っており、Google Cloud Storageなどの別のストレージを介してYAMLがやり取りされる場合、さらに状況が複雑になります。
現状、Node.jsのPulumi SDKでは、ローカルのファイルから読み込む機能以外はサポートされていません。代わりに、YAML形式のDataがあれば、そこから同様のことをしてくれる機能はあるようです。
そこで、 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にデプロイする方法の紹介をしました。特に最後の Google Cloud StorageにあるYAML読み込んでデプロイする
では、PulumiのKubernetes SDKと、単なる Node.jsのGoogle Cloud SDKを組み合わせて利用しました。
PulumiのSDKの中だけだとできないことも、純粋なプログラミングとして実現できることは、Pulumiでは実装可能だというのが、terraformなどの別のツールとの違いな気がします。
今回、Pulumiを触ってみて感じたのは、普段使っている言語でインフラをデプロイするという、よりアプリケーション開発者がCloudを使うためのツールとしての1つのアプローチだなと思いました。企業のサービスやシステムでは、まだまだまインフラエンジニアと呼ばれる人たちが多く存在しますが、どんどんシームレスになり、ソフトウェアを書くことが当たり前になるべきだと思います。NTTコミュニケーションズの中でも、そう言う流れが徐々に強くなりつつあるので、自身も含め、精進していきたいなと思っています。
以上です。明日は、 @kanatakita さんの記事です。