【実務・中級編】PulumiでKubernetesマニフェストを安全に管理:Helmチャートとの統合とKustomize代替アプローチ – インフラ構成管理(IaC)活用バイブル

模倣を超えた先へ:PulumiでKubernetesとHelmを完全にコード化する極意

テックリードの君なら、もうYAML地獄にうんざりしているはずだ。
数千行のYAML、Kustomizeの複雑なオーバーレイ、Helmの巨大で不透明な`values.yaml`、そして`helm template`の出力結果を`kubectl apply`した瞬間に走る冷や汗。

「Kubernetesのマニフェスト管理は、なぜこうも美しくないのか?」

そのフラストレーションに対する、インフラエンジニアリングの回答がPulumiだ。
TypeScriptやPythonといった汎用言語の型システム、ループ、条件分岐、そしてテスト駆動開発(TDD)の力を使って、KubernetesリソースやHelmチャートを完全に掌中に収める。

今回は、単なる「Pulumiの入門」ではない。チーム全体の開発スピードを劇的に引き上げ、本番障害をゼロにするための実戦的・極限の知見を伝授しよう。

—

1. 開発スピードを異次元にする:プロの環境構築

まずは、コードを書く手をとめさせないための「神環境」を構築する。ここでの数秒の最適化が、一日の終わりに数時間の余裕を生む。

必須プラグイン・拡張機能(VS Code / Neovim)

1. Pulumi Extension (VS Code)

  • コード補完、スタックの切り替え、リソースのプレビューをIDE上で完結させる。

2. Error Lens

  • 型エラーやPulumiのConfig未設定をインラインで即座に赤字表示。コンパイルエラーを未然に防ぐ。

3. YAML / Kubernetes (Red Hat)

  • Pulumi内からCRD(Custom Resource Definition)や外部YAMLを読み込む際のスキーマ補完に必須。

チームで共有すべきプロジェクト設定ルール

Pulumiプロジェクトを複数人で安全に回すため、`Pulumi.yaml` と `.gitignore` は以下のように厳格に定義せよ。暗号化された秘密情報は絶対にGitにコミットさせない。

Pulumi.yaml
name: k8s-production-core
runtime: nodejs
description: Production-grade Kubernetes infrastructure managed by TypeScript
config:
pulumi:tags:
value:
project: core-infrastructure
owner: platform-team

.gitignore の必須項目
node_modules/
bin/
.pulumi/
スタックごとの設定ファイルで秘密情報を含むものは除外(暗号化された .yaml は許可)
Pulumi..yaml
!Pulumi.dev.yaml
!Pulumi.prod.yaml

—

2. ネイティブマニフェスト管理:Kustomizeを捨て、型安全を手に入れろ

Kustomizeのパッチ当てに消耗するのはもう終わりだ。PulumiのKubernetes Providerを使えば、すべてのK8sリソースを「型安全なオブジェクト」として生成・検証できる。

以下のTypeScriptコードは、DeploymentとServiceを安全に定義し、動的な値(環境変数やメタデータ)を完全に制御する実用的なコードだ。

import as k8s from “@pulumi/kubernetes”;
import as pulumi from “@pulumi/pulumi”;

// 1. 設定値の読み込み(型安全なConfig)
const config = new pulumi.Config();
const replicaCount = config.getNumber(“replicaCount”) || 3;
const imageTag = config.require(“imageTag”); // デプロイ時に必ず指定させる

// 2. ネームスペースの作成(リソースの論理分離)
const appNamespace = new k8s.core.v1.Namespace(“app-ns”, {
metadata: {
name: “core-workload”,
labels: { “managed-by”: “pulumi” }
},
});

// 3. アプリケーション用Deploymentの構築
const appLabels = { app: “payment-api” };
const deployment = new k8s.apps.v1.Deployment(“payment-api-dep”, {
metadata: {
namespace: appNamespace.metadata.name,
labels: appLabels,
},
spec: {
replicas: replicaCount,
selector: { matchLabels: appLabels },
template: {
metadata: { labels: appLabels },
spec: {
containers: [{
name: “api”,
image: `ghcr.io/my-org/payment-api:${imageTag}`,
ports: [{ containerPort: 8080 }],
// 型安全に環境変数を注入
env: [
{ name: “NODE_ENV”, value: “production” },
{ name: “DB_HOST”, value: config.require(“dbHost”) },
],
resources: {
limits: { cpu: “500m”, memory: “512Mi” },
requests: { cpu: “100m”, memory: “128Mi” },
},
}],
},
},
},
});

// 4. 外部公開用Service
const service = new k8s.core.v1.Service(“payment-api-svc”, {
metadata: {
namespace: appNamespace.metadata.name,
},
spec: {
type: “ClusterIP”,
ports: [{ port: 80, targetPort: 8080 }],
selector: appLabels,
},
});

// エクスポートして外部スタックから参照可能にする
export const serviceName = service.metadata.name;
export const namespaceName = appNamespace.metadata.name;

