午前2時14分の本番環境トラブル
午前2時14分。枕元のスマートフォンがPagerDutyのアラートで震え、胃を殴られたような感覚で目が覚めました。本番環境のAPIが503エラーを吐き出し続けています。モニターの眩しいブルーライトに目を細めながらログを確認すると、原因が判明しました。ハードコードされたステージング環境のDB URI(db-staging.internal:5432)が、手動の ‘kubectl apply’ によって本番クラスターに紛れ込んでいたのです。
その後の45分間、12層ものネスト構造を持つ悪夢のようなHelmチャートを解きほぐす羽目になりました。YAMLファイルのインデントがたった一つ欠けていただけで、設定の乖離(ドリフト)が発生していたのです。その夜、一つの結論に達しました。このプロジェクトにHelmは過剰(オーバーキル)だったのです。私たちが必要としていたのは、重厚なパッケージマネージャーではなく、テンプレート地獄に陥ることなく環境固有の変更を管理できる、クリーンでネイティブな手法でした。それが、スタックをKustomizeへ移行したきっかけです。
クイックスタート:5分でデプロイまで完了
Kustomizeは既存の kubectl バイナリに組み込まれています。追加のダウンロードは不要です。管理すべきサーバー側の ‘Tiller’ も存在しません。動作原理は極めてシンプルです。共通要素である Base と、環境ごとの差異である Overlays を定義するだけです。
以下は、私が新しいマイクロサービスを作成する際に使用する標準的なディレクトリ構造です:
myapp/
├── base/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── kustomization.yaml
└── overlays/
├── dev/
│ └── kustomization.yaml
└── prod/
└── kustomization.yaml
まず、最小限の base/deployment.yaml を作成します:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
replicas: 1
selector:
matchLabels:
app: web-server
template:
metadata:
labels:
app: web-server
spec:
containers:
- name: main
image: my-repo/api:latest
ports:
- containerPort: 8080
次に、base/kustomization.yaml でリソースファイルを登録します:
resources:
- deployment.yaml
- service.yaml
本番環境を10個のレプリカにスケールさせるには、overlays/prod/kustomization.yaml にその差分だけを記述します:
resources:
- ../../base
patches:
- target:
kind: Deployment
name: web-server
patch: |-
- op: replace
path: /spec/replicas
value: 10
デプロイはコマンド一行で完了します:
kubectl apply -k overlays/prod/
複雑な変数の注入も、’if-else’ ロジックも不要です。実行時にパッチが適用される、生のKubernetes YAMLがあるだけです。
オーバーレイパターンの仕組み
実際の現場において、このパターンはインフラを巨大な一つのスクリプトではなく、レイヤー(層)として扱うよう促します。Helmでは、1,500行にも及ぶ values.yaml のどこで特定の変数が使われているか、ドライランを実行するまで分からないことがよくあります。Kustomizeはそれを変えてくれます。
Strategic Merge Patching を使用することで、KustomizeはBaseの設定に変更を融合させます。これはセキュリティ面でも大きなメリットがあります。Baseのデプロイで厳格なリソース制限やセキュリティヘッダーを固定しておき、開発者はオーバーレイでの環境変数管理だけに集中させることができるからです。
「古い設定」の罠:ConfigMap Generator
ConfigMapを更新したのにPodの再起動を忘れてしまうのは、よくある初心者ミスです。Kustomizeは configMapGenerator でこの問題を解決します。ConfigMapの名前に一意の10文字のハッシュ(api-config-f98h2g6 など)を付与するのです。値を変更するたびに新しい名前が生成されるため、Deploymentのローリングアップデートが自動的にトリガーされます。
# overlays/dev/kustomization.yaml
configMapGenerator:
- name: api-config
literals:
- DB_URL=jdbc:mysql://dev-db:3306/mydb
- DEBUG=true
基本を超えて:実戦的なパッチ適用
単純な値の置換だけでは限界があります。トラフィックが増えるにつれ、CI/CDパイプライン経由でイメージタグを差し替えたり、グローバルなメタデータを注入したりする必要が出てきます。Kustomizeは、YAMLを精密に操作するメスのような役割を果たす組み込みトランスフォーマーでこれらを処理します。
イメージタグの自動化
ビルドのたびに手動でYAMLを編集するのは、疲弊の元です。CIツール(GitHub ActionsやGitLab CI)を使用して、ビルドプロセス中にイメージタグを動的に更新できます:
# overlays/prod/kustomization.yaml 内
images:
- name: my-repo/api
newName: my-repo/api
newTag: v1.4.2
メタデータの標準化
私は常に commonLabels フィールドを含めるようにしています。これにより、kubectl get pods -l env=prod を使ったログのフィルタリングやデバッグが一瞬で完了します。また、オーバーレイ全体にネームスペースを強制することで、’dev’ のリソースが誤って ‘production-apps’ に紛れ込むのを防ぐこともできます。
namespace: production-apps
commonLabels:
variant: prod
team: backend
現場で学んだ教訓
午前2時にトラブル対応をしているとき、曖昧さは最大の敵です。クラスターを健全に保つために私が守っている4つのルールを紹介します:
- Baseは最小限に: Baseはアプリケーションの「骨組み」であるべきです。環境固有のデフォルト値をBaseに含めず、’default’ や ‘dev’ のオーバーレイに持たせるようにしましょう。
- 適用前にプレビューを: まず
kubectl kustomize overlays/prodを実行してください。マージされた最終的なYAMLがターミナルに出力されます。これで壊れた設定をデプロイせずに済んだことが何度もあります。 - シークレットの保護:
secretGeneratorを使う場合、ソースファイルを絶対にGitにコミットしてはいけません。機密情報にはHashiCorp VaultやSealedSecretsのような外部のシークレットマネージャーを使用しましょう。 - 階層を深くしすぎない: 原則としてBaseとOverlaysの2層にとどめましょう。もし
base -> staging -> prod-region-1 -> prod-region-1-zone-aのような構成になっているなら、それはオーバーエンジニアリングです。
結論として、KustomizeはHelmの完全な代替品ではありません。Prometheusのような複雑なサードパーティ製ツールの配布には、依然としてHelmが最適です。しかし、自社のマイクロサービスにおいては、Kustomizeはテンプレートには真似できない明快さを提供してくれます。画面上のYAMLがそのままクラスターで動いていると確信できることは、エンジニアの安眠に繋がります。

