2021/11/28
はじめに
以下の作業は Linux で行っています.
Pulumi とは?
Apache2.0 でライセンスされた OSS の IaC ツールであり,AWS CDK と類似したコード化を実現する.
IaC ツールとしてよく耳にするのが,AWS CloudFormation や OSS の Terraform であり,そちらはテンプレート型の記法により IaC を実現している.
AWS のリソースのみを使う場合は CloudFormation/CDK を使うのが良いと思うが,GCP を使いたかったので,Pulumi を使えるようになりたかったのが,今回 Pulumi を試してみた背景である.
ちなみに,調べてはじめて知ったが,GCP にも Cloud Deployment Manager という YAML 形式で記述する IaC ツールがあるようだ.
セットアップ
サインアップ
ここからサインアップする.
CLI のインストール
マイページにインストール方法が表示されるので,それに従いコマンドを実行する.
curl -fsSL https://get.pulumi.com | sh
/home/<username>/.pulumi
内にバイナリが配置され,PATH も上記コマンドで追加される.
アンインストールしたい場合,~/.pulumi
を削除して.bashrc
等に追加された.pulumi
を PATH に追加する記述を削除するとよい.
Get Started with Google Cloud
Pulumi で Google Cloud にインフラをデプロイするためのチュートリアルとして,以下を行う.
- Google Cloud アカウントへのアクセス設定
- Pulumi プロジェクトを作る
- Google Cloud Storage バケットをプロビジョニングする
- ホスティングを目的とし,バケットに
index.html
を配置する. - プロビジョニングしたリソースをクリーンアップする
Begin
言語の選択肢としては,TyeScript/JavaScript/Python/Go/C#がある.
以前 aws-cdk を使っていた時には TypeScript を使っていたので思考停止して TS を使いたかったが,初めて触るツールだったので,再度技術選定をしようと思った.
おそらく,「プログラミング言語で IaC を実現」といえど,プログラミングと言うよりは一定のルールに基づいてインフラを記述するための手段の一つとしてプログラミング言語の記述形式を取っているという前提の元だと思うので,以下の判断基準が挙げられる.
- サンプルの豊富さ
- 言語の記法が簡潔
- 言語自体に慣れている
この中でクリティカルなのがサンプルの豊富さであるが,最初に脳内で2つに絞った.
まずサンプル数を確認する前に絞り込んだが,TypeScript/Python/Go の 3 択にしようと思った.JS を使う選択をするなら TS の方が良いという判断も含めており,それは,TS/JS のサンプルは実質相互に利用可能であり,TS の方が補完が効きやすいという理由からである.C#を使わないと決めたのは,そもそも C#を使ったことがなく今後も自分は使わなそうという理由からである.
逆に,TypeScript/Python/Go を候補から外さなかった理由として,TypeScript は Web 開発によく使われる言語であり自分も慣れ親しんでいるため,Python は広く用いられており記述の簡潔さがありつつ自分もある程度使っているため,Go はバックエンドで使いやすい言語で自分も適度に触れているためである.
Pulumi のサンプルはどうやら公式 Github アカウントのリポジトリにあり,リポジトリのルートに置かれているようである.
サンプルの言語は命名規則で分類されており,ts/js/py/go/cs
で区別されている.
見る限り,上記 5 つに分類されていないもの(fs
)があったが今回は無視した.
適当に Python スクリプトを書いてカウントしたところ,{'ts': 113, 'js': 14, 'py': 58, 'go': 32, 'cs': 34}
となり,ts が最も多く,次点の py の2倍近かった.
以上の結果を踏まえ,Pulumi を触れるための言語を TypeScript に決定した(よかった).
Create a New Project
ドキュメントの指示に従い,プロジェクトの設定をした.ProjectID は,事前に専用に作成した GCP プロジェクトを設定した.
Review the New Project
Pulumi.yaml
はプロジェクトの定義Pulumi.dev.yaml
はプロジェクトの設定(リージョンなど)index.ts
はインフラの記述
また,gcp.storage.Bucket
のlocation
が生成時にUS
となっているので,GCP のバケットロケーションリファレンスをもとに`ASIA-NORTHEAST1'と以下のように変更した.
import * as pulumi from '@pulumi/pulumi';
import * as gcp from '@pulumi/gcp';
// Create a GCP resource (Storage Bucket)
const bucket = new gcp.storage.Bucket('my-bucket', {
location: 'ASIA-NORTHEAST1',
});
// Export the DNS name of the bucket
export const bucketName = bucket.url;
Deploy the Stack
$ pulumi up
を実行すると,
Previewing update (dev)
View Live: https://app.pulumi.com/<username>/quickstart/dev/previews/<id>
Type Name Plan
+ pulumi:pulumi:Stack quickstart-dev create
+ └─ gcp:storage:Bucket my-bucket create
Resources:
+ 2 to create
Do you want to perform this update? [Use arrows to move, enter to select, type to filter]
yes
> no
details
と表示され,View Live
リンクに飛ぶと各種情報をブラウザから閲覧することが出来る(すごい).
また,detail
を選択すると,aws-cdk 同様にインフラの変更部を表示することが出来る.
yes
を選択することで,生成に成功した雰囲気がでた.
GCP コンソールの該当プロジェクトの Storage を確認したところ,しっかりとバケットが生成されていた.
$ pulumi stack output bucketName
を実行すると,バケット名が出力される.
また,Pulumi 上でもアクティビティを閲覧することで,同様の情報を確認できる.
Modify the Program
指示に従い,
cat <<EOT > index.html
<html>
<body>
<h1>Hello, Pulumi!</h1>
</body>
</html>
EOT
を実行するとデプロイするindex.html
が生成される(手動で作っても問題ない).
また,以下をindex.ts
に追記した.
const bucketObject = new gcp.storage.BucketObject('index.html', {
bucket: bucket.name,
source: new pulumi.asset.FileAsset('index.html'),
});
Deploy the Changes
$ pulumi up
コマンドを実行し,detail
を確認ののちyes
を選択すると,今回は Pulumi からメールが届いた.
$ gsutil ls $(pulumi stack output bucketName)
により,detail
選択時に表示されたindex.html-xxxx
がストレージに保存されていることが確認できた.
さらに,ドキュメントに従いindex.ts
を
import * as pulumi from '@pulumi/pulumi';
import * as gcp from '@pulumi/gcp';
// Create a GCP resource (Storage Bucket)
const bucket = new gcp.storage.Bucket('my-bucket', {
location: 'ASIA-NORTHEAST1',
website: {
mainPageSuffix: 'index.html',
},
uniformBucketLevelAccess: true,
});
const bucketIAMBinding = new gcp.storage.BucketIAMBinding(
'my-bucket-IAMBinding',
{
bucket: bucket.name,
role: 'roles/storage.objectViewer',
members: ['allUsers'],
}
);
const bucketObject = new gcp.storage.BucketObject('index.html', {
bucket: bucket.name,
contentType: 'text/html',
source: new pulumi.asset.FileAsset('index.html'),
});
// Export the DNS name of the bucket
export const bucketName = bucket.url;
export const bucketEndpoint = pulumi.concat(
'http://storage.googleapis.com/',
bucket.name,
'/',
bucketObject.name
);
と書き換え,再度pulumi up
した.
また,
$ curl $(pulumi stack output bucketEndpoint)
を実行すると,先ほど作成して Storage に配置したindex.html
の内容が出力された.
さらに,
$ echo $(pulumi stack output bucketEndpoint)
として出力されたアドレスにアクセスすると,Hello, Pulumi!
と書かれたページが表示 s れた.
Destroy the Stack
$ pulumi destroy
チュートリアルに従い実行したが,コマンドに失敗し,インフラの削除が出来ず,以下のエラーが出力された.
Diagnostics:
gcp:storage:Bucket (my-bucket):
error: deleting urn:pulumi:dev::quickstart::gcp:storage/bucket:Bucket::my-bucket: 1 error occurred:
* Error trying to delete bucket my-bucket-f9946b3 containing objects without `force_destroy` set to true
再度コマンドを実行したが,結果は変わらなかった.
そこで,ログを見ると,Storage のオプションがマズイらしく,Destroy ができない.具体的には,force_destroy
がtrue
でないとバケットを削除できないとのこと.
Pulumi のチュートリアルコードが間違えているらしく,gcp.storage.Bucket
のオプションがこのままだと Destroy できないようだ(悲しい).
そこで,
const bucket = new gcp.storage.Bucket('my-bucket', {
location: 'ASIA-NORTHEAST1',
forceDestroy: true,
website: {
mainPageSuffix: 'index.html',
},
uniformBucketLevelAccess: true,
});
と書き換え,再度pulumi up
した.
その後,
$ pulumi destroy
をすると成功し,GCP コンソールを見るとバケットごとなくなっていた(嬉しい).
おわりに
途中で気づいたのだが,Pulumi のサイトのアイコンが,サブドメイン(用途)毎に回転していて,かなりデザインに凝っていてかなり好印象だった.
機能面でも,同じアイコンが並んだ時にどれがwww.pulumi.com
なのかapp.pulumi.com
なのか判別できるため,チョット優しいなと思った.