このアプローチの圧倒的な優位性

  • TypeScriptの補完: `k8s.apps.v1.Deployment` を記述する際、IDEがAPI仕様に基づいたプロパティを完全網羅して提案してくれるため、Kubernetesの公式ドキュメントを往復する時間がゼロになる。
  • 動的計算: 「ステージ環境ならReplicaは1、本番なら最低3、かつ時間帯でオートスケーリングのベースを変更する」といったロジックを、三項演算子やループでエレガントに記述できる。

—

3. Helmチャートの高度な統合・ラップ手法

「既存のサードパーティ製Helmチャート(Cert-Manager, ArgoCDなど)を導入したいが、デフォルトのvaluesだけでは要件を満たせない、あるいは安全性を担保したい」
そんなときは、Pulumiの `helm.v3.Chart` リソースを使い、コード側から値を動的に注入・ラップする。

以下のコードは、Prometheusスタックのチャートをデプロイしつつ、特定の値動的注入と、デプロイ後のリソースへのアクセスを両立させる例だ。

import as k8s from “@pulumi/kubernetes”;

// 監視用ネームスペース
const monitoringNs = new k8s.core.v1.Namespace(“monitoring”, {
metadata: { name: “monitoring” }
});

// Helmチャートのデプロイとラップ
const prometheusStack = new k8s.helm.v3.Chart(“kube-prometheus-stack”, {
chart: “kube-prometheus-stack”,
version: “55.5.0”,
namespace: monitoringNs.metadata.name,
fetchOpts: {
repo: “https://prometheus-community.github.io/helm-charts”,
},
// valuesをコードで完全に制御・型付けする
values: {
grafana: {
enabled: true,
adminPassword: “super-secure-initial-password”, // 実際にはPulumi Config Secretを使用すべき
service: {
type: “ClusterIP”,
},
},
prometheus: {
prometheusSpec: {
retention: “10d”,
storageSpec: {
volumeClaimTemplate: {
spec: {
storageClassName: “gp3”,
resources: {
requests: {
storage: “50Gi”,
},
},
},
},
},
},
},
},
}, {
// 依存関係の明示的指定(ネームスペースが確実に作成された後にHelmを実行)
dependsOn: [monitoringNs],
});

// Helmでデプロイされた特定のリソースのプロパティを抽出して利用する
export const grafanaServicePort = prometheusStack.getResourceProperty(
“v1/Service”,
“monitoring/kube-prometheus-stack-grafana”,
“spec.ports[0].port”
);

プロの技:Helmチャートの「Transformations」

Pulumiの真骨頂は、サードパーティ製Helmチャートが吐き出すマニフェストに対して、コード側から強制的に変更(Transform)を加えられることだ。例えば、「すべてのリソースに特定のセキュリティラベルを強制付与したい」場合、以下のように記述できる。

const securedChart = new k8s.helm.v3.Chart(“secure-app”, {
chart: “some-third-party-chart”,
// … 設定 …
}, {
// チャートから生成される全リソースに対するフック処理
transformations: [
(args) => {
if (args.props.metadata) {
args.props.metadata.labels = {
…args.props.metadata.labels,
“security.policy/compliant”: “true”,
};
}
},
],
});

この機能により、修正不可能なサードパーティ製チャートのセキュリティ要件や命名規則の不統一を、コードベースで美しく強制統制できる。

—

4. チーム開発で失敗しないための実践的ベストプラクティス

1. プレビュー(`pulumi preview`)をCI/CDのゲートに強制せよ
Pull Requestが作成された段階で必ず `pulumi preview` を実行し、「意図しないリソースの削除や変更」が発生していないかを差分(Diff)でレビューする文化を作れ。これが本番障害を防ぐ最強の盾となる。
2. スタックの分離は「環境」ではなく「ライフサイクル単位」で
`dev`, `staging`, `prod` の分離はもちろんのこと、「共通基盤(Network/Cluster)」と「個別ワークロード(App)」でPulumiプロジェクトを分割し、依存関係を明確にせよ。すべてを1つの巨大なプロジェクトに詰め込むのはアンチパターンだ。
3. シークレット管理の徹底
生パスワードやAPIトークンをコードや設定ファイルに直書きしてはならない。必ず `pulumi config set –secret` を使用し、KMSやVault等のシークレットプロバイダと連携させろ。

—

結び:インフラを「コードの芸術」へ昇華させろ

YAMLやKustomizeに縛られていた時代は終わった。
Pulumiを使ったKubernetes管理は、単なる効率化ではない。それは、インフラストラクチャを純然たるソフトウェア開発の領域へと引き上げ、テスト可能性、型安全性、そして圧倒的な表現力を手に入れることだ。

今日から君のチームのワークフローにこのアプローチを導入し、手作業や場当たり的なマニフェスト修正を過去のものにしてほしい。妥協なきエンジニアリングの成果を見せてやろう。

タイトルとURLをコピーしました