昨今周りで Pulumi というものを良く聞くので実際使ってみました。
マルチクラウド向けのオーケストレーションツールのようですが、ページだけを見ると ただのSDK
っぽくしか見えなかったんですよ。実際使ってみて違うということがわかりました。
インストールから初期設定まで
Pulumiコマンド自体をまずはインストールするため以下を実行。
curl -fsSL https://get.pulumi.com | sh
brewでも入るらしい。
こんな感じでインストールが進行して、成功すると now installed!
と出る。
=== Installing Pulumi v0.17.1 ===
+ Downloading https://get.pulumi.com/releases/sdk/pulumi-v0.17.1-darwin-x64.tar.gz...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 36.9M 100 36.9M 0 0 902k 0 0:00:41 0:00:41 --:--:-- 3777k
+ Extracting to /Users/yuzu/.pulumi/bin
+ Adding $HOME/.pulumi/bin to $PATH in /Users/yuzu/.zshrc
=== Pulumi is now installed! 🍹 ===
+ Please restart your shell or add add /Users/yuzu/.pulumi/bin to your $PATH
+ If you're new to Pulumi, here are some resources for getting started:
- Getting Started Guide: https://pulumi.io/quickstart
- Examples Repo: https://github.com/pulumi/examples
- Create a New Project: Run 'pulumi new' to create a new project using a template
上記の通り /Users/yuzu/.pulumi/bin
にパスを通しておく。
次に作業用ディレクトリを作って、そこで pulumi new
する。
mkdir -p ~/dev/pulumi_work
cd ~/dev/pulumi_work
pulumi new
Manage your Pulumi stacks by logging in.
Run `pulumi login --help` for alternative login options.
Enter your access token from https://app.pulumi.com/account/tokens
or hit <ENTER> to log in using your browser :
We've launched your web browser to complete the login process.
Waiting for login to complete...
Please choose a template:
> aws-go A minimal AWS Go Pulumi program
aws-javascript A minimal AWS JavaScript Pulumi program
aws-python A minimal AWS Python Pulumi program
aws-typescript A minimal AWS TypeScript Pulumi program
azure-go A minimal Azure Go Pulumi program
azure-javascript A minimal Azure JavaScript Pulumi program
azure-python A minimal Azure Python Pulumi program
azure-typescript A minimal Azure TypeScript Pulumi program
gcp-go A minimal Google Cloud Go Pulumi program
gcp-javascript A minimal Google Cloud JavaScript Pulumi program
gcp-python A minimal Google Cloud Python Pulumi program
gcp-typescript A minimal Google Cloud TypeScript Pulumi program
go A minimal Go Pulumi program
hello-aws-javascript A simple AWS serverless JavaScript Pulumi program
javascript A minimal JavaScript Pulumi program
kubernetes-javascript A minimal Kubernetes JavaScript Pulumi program
kubernetes-python A minimal Kubernetes Python Pulumi program
kubernetes-typescript A minimal Kubernetes TypeScript Pulumi program
openstack-go A minimal OpenStack Go Pulumi program
openstack-javascript A minimal OpenStack JavaScript Pulumi program
openstack-python A minimal OpenStack Python Pulumi program
openstack-typescript A minimal OpenStack TypeScript Pulumi program
python A minimal Python Pulumi program
typescript A minimal TypeScript Pulumi program
すると認証が走る。自分の場合は先にブラウザ側でGithubログインしていたので、直接 Thansk for logging in!
という画面だけが表示された。
で、上記画面で >
が出ているが、これ実はカーソル。上下で選択するようになっている。
openstack-python
を選択してみた。
するといろいろ質問される。すべてデフォルトで答えてみた。
Enter a value or leave blank to accept the (default), and press <ENTER>.
Press ^C at any time to quit.
project name: (pulumi_work)
project description: (A minimal OpenStack Python Pulumi program)
Created project 'pulumi_work'
stack name: (dev)
Created stack 'dev'
Your new project is configured and ready to go! ✨
To perform an initial deployment, run the following commands:
1. virtualenv -p python3 venv
2. source venv/bin/activate
3. pip3 install -r requirements.txt
Then, run 'pulumi up'
以下のように出ていますね。
1. virtualenv -p python3 venv
2. source venv/bin/activate
3. pip3 install -r requirements.txt
が、virtualenv直接使ってない人(pyenv + virtualenv) なので、それようにvirtualenv切ってみる。
3系が要求事項のようだし、3.6.6がレディだったのでそれにした。
pyenv virtualenv 3.6.6 pulumi
pyenv shell pulumi
pip install -r requirements.txt
とりあえず全部インストールOK。
pulumi up
さて次は、
Then, run 'pulumi up'
と書かれているが、そもそも pulumi up
すると何が起きるのか説明されてない。
ので、同ディレクトリに存在するファイルをみてみる。
まず Pulumi.yaml
。
name: pulumi_work
runtime: python
description: A minimal OpenStack Python Pulumi program
メタデータしか入ってない。
じゃあ、 __main__.py
かな?
import pulumi
from pulumi_openstack import compute
# Create an OpenStack resource (Compute Instance)
instance = compute.Instance('test',
flavor_name='s1-2',
image_name='Ubuntu 16.04')
# Export the IP of the instance
pulumi.export('instance_ip', instance.access_ip_v4)
???
OpenStackのAuthURLはここ!とか、認証情報はこれ!とかそういうのをどこに持っているのかさっぱりわからない。
これ、実行すると何が起きるんだ・・・と思いつつ試しに叩いてみる。
% pulumi up
Previewing update (dev):
Type Name Plan Info
+ pulumi:pulumi:Stack pulumi_work-dev create
└─ openstack:compute:Instance test 1 error
Diagnostics:
openstack:compute:Instance (test):
error: One of 'auth_url' or 'cloud' must be specified
error: an error occurred while advancing the preview
error: One of 'auth_url' or 'cloud' must be specified
まぁ、そりゃそうだ・・・と思いつつどこで指定するのかを調べる。
pulumi up --help
, pulumi --help
共にそれっぽい情報がない。
で、 ここ をみたら、
OpenStack setup page というリンクがあり、そこにこう書かれている。
source openrc.sh
つまりバックエンドでOpenStack SDK使ってんだろうな・・・と思いつつ cloud
と書かれている理由も理解。
とりあえずopenrcでいく。
export OS_AUTH_URL=https://<my-keystone-url>/v3/
export OS_USERNAME=<my-username>
export OS_PASSWORD=<my-password>
export OS_REGION_NAME=<my-regio>
export OS_TENANT_ID=<my-tenant-id>
export OS_IDENTITY_API_VERSION=3
export OS_PROJECT_DOMAIN_NAME=default
export OS_USER_DOMAIN_NAME=default
あとは、 __main__.py
を少々修正する。
import pulumi
from pulumi_openstack import compute
# Create an OpenStack resource (Compute Instance)
instance = compute.Instance('test',
flavor_name='m1.tiny', <-- ここを実際存在するフレーバー名に修正
image_name='cirros-0.4.0-x86_64-disk') <-- ここを実際存在するイメージ名に修正
これで pulumi up
Previewing update (dev):
Type Name Plan
+ pulumi:pulumi:Stack pulumi_work-dev create
+ └─ openstack:compute:Instance pulumi-test-instance1 create
Resources:
+ 2 to create
Do you want to perform this update?
yes
> no
details
おー、来ましたね。
これも矢印で(なんか斬新感)yesを選んでみる。
Previewing update (dev):
Type Name Plan
pulumi:pulumi:Stack pulumi_work-dev
+ └─ openstack:compute:Instance pulumi-test-instance1 create
Resources:
+ 1 to create
1 unchanged
Do you want to perform this update? yes
Updating (dev):
Type Name Status
pulumi:pulumi:Stack pulumi_work-dev running... <-- ここがチカチカする
+ └─ openstack:compute:Instance pulumi-test-instance1 creating... <-- ここがチカチカする
上記のように ...
の部分がローディングを表してくれます。
で、最後に
% pulumi up
Previewing update (dev):
Type Name Plan
pulumi:pulumi:Stack pulumi_work-dev
+ └─ openstack:compute:Instance pulumi-test-instance1 create
Resources:
+ 1 to create
1 unchanged
Do you want to perform this update? yes
Updating (dev):
Type Name Status
pulumi:pulumi:Stack pulumi_work-dev
+ └─ openstack:compute:Instance pulumi-test-instance1 created
Outputs:
+ instance_ip: "10.0.0.33"
Resources:
+ 1 created
1 unchanged
Duration: 19s
Permalink: https://app.pulumi.com/keiichi-hikita/pulumi_work/dev/updates/7
となりました。
Outputに書いておくと作成時に表示してくれるわけね。
実際インスタンスもできている。(対向はdevstackにしてたので、そこで確認)
stack@devstack:~/devstack$ openstack server list
+--------------------------------------+-------------------------------+--------+---------------------------------------------------------+--------------------------+---------+
| ID | Name | Status | Networks | Image | Flavor |
+--------------------------------------+-------------------------------+--------+---------------------------------------------------------+--------------------------+---------+
| 0a3b4561-19c7-41a6-9090-2f9a7516fad0 | pulumi-test-instance1-0321052 | ACTIVE | private=10.0.0.33, fddf:6a77:2fa7:0:f816:3eff:fe51:5bf9 | cirros-0.4.0-x86_64-disk | m1.tiny |
+--------------------------------------+-------------------------------+--------+---------------------------------------------------------+--------------------------+---------+
名前に勝手にSuffixがつくのは、 Auto-naming という仕様らしい。
そのままもう一度pulumi up
Terraformとかの考え方だと、何も考えずにもう一度applyしても 差分無いよ
と言われるわけだが、Pulumiだとどうなるのかをやってみた。
% pulumi up
Previewing update (dev):
Type Name Plan
pulumi:pulumi:Stack pulumi_work-dev
Resources:
2 unchanged
Do you want to perform this update? yes
Updating (dev):
Type Name Status
pulumi:pulumi:Stack pulumi_work-dev
Outputs:
instance_ip: "10.0.0.33"
Resources:
2 unchanged
Duration: 2s
Permalink: https://app.pulumi.com/keiichi-hikita/pulumi_work/dev/updates/8
期待通り 2 unchanged
(0 changedでよくね?) と表示されたので、そういう差分管理はちゃんとやってくれる模様。
実際に変更の掛かるpulumi upをしてみる
ためしに、 __main.py__
をこうしてみる。
import pulumi
from pulumi_openstack import compute
# Create an OpenStack resource (Compute Keypair)
keypair = compute.Keypair('pulumi-keypair1')
# Create an OpenStack resource (Compute Instance)
instance = compute.Instance('pulumi-test-instance1',
flavor_name='m1.tiny',
key_pair=keypair.name,
image_name='cirros-0.4.0-x86_64-disk')
# Export the IP of the instance
pulumi.export('instance_ip', instance.access_ip_v4)
Keypairを足して、それをInstanceの引数 key_pair
に渡してみる。
で、pulumi up。どうなるか。
Previewing update (dev):
Type Name Plan Info
pulumi:pulumi:Stack pulumi_work-dev
+ ├─ openstack:compute:Keypair pulumi-keypair1 create
+- └─ openstack:compute:Instance pulumi-test-instance1 replace [diff: +keyPair~name
Resources:
+ 1 to create
+-1 to replace
2 changes. 1 unchanged
Do you want to perform this update?
yes
> no
details
Keypairを変えるとTerraformでいうForceNewがかかる模様。(Terraformと同じ仕様だが多分Novaの仕様なんだろうな)
Replaceされると書かれているので。
同じようにyesを選択。
最終的にこうなりました。
% pulumi up
Previewing update (dev):
Type Name Plan Info
pulumi:pulumi:Stack pulumi_work-dev
+ ├─ openstack:compute:Keypair pulumi-keypair1 create
+- └─ openstack:compute:Instance pulumi-test-instance1 replace [diff: +keyPair~name
Resources:
+ 1 to create
+-1 to replace
2 changes. 1 unchanged
Do you want to perform this update? yes
Updating (dev):
Type Name Status Info
pulumi:pulumi:Stack pulumi_work-dev
+ ├─ openstack:compute:Keypair pulumi-keypair1 created
+- └─ openstack:compute:Instance pulumi-test-instance1 replaced [diff: +keyPair~nam
Outputs:
~ instance_ip: "10.0.0.33" => "10.0.0.30"
Resources:
+ 1 created
+-1 replaced
2 changes. 1 unchanged
Duration: 29s
Permalink: https://app.pulumi.com/keiichi-hikita/pulumi_work/dev/updates/9
なるほど。
ちゃんと差分更新もできるようですね。
あと、リソースの依存関係を直接オブジェクトライクに( key_pair=keypair.name
みたいに)できるのは新鮮。
普通に間にロジック挟むとどうなる?
このPulumiを最初見た時に感じていたのは、
- ただのSDKみたいなものでしか無いなら依存関係とかかけないじゃないか?それって便利?
- ただ、テンプレートじゃなくてロジックをかけるということは、間に色々処理が挟めて良さげ
という2点でした。
前者はTerraformやHeatがやってくれる順序制御より劣るのでは?と思っていた点だし、後者はPulumi良さげと思っていた点。(.tfの中にHCLでロジックかけないしね)
上記1は何ら問題ないことがわかったので、今度は2を試してみる。
main.pyを以下のように修正。
import pulumi
from pulumi_openstack import compute
def func():
print("Hello World!!")
# Create an OpenStack resource (Compute Keypair)
keypair = compute.Keypair('pulumi-keypair1')
# Create an OpenStack resource (Compute Instance)
instance = compute.Instance('pulumi-test-instance1',
flavor_name='m1.tiny',
key_pair=keypair.name,
image_name='cirros-0.4.0-x86_64-disk')
# Export the IP of the instance
pulumi.export('instance_ip', instance.access_ip_v4)
func()
足したのはfuncの部分。
で、pulumi up。
% pulumi up
Previewing update (dev):
Type Name Plan Info
pulumi:pulumi:Stack pulumi_work-dev 1 message
Diagnostics:
pulumi:pulumi:Stack (pulumi_work-dev):
Hello World!!
Resources:
3 unchanged
Do you want to perform this update?
> yes
no
details
Diagnostics:
・・・というところに Hello World!!
と表示されている。。。
つまり関数はupした際のプランニング時点で評価されて、以上!!
なのだろうか。
試しにyesしてみる。
Do you want to perform this update? yes
Updating (dev):
Type Name Status Info
pulumi:pulumi:Stack pulumi_work-dev 1 message
Diagnostics:
pulumi:pulumi:Stack (pulumi_work-dev):
Hello World!!
Outputs:
instance_ip: "10.0.0.30"
Resources:
3 unchanged
Duration: 1s
Permalink: https://app.pulumi.com/keiichi-hikita/pulumi_work/dev/updates/10
とりあえずロジックは実行されるようですね。
でも、これだとあまり現実的ではない。
一旦もう少し踏み込んで見るために、一度Destroyしてみます。
% pulumi destroy
Previewing destroy (dev):
Type Name Plan
- pulumi:pulumi:Stack pulumi_work-dev delete
- ├─ openstack:compute:Instance pulumi-test-instance1 delete
- └─ openstack:compute:Keypair pulumi-keypair1 delete
Resources:
- 3 to delete
Do you want to perform this destroy? yes
Destroying (dev):
Type Name Status
- pulumi:pulumi:Stack pulumi_work-dev deleted
- ├─ openstack:compute:Instance pulumi-test-instance1 deleted
- └─ openstack:compute:Keypair pulumi-keypair1 deleted
Resources:
- 3 deleted
Duration: 15s
Permalink: https://app.pulumi.com/keiichi-hikita/pulumi_work/dev/updates/11
あー、ちゃんと消えましたね。
で、次にDiagnosticsに入る前にちょっと小休止。
さっきから出ているURLはなんなのか?
ずっとさっきから、upしたりdestroyした後にこういうURLが出ています。
Permalink: https://app.pulumi.com/keiichi-hikita/pulumi_work/dev/updates/11
これなんなのだろう・・・とブラウザから接続してみて驚きました。
なんと作業履歴が全て残っているんですね!!
例えば直近のURLにつなぐとこんな画面に。
こりゃすごい。
ここまでは、そこまで便利なのかなぁと思っていたのですが、この機能はいいですね。
履歴全部残してくれるならもし商用適用した時に切り分けとか相当楽になるんじゃないか?これ。
話を戻してDiagnostics
本来のDiagnosticsの使い方はまだ勉強中なんですが、こんな事はできるのだろうか。
import pulumi
import random
import string
from pulumi_openstack import compute
def random_name(n):
randlst = [random.choice(string.ascii_letters + string.digits) for i in range(n)]
return ''.join(randlst)
# Create an OpenStack resource (Compute Keypair)
keypair = compute.Keypair('pulumi-keypair1')
# Create an OpenStack resource (Compute Instance)
instance = compute.Instance(random_name(10),
flavor_name='m1.tiny',
key_pair=keypair.name,
image_name='cirros-0.4.0-x86_64-disk')
# Export the IP of the instance
pulumi.export('instance_ip', instance.access_ip_v4)
これでpulumi upしてみましたが、どうやら問題はないようです。
% pulumi up
Previewing update (dev):
Type Name Plan
+ pulumi:pulumi:Stack pulumi_work-dev create
+ ├─ openstack:compute:Keypair pulumi-keypair1 create
+ └─ openstack:compute:Instance dcvnkfk5Iu create
Resources:
+ 3 to create
Do you want to perform this update? yes
Updating (dev):
Type Name Status
+ pulumi:pulumi:Stack pulumi_work-dev created
+ ├─ openstack:compute:Keypair pulumi-keypair1 created
+ └─ openstack:compute:Instance hLTf397LGV created
Outputs:
instance_ip: "10.0.0.13"
Resources:
+ 3 created
Duration: 18s
Permalink: https://app.pulumi.com/keiichi-hikita/pulumi_work/dev/updates/12
ただ、planと実際の結果で名前が変わっちゃってますね。
これだと、毎回pulumi upの度に、pulumiは 差分がある!
と錯覚してしまうようでした。(錯覚と言うか文字列的に差があるんだから当然っちゃ当然)
試しにそのままpulumi upを実行してみてください。インスタンスのReplaceが発生しちゃいます。
とはいえ、これはなにかに使えそうな気はします。
TerraformのProvisionerみたいな使い方ができるのだろうか。ちょっと調べてみよう。
例えば、近隣のNW機器にnetconfでコンフィグを投げつつインスタンス起動!みたいな使い方できたら嬉しいのと、それをapply時とdestroy時にロジック的に分岐させれれば嬉しいな。
イメージ的に、
import pulumi
import random
import string
from pulumi_openstack import compute
def netconf_set():
if pulumi.is_apply: <-- こんな設定とかをどっかから引く。環境変数とかないかな
// set netconf settings <-- なんか設定
else:
// remove netconf settings <-- 入れたもの消す
# Create an OpenStack resource (Compute Keypair)
keypair = compute.Keypair('pulumi-keypair1')
# Create an OpenStack resource (Compute Instance)
instance = compute.Instance('pulumi-test-instance',
flavor_name='m1.tiny',
key_pair=keypair.name,
image_name='cirros-0.4.0-x86_64-disk')
# Export the IP of the instance
pulumi.export('instance_ip', instance.access_ip_v4)
netconf_set() <-- この中で勝手に分岐
・・・な感じなんですが多分無理だろな。
とりあえずざっと使ってみましたので、これからDocument読みます。
今わからないと思っているのは、
- terraform.tfstate的なものはどこにあるんだ?(作業ディレクトリにも ~/.pulumiにもそれっぽいのがないように見える)
- Diagnosticsって何?
- 他色々機能ありそう
とはいえ、Providerが増えてきたら便利そうですねぇこれ。
感じたメリットは、
- ロジックとして書けるのでAPI化とかは楽そう
- シングルPythonファイルで動くイメージなのでFaaS的な実行に向いている感
- 依存関係がちゃんとかけれる
- 主要クラウドはサポートしている
一方使い始めたら困りそうなのは以下ですがどんどん改善されていきそうな気も。
- Providerのリポジトリにgo/python/nodeと全部入っているからなのかわからないけどProviderのドキュメントがおざなり(読んでもよくわかんない)
- Providerのどこに実際のCRUDポーリングの処理が入ってんのかよく分かってない
** Classだけだと全然情報足らんよね
** 例えば これ とかほとんど情報量無い
** あとPycodestyleしておいてくれると見やすいのに
またなにか分かったら書きます。