ys memos

Blog

pulumi初めてみた


pulumi

2021/11/28


以下の作業は Linux で行っています.


Apache2.0 でライセンスされた OSS の IaC ツールであり,AWS CDK と類似したコード化を実現する.

IaC ツールとしてよく耳にするのが,AWS CloudFormation や OSS の Terraform であり,そちらはテンプレート型の記法により IaC を実現している.

AWS のリソースのみを使う場合は CloudFormation/CDK を使うのが良いと思うが,GCP を使いたかったので,Pulumi を使えるようになりたかったのが,今回 Pulumi を試してみた背景である.

ちなみに,調べてはじめて知ったが,GCP にも Cloud Deployment Manager という YAML 形式で記述する IaC ツールがあるようだ.



ここからサインアップする.


マイページにインストール方法が表示されるので,それに従いコマンドを実行する.

curl -fsSL https://get.pulumi.com | sh

/home/<username>/.pulumi内にバイナリが配置され,PATH も上記コマンドで追加される.

アンインストールしたい場合,~/.pulumiを削除して.bashrc等に追加された.pulumiを PATH に追加する記述を削除するとよい.


Pulumi で Google Cloud にインフラをデプロイするためのチュートリアルとして,以下を行う.

  1. Google Cloud アカウントへのアクセス設定
  2. Pulumi プロジェクトを作る
  3. Google Cloud Storage バケットをプロビジョニングする
  4. ホスティングを目的とし,バケットにindex.htmlを配置する.
  5. プロビジョニングしたリソースをクリーンアップする

言語の選択肢としては,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 に決定した(よかった).


ドキュメントの指示に従い,プロジェクトの設定をした.ProjectID は,事前に専用に作成した GCP プロジェクトを設定した.


  • Pulumi.yamlはプロジェクトの定義
  • Pulumi.dev.yamlはプロジェクトの設定(リージョンなど)
  • index.tsはインフラの記述

また,gcp.storage.Bucketlocationが生成時に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;

$ 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 上でもアクティビティを閲覧することで,同様の情報を確認できる.


指示に従い,

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'),
});

$ 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 れた.


$ 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_destroytrueでないとバケットを削除できないとのこと.

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なのか判別できるため,チョット優しいなと思った.


関連タグを探す