こんにちは!クラウドインフラ・SREの世界へようこそ。
日々、YAMLの山に埋もれて「このインデント、本当に合ってるのか……?」と冷や汗をかいたり、applyした瞬間にクラスターが盛大に爆発して頭を抱えたりしていませんか?
今回は、そんなKubernetes(K8s)マニフェスト地獄からあなたを救い出す、最高にエキサイティングなツール「Pulumi(プルミ)」の世界にご案内します。
これをマスターすれば、毎日のインフラ・デプロイ作業が劇的に楽になりますよ。さあ、一緒に深淵を覗いてみましょう!
—
なぜ、いま「Pulumi」なのか?
私たちは長年、YAMLという名の「テキストファイル」と格闘してきました。KustomizeやHelmを使って何とか共通化やテンプレート化を図ろうとしても、結局は「文字列の置換」の域を出ません。変数の型チェックもないし、IDEの強力な補完も効かない。デプロイして初めて「あ、スペルミスってた」と気づく絶望感……。
そこでPulumiです。
Pulumiは、TypeScript、Python、Goといった本物のプログラミング言語を使ってクラウドやKubernetesのリソースを定義するIaC(Infrastructure as Code)ツールです。
K8s管理において、Pulumiを使うと何が嬉しいのか?
- 完全な型安全性: Kubernetesの複雑なAPIスキーマがそのままコードの型(TypeScriptのインターフェースなど)になります。IDEがプロパティを完璧に補完してくれます。
- KustomizeやHelmの完全な内包: 既存のHelmチャートをそのまま取り込んでコード側から動的に値を注入したり、Kustomizeのオーバーレイをプログラムのロジック(`if`文や`for`ループなど)でエレガントに代替できます。
- 圧倒的な冪等性(べきとうせい): 「何を変更しようとしているのか」をデプロイ前にプレビュー(`pulumi preview`)で完全に把握できます。
百聞は一見に如かず。実際に手を動かして、その心地よさを体感していきましょう!
—
1. 基礎セットアップ:環境の構築
まずは、手元のマシンにPulumiとKubernetesを操作するための道具を揃えます。今回は多くのエンジニアにとって馴染み深く、型安全性の恩恵を最大限に受けられるTypeScriptをチョイスします。
必要なツールのインストール
以下のツールがインストールされている前提で進めます(MacならHomebrewが一撃です)。
- `pulumi` CLI
- `node` (v18以上推奨) & `npm`
- `kubectl` (ローカルのK8sクラスター、例えばDocker DesktopやMinikube、Kindなどに接続できている状態)
Pulumiのインストール確認
pulumi version
プロジェクトの初期化
適当な作業ディレクトリを作成し、Pulumiプロジェクトを初期化します。
mkdir pulumi-k8s-demo && cd pulumi-k8s-demo
pulumi new kubernetes-typescript
途中でいくつか質問されます(プロジェクト名やスタック名)。デフォルトのままEnter連打でOKです。最後に「Choose a stack」と聞かれたら `dev` と入力してください。
完了すると、ディレクトリに `index.ts` や `package.json` が生成されます。
これが、あなたの新しいKubernetesマニフェスト管理の中枢となります!
—
2. HelloWorld:型安全なK8sリソースの直感的な構築
まずは伝統的な「Nginx」をKubernetes上にデプロイしてみましょう。
`index.ts` をエディタで開き、以下のように書き換えてみてください。
`index.ts`(最初のコード)
import as pulumi from “@pulumi/pulumi”;
import as k8s from “@pulumi/kubernetes”;
// 1. ネームスペースの作成
// 型が効くので、どんなプロパティが指定できるかIDEが教えてくれます
const ns = new k8s.core.v1.Namespace(“demo-ns”, {
metadata: { name: “pulumi-demo” },
});
// 2. Deploymentの作成
const appLabels = { app: “nginx-hello” };
const deployment = new k8s.apps.v1.Deployment(“nginx-deployment”, {
metadata: {
namespace: ns.metadata.name, // ネームスペースの依存関係も自動解決!
labels: appLabels,
},
spec: {
replicas: 2, // レプリカ数をコードで明示
selector: { matchLabels: appLabels },
template: {
metadata: { labels: appLabels },
spec: {
containers: [{
name: “nginx”,
image: “nginx:1.25-alpine”,
ports: [{ containerPort: 80 }],
}],
},
},
},
});
// 3. 外部からアクセスするためのServiceの作成
const service = new k8s.core.v1.Service(“nginx-service”, {
metadata: {
namespace: ns.metadata.name,
},
spec: {
type: “ClusterIP”,
selector: appLabels,
ports: [{ port: 80, targetPort: 80 }],
},
}, { dependsOn: [deployment] }); // 明示的な依存関係の制御も自由自在
// 4. デプロイ後にサービス名をエクスポート
export const namespaceName = ns.metadata.name;
export const serviceName = service.metadata.name;
デプロイを実行する!
さあ、このコードをK8sクラスターに適用(Up)しましょう。以下のコマンドを実行します。
pulumi up
ターミナルに、これから作成されるリソースの一覧(プレビュー)が表示されます。確認画面で `yes` を選ぶと、魔法のようにリソースがクラスターに適用されます。
View Live: https://app.pulumi.com/… (ローカルバックエンドの場合はCLIに出力されます)
Type Name Status
+ pulumi:pulumi:stack pulumi-k8s-demo-dev created
+ – kubernetes:core/v1:Namespace demo-ns created
+ – kubernetes:apps/v1:Deployment nginx-deployment created
+ – kubernetes:core/v1:Service nginx-service created
Outputs:
namespaceName: “pulumi-demo”
serviceName: “nginx-service”
Resources:
+ 4 created
Duration: 3s
どうですか?YAMLファイルを1行も書かずに、TypeScriptの恩恵を受けながら安全にK8sリソースをデプロイできました。これがPulumiの基本であり、強力なアプローチです。
—
3. 発展:Helmチャートの統合とKustomize代替アプローチ
ここからが本番です。「既存の巨大なHelmチャートを使いたい」「Kustomizeのオーバーレイをスマートに書きたい」という現場の切実な悩みをPulumiで解決していきましょう。
A. HelmチャートをPulumiで優しくラップする
例えば、広く使われている `ingress-nginx` のようなHelmチャートを、Pulumiから直接デプロイしてみます。さらに、環境ごとに動的に設定(values)を注入してみましょう。
`index.ts` に以下のコードを追加します。
import as helm from “@pulumi/kubernetes/helm”;
// Helmチャートを用いて NGINX Ingress Controller をデプロイ
const ingressController = new helm.v3.Chart(“nginx-ingress”, {
chart: “ingress-nginx”,
version: “4.8.3”,
fetchOpts: {
repo: “https://kubernetes.github.io/ingress-nginx”,
},
namespace: ns.metadata.name,
// valuesをTypeScriptのオブジェクトとして型安全に渡せる!
values: {
controller: {
replicaCount: 1,
service: {
type: “ClusterIP”, // 簡易的にClusterIPに設定
},
},
},
}, { provider: k8sProvider }); // 必要に応じてプロバイダーを指定
Helmの複雑な `values.yaml` を別途管理する必要はありません。TypeScriptのオブジェクトとして記述できるため、他のリソースの値を参照して動的に設定を書き換えることも容易です。
B. Kustomizeの代替:プログラムによるリソースの動的生成
「ステージ(staging / production)ごとに環境変数の値を切り替えたい、かつリソースの一部を動的に増やしたい」という要件を考えてみます。Kustomizeでは複雑なパッチが必要になるケースですが、Pulumiなら普通のプログラミング言語のロジックで一撃です。
// ステージに応じた設定を定義(TypeScriptの型安全なオブジェクト)
const env = pulumi.getStack(); // “dev”, “staging”, “prod” などが取得できる
const isProd = env === “prod”;
const replicaCount = isProd ? 5 : 1;
const logLevel = isProd ? “error” : “debug”;
// このように変数や三項演算子、ループを使ってマニフェストを自在に組み立てられる
// これが「Kustomize代替アプローチ」の真髄です
YAMLの構文制限に縛られず、プログラミング言語の表現力をそのままインフラ定義に持ち込める。これが、私たちの開発体験を劇的に変える理由です。
—
片付け(リソースの削除)
検証が終わったら、作ったリソースは綺麗に削除しておきましょう。Pulumiなら以下のコマンドひとつで、依存関係を考慮した安全な逆順削除を行ってくれます。
pulumi destroy
「本当に消していいですか?」と聞かれるので `yes` を入力すれば、クラスターは元のクリーンな状態に戻ります。ゴミが残る心配もありません。
—
おわりに:明日のインフラ運用のために
今回は、Pulumiを用いたKubernetesマニフェストのネイティブ管理と、Helmチャート統合の基本をハンズオン形式で解説しました。
- YAMLの構文エラーやタイポから解放される安心感
- TypeScriptの型補完による圧倒的な開発スピード
- HelmやKustomizeの複雑なワークアラウンドをプログラミング言語でスマートに解決するアプローチ
これらを自分の手札に加えるだけで、あなたのインフラエンジニアとしての戦闘力は跳ね上がります。
「毎日のインフラ作業が面倒くさい」と感じたら、それは自動化のチャンスです。ぜひ明日の開発から、Pulumiの世界に飛び込んでみてください。あなたの手元で素晴らしいインフラが構築されることを応援しています!