Unyablog.

のにれんのブログ

Pulumi で Google Cloud と k8s のリソースを管理するようにした

プライベートで Google Cloud を使っていて、今まではあまり IaC は使わずポチポチでやっていた。GKE ぐらいしかまともに使ってるリソースがなかったのでそれでよかったけど、最近 VPS をやめて各種クラウドに寄せるようにしており、クラウドのリソースが増えてきた。

ポチポチだと見落とす設定もあったりするので、IaC (Pulumi) を本格的に取り入れることにした*1

ついでに k8s のリソースも kpt と Helmfile から Pulumi 管理にしたので、そのあたりの話。

Pulumi

Pulumi とは IaC ソリューションの一つで、要するに Terraform 的なもの。 はじめは Terraform とか知ってるし普通すぎるし Pulumi のほうが面白そう、といった理由で使い始めたのだけれど、今でも満足して使っていている。

Terraform との大きな違いは、Python や TypeScript などの一般的な言語を使って記述すること。

www.pulumi.com

www.pulumi.com

※ いろいろ言語が選べるが、この記事では自分が使っている Python での話をしていく

一般的な言語が使えることの何が嬉しいかというと…

プログラミングと同じ作法・感覚での記述

Terraform だと、HCL という Hashicorp 独自の言語を使う必要があり、ちょっと変わった処理をしようとするとすぐリファレンスを見る必要がある*2。また、Provider のバージョン更新やディレクトリ構成など、 Terraform での作法を色々調べないといけない。

一方 Pulumi は普段プログラムを書くときと同じ感覚で IaC の記述ができる。 やることは非常に明快で、リソースに対応するクラスを使ってインスタンスを作るだけ。各種 Provider は Python module として提供されており、それらの更新やインストールは pip 経由で行うので、いつも通り requirements.txtpyproject.toml でバージョンの管理ができる。

この "いつも通り" が頭を使わずに済んで便利。

もちろん HCL より色々柔軟に書けて便利というのもあるが、それは個人レベルだとどっちでもいいかな。

IDE による強力なサポート

TypeScript や Python、Go など人気のプログラミング言語IDE のサポートが強力で、補完やドキュメントの参照などがスムーズに行うことができる。 Pulumi は Provider にちゃんと型ヒントやコメントをつけてくれているので、VSCode などで快適に IaC のコーディングができる。

Terraform にももちろんそういった VSCode 拡張はあるけど、どうしても MS が公式で提供しているものよりはクオリティやパフォーマンスが落ちてしまう。

Q. プログラミング言語で IaC…? Chef でみんな諦めたはずでは…?

あれは確かにつらいけど、背景が違うと思う。今は型があるし、IDE も強いし、Ruby ベースのよくわからない DSL ではない。また、生成したいものは基本的に宣言的なリソースであり Linux サーバーのような変化しうるものを相手にするものとは違うので、複雑な独自処理を書く機会は少ない。

CDK が便利だよね、というのと同じ感覚で見れば良いと思う。

その他嬉しいこと

Provider は Terraform と同等

Pulumi を使い始める前は Terraform で使えるものが使えないような事態を危惧していたけど、Provider は想像以上にたくさんある。

www.pulumi.com

これらすべてを Pulumi が毎秒更新しているかというとそんなことはなく、多くは Terraform provider から自動生成されたものらしい。なので Terraform でできることはだいたいできる。

www.pulumi.com

一部 Native provider というものがあり、これは Pulumi 開発のもので API から自動生成しているため対応の速さが自慢とのこと。

www.pulumi.com

Diff が見やすい

文字列の差分が色付きで出てくれて非常に見やすい。細かいパラメータの変更があったときにどこが変わったのかを目 grep せずに済む。

Import がめっちゃ楽

補足: 書いてる途中で Terraform にも似た機能が最近 Experimental リリースされたことを発見した Import - Generating Configuration | Terraform | HashiCorp Developer
Pulumi のほうが気楽そうではあるけど、TF でも自動生成してくれるようになるのはよさそう。

最初はポチポチで作って後から IaC で…ってのはよくあることだと思う。こういうときはポチポチで作ったリソースを State に Import することになる。

Pulumi ではこれが楽で、 Import 完了後にリソースに対応するコードを出力してくれるから、これをコピペすれば IaC 対応が完了する。

Please copy the following code into your Pulumi application. Not doing so
will cause Pulumi to report that an update will happen on the next update command.

Please note that the imported resources are marked as protected. To destroy them
you will need to remove the `protect` option and run `pulumi update` *before*
the destroy will take effect.

import pulumi
import pulumi_gcp as gcp

sa = gcp.serviceaccount.Account("sa",
    account_id="sa",
    display_name="sa",
    project="XXXXX",
    opts = pulumi.ResourceOptions(protect=True))
...

また、ドキュメントには Import コマンドの例が基本的に書かれているのも嬉しい(例: https://www.pulumi.com/registry/packages/gcp/api-docs/serviceaccount/account/#import)。

オープンソース

Terraform は最近ライセンスが BSL に変わってしまった。普段エンドユーザーとして使ってる分にはあまり変わらないが、やっぱりオープンソースだと嬉しいし、応援していきたい。

微妙なこと

State の refresh は自動で行われない

Terraform では plan すると refresh が必ず行われるので、IaC の外で変更があったときに気づきやすい。Pulumi は flag をつけないと自動で行われないので変更を見逃して適切な対応ができない可能性がある。

常に IaC でしか変更しないならいいけど、GKE のアップグレードとか色々あるよね。

Google Cloud で使う

Google Cloud 向けには Native provider と Terraform から変換した Classic Provider がある。今回はチュートリアルに従って Classic Provider を使っている*3

使ってみた感想としては普通に便利に使えるのだけど、 Google Cloud では Terraform を使ったほうが良さそう…😇

というのも、Google Cloud は Terraform を公式サポートしており、ドキュメントにも Terraform に関する記載が豊富だし、Provider の更新に Google の社内チームが関わっている。

github.com

また、最近はリソースを作っていると同等の TF 定義が右のパネルに出てきてくれたりする。

Pulumi を使っているとこれを参考しながら Pulumi のコードに落としていくことになるので、ちょっと悲しい…。普通に使えるんだけどね。

Kubernetes で使う

※ Terraform の k8s provider は使ったことないです

Manifest

自分は k8s の Manifest 自体は Kustomize などで YAML で書けばいいと思っており、こういうのを使う嬉しさは主に Pruning (不要になったリソースの削除)だと考えている。

その用途として kpt を使っていたけど、しょっちゅうよくわからない感じで壊れてイヤになってきたので Pulumi 管理にすることにした。そもそも kpt の主目的はそういうとこじゃない、というのもある *4

Manifest は Kustomize で組んでいるので移行は非常に簡単で、 Pulumi の Kustomize 対応を使って kustomization.yaml があるディレクトリの指定をするだけだった。

from pulumi_kubernetes.kustomize import Directory

Directory(
    "main",
    directory=".",
)

こうすると Kustomize をビルドして、ビルド結果に含まれる Deployment などの各リソースをそれぞれ管理してくれる。

使った感触としては非常に良くて、Server side apply してくれて安心だし、kpt よりも apply が早いし、diff も前述の通り見やすくなった。 変更時は READY になるかをちゃんと見てから古いリソースを削除したりと、安心して適用がしやすいのも良い。

Kustomize と Pulumi の組み合わせ、おすすめです!

Helm

Helm はもともと Helmfile で管理していた。それほど不満はなかったが、Pulumi でも Helm の管理ができるので Manifest と一元管理することにした。

Pulumi は Helm を入れるためのリソースとして ChartRelease があり、Chart が Pulumi 側で一旦展開され、てインストールするもの、Release がインストールも Helm に任せるものとなっている。

www.pulumi.com

↑にあるように一長一短あり、Chart だと Pulumi が各リソースを管理しているのでリソースの細かい diff まで確認することができる一方、Hooks など Helm 関連の機能が使えない、 Release だと Helm の全機能が使えるが diff は values の範囲に限られてしまう。

今回は互換重視で Release にした。これも良い感じに使えていて、Helmfile より UI が良くなった上に $ pulumi up コマンド一回で k8s Manifest と一緒にデプロイできるようになり、楽になった。

一方、これは cdk8s でも感じたことだが、values を Python で書くことになるので REAMDE とかにある例をそのままコピーできないのは面倒くさい。せっかく Pulumi ・ Pythonなので、 values は YAML として置いておき、PyYAML を使って読むようにしても良いかもしれない。


Pulumi のユーザーは Terraform より少ないのでナレッジは少ないけど、ドキュメントがしっかりしているので特に困ることはなかった。同じような話で Google Cloud などサポート的に Terraform を使ったほうが便利な局面もあるものの、 TF の Provider を自動変換してることもあって機能面では不自由なく使うことができる。なにより自分に馴染んだ言語で書けるというのがすばらしく、そして楽しい!

k8s のサポートもよくできているし、普通とはちょっと違うものを使ってみたい欲も満たせて*5なかなか良いものだった。これからも使って & 推していきたい。

*1:逆に Itamae は使わなくなった

*2:Object の書き方がいっぱいあるのやめてほしい…

*3:今は Native は Preview だし、 Classic は Google 公式の TF Provider が元になってるしで Classic のほうが少なくとも今は良いよね、という話もあった https://github.com/pulumi/pulumi/discussions/12470

*4:最近 kubectl 側で ApplySet というのが議論されているが、まだ alpha で今使うものではなさそう

*5:これが一番大事