Claude Codeでkustomizeのoverlayを組む手順 — build出力をdiffで確かめる
baseとoverlayの構成をClaude Codeに書かせ、kubectl kustomizeの出力をdiffで読ませて意図した差分だけが出ているかを確かめる手順です。
kustomizeのoverlayは、書いた時点では何が変わるのか見えにくい設定です。kustomization.yamlを読んでも最終的なYAMLは分かりません。Claude Codeに組ませるときは、ファイルを書かせたあとにkubectl kustomizeでbuild出力を作らせ、環境間の差分をdiffで読ませる流れにすると、意図しない変更を機械的に拾えます。
kustomizeのbaseとoverlayは何を分けるのか
kustomizeは、Kubernetesの設定をkustomization.yamlで組み替えるツールです。機能は3つに分かれます。他のソースからのリソース生成、全リソース共通のフィールド設定、リソース集合の合成とカスタマイズです。kubectlも1.14からkustomizationファイルでの管理に対応しており、kubectl kustomize <ディレクトリ>で結果を表示し、kubectl apply -k <ディレクトリ>で適用します。
baseは、kustomization.yamlを持つディレクトリです。リソースとそれに付随するカスタマイズをまとめます。overlayは、他のkustomizationディレクトリをbaseとして参照するディレクトリです。baseはoverlayの存在を知りません。1つのbaseを複数のoverlayから使えます。
この非対称が、Claude Codeに任せるときの要点になります。環境ごとの違いはoverlay側だけに書き、baseは触らない。この線を守れているかは、後述のdiffで確認できます。素のマニフェストの書き方はClaude CodeでKubernetesマニフェストを書く手順、テンプレート方式のHelmチャートとは役割が異なります。
最小のbase・dev・prod構成を作る
kubernetes.ioのBases and Overlaysの例は、base/にDeploymentとServiceを置き、dev/とprod/がresources: - ../baseで参照する形です。違いはnamePrefixだけです。ここから一歩進め、prodでレプリカ数とイメージタグを変える構成にします。
app/
├── base/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── kustomization.yaml
└── overlays/
├── dev/kustomization.yaml
└── prod/
├── kustomization.yaml
└── increase_replicas.yamlbaseのkustomization.yamlは、ドキュメントの例どおりリソースを列挙するだけです。
resources:
- deployment.yaml
- service.yamldeployment.yamlとservice.yamlは、ドキュメントの例と同じmy-nginxです。以降のdiffは、この中身を前提にしています。
# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80# base/service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
run: my-nginxdevのoverlayは、baseを参照して接頭辞とnamespaceを足すだけです。namespaceは全リソースのmetadata.namespaceに設定されます。環境ごとに分けたい値は、baseではなくoverlayに置きます。app-devとapp-prodは、この記事で仮に決めた名前です。
# overlays/dev/kustomization.yaml
resources:
- ../../base
namePrefix: dev-
namespace: app-devprodのoverlayは、baseを参照し、接頭辞・namespace・イメージ・パッチを重ねます。imagesは、パッチを書かずにコンテナイメージの名前やタグを差し替えるフィールドです。patchesは、1つのことだけをする小さなパッチを重ねる使い方が推奨されています。
# overlays/prod/kustomization.yaml
resources:
- ../../base
namePrefix: prod-
namespace: app-prod
images:
- name: nginx
newTag: "1.4.0"
patches:
- path: increase_replicas.yaml# overlays/prod/increase_replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 3パッチの対象は、パッチファイル内のgroup、version、kind、nameで決まります。レプリカ数の変更だけのパッチにしておけば、メモリ上限を変えるパッチを別ファイルで足せます。
CLAUDE.mdに書くoverlayの規約
Claude Codeは、CLAUDE.mdをセッションの冒頭で読みます。検証できる程度に具体的な指示が勧められています。「マニフェストを正しく書く」ではなく、ディレクトリとコマンドを名指しします。次の断片は、この記事の構成に合わせた一例です。
## kustomize
- 構成は app/base と app/overlays/{dev,prod}。環境差分は overlays 側にだけ書く
- base/ のファイルは、変更を頼まれたときを除いて編集しない
- 変更後は必ず `kubectl kustomize app/overlays/dev` と `.../prod` を実行する
- 完了報告には base と各overlayのbuild出力のdiffを付ける
- `kubectl apply -k` と `kubectl delete -k` は、指示があるまで実行しないCLAUDE.mdは、強制される設定ではなく文脈として扱います。長いほど遵守率が落ちるため、1ファイル200行未満が目安です。禁止事項を確実に止めたいなら、次節の権限設定と併用します。
kubectl kustomizeを許可リストに入れる
kubectl kustomizeはクラスタに書き込まない表示コマンドですが、権限の面では通常のBashコマンドです。組み込みの読み取り専用コマンドとして挙がっているのはls、cat、grep、diff、読み取り専用のgitなどで、kubectlは例示にありません。何も設定しないと、build確認のたびに承認を求められます。
ルールは、サブコマンドの後ろに*を置いて書きます。Bash(git log *)がgit log系だけを許す、というのと同じ形です。
{
"permissions": {
"allow": ["Bash(kubectl kustomize *)"],
"deny": ["Bash(kubectl apply *)", "Bash(kubectl delete *)"]
}
}denyはallowより優先され、広い拒否ルールは狭い許可ルールでも覆せません。-k付きの適用を止めたい場合も、kubectl apply系を拒否側に置くのが確実です。
複合コマンドは、区切られた各サブコマンドが個別に判定されます。&&や;でつなぐ書き方では、片方のコマンドだけが許可リストにあっても全体は通りません。build出力のファイル保存はリダイレクトで行い、1コマンドずつ実行させると、承認の挙動が読みやすくなります。リダイレクトには書き込み先の確認が加わります。
build出力のdiffで差分を確かめさせる
依頼文には、「書いたら終わり」ではなく「確かめるところまで」を含めます。
overlays/prod を追加して。prodだけレプリカ数を3、イメージタグを1.4.0にする。
kubectl kustomize で base と dev と prod の出力をそれぞれ作り、
dev と prod のdiffを見せて。意図した差分以外が混じっていたら、原因を直してから報告して。build出力をファイルに保存してからdiffを取る、という手順を指示に含めると、次のようなコマンドが並びます。
mkdir -p .build
kubectl kustomize app/overlays/dev > .build/dev.yaml
kubectl kustomize app/overlays/prod > .build/prod.yaml
diff -u .build/dev.yaml .build/prod.yamlドキュメントの挙動から組み立てた想定では、差分は次のような形になるはずです(実行結果ではなく、Deploymentの該当行だけを抜いた期待出力の例です。dev-my-nginxはbaseのmy-nginxにdevのnamePrefix: dev-が付いた名前、replicas: 2はbaseの値です)。
- name: dev-my-nginx
+ name: prod-my-nginx
- namespace: app-dev
+ namespace: app-prod
- replicas: 2
+ replicas: 3
- - image: nginx
+ - image: nginx:1.4.0読み方の基準は単純です。名前の接頭辞、namespace、レプリカ数、イメージタグの4種類しか出ていなければ、依頼した変更だけが入っています。ここにラベルやannotationsの行、リソース上限の行が混じっていたら、overlayが想定外の項目を書き換えています。差分の行数が少ないほど、レビューは速くなります。
比較の軸は、dev対prodだけではありません。baseをそのままbuildした出力と各overlayを比べると、overlayが足した差分だけが見えます。コマンドは次の2行です。
kubectl kustomize app/base > .build/base.yaml
diff -u .build/base.yaml .build/prod.yaml変更を加える前後の同じoverlayを比べれば、今回の編集が環境に与える影響も分かります。Claude Code上で未コミットの変更を並べて見たい場合は、Claude Codeのdiffパネルが使えます。ただしこれはソースの差分で、build後のYAMLの差分とは別物です。
差分に出る項目と、見落としやすいノイズ
overlayで使う主なフィールドが、build出力のどこに現れるかを表にします。diffで何が出れば正常かの目安になります。
| フィールド | build出力での現れ方 | diffで見る点 |
|---|---|---|
namePrefix / nameSuffix | build出力での現れ方全リソースのmetadata.nameに付く | diffで見る点接頭辞のずれ、参照先の名前 |
images | build出力での現れ方コンテナのimageが差し替わる | diffで見る点名前とタグが意図どおりか |
patches | build出力での現れ方対象リソースのフィールドが変わる | diffで見る点対象のDeploymentだけが変わっているか |
namespace | build出力での現れ方全リソースのmetadata.namespaceに付く | diffで見る点環境ごとのnamespaceが正しいか、取り違えていないか |
commonAnnotations | build出力での現れ方全リソースのmetadata.annotationsとPodテンプレートのannotationsに付く | diffで見る点想定外のannotationが増えていないか |
labels(includeSelectors: true) | build出力での現れ方ラベルとselector.matchLabelsが変わる | diffで見る点セレクタまで書き換わっていないか |
configMapGenerator | build出力での現れ方ConfigMap名にハッシュが付く | diffで見る点内容を変えたときだけ名前が変わるか |
ノイズになりやすいのは、次の4点です。
- ジェネレータのハッシュ: ConfigMapとSecretの名前には内容のハッシュが付きます。中身を変えると名前も変わり、それを参照するDeploymentの行にもdiffが出ます。
generatorOptionsのdisableNameSuffixHash: trueで付与を止められます。 - ラベルのセレクタ:
labelsのincludeSelectors: trueを付けると、ドキュメントの出力例ではselector.matchLabelsと、Podテンプレートのラベルも書き換わります。ラベル追加のつもりが、セレクタごと変わっていないか確認します。セレクタを書き換えずにラベルだけ足すなら、includeSelectorsを付けません。 - commonAnnotations: ドキュメントの出力例では、
commonAnnotationsに書いたoncallPager: 800-555-1212が、Deploymentのmetadata.annotationsと、Podテンプレートのannotationsの両方に現れます。1行足したつもりが2か所に出るので、diffの行数が想定より増えます。Podテンプレート側が変わるとPodの作り直しにつながるため、annotationをoverlayに足すときは、この2か所の変化を意識して読みます。 - 名前の参照:
namePrefixを足すと、コマンド引数などにベタ書きしたService名は古いままになります。こうした値は、ベタ書きせずreplacementsで注入する方法が示されています。diffでService名の行が残っていたら、このパターンを疑います。
namespaceとnamePrefixは、どちらも全リソースに一括で付く点が共通です。ドキュメントの例ではnamespace: my-namespaceとnamePrefix: dev-を同じkustomization.yamlに書き、metadata.namespaceとmetadata.nameの両方に反映されています。環境ごとにnamespaceを分けるなら、devとprodのoverlayそれぞれにnamespace:を1行置きます。baseに書くと全環境が同じnamespaceになるため、overlay側に置いたかどうかも、baseのbuild出力と見比べれば確認できます。
適用前の確認と、適用後の確認
クラスタが手元にあるなら、kubectl diff -k <ディレクトリ>が使えます。マニフェストを適用した場合にクラスタがどうなるかと、現在の状態を比べるコマンドです。ここまでのdiffは「overlay同士」の比較でしたが、こちらは「適用後のクラスタ」との比較で、見る相手が違います。検証の使い分けは、Kubernetesマニフェストの記事にあるdry-runとdiffの節が詳しい内容です。
適用後の同期をArgoCDに任せる場合は、kubectl apply -kを人もClaude Codeも打たない運用になります。その場合の置き方はArgoCD Applicationマニフェストの記事で扱っています。
まとめ
kustomizeのoverlayをClaude Codeに任せるときの流れは、4つに絞れます。環境差分はoverlayだけに書くことをCLAUDE.mdで固定する。kubectl kustomizeを許可し、applyとdeleteは拒否する。build出力をファイルに保存してdiffを取らせる。diffに出た行が依頼した変更と一致するか、ハッシュ・セレクタ・名前参照のノイズを切り分けながら確かめる。
kustomization.yamlは、読んだだけでは結果が見えません。出力のdiffという検証手段を最初の依頼に含めておくと、Claude Codeの変更は人間のレビューを通しやすい大きさに収まります。