Slide 1

Slide 1 text

kustomize を kustomize を いい感じに使う方法 いい感じに使う方法 Yuki Osako Yuki Osako 2023/06/30 2023/06/30

Slide 2

Slide 2 text

Table of Contents Table of Contents kustomize とは ありがちな問題 kustomize の基本 オススメ構成 (案) 加工方法の種類 tips まとめ

Slide 3

Slide 3 text

kustomize とは kustomize とは

Slide 4

Slide 4 text

kustomize とは kustomize とは k8s のマニフェストを加工するツール k8s のリソースはマニフェスト (yaml) として記述される 複数環境がある時にマニフェストを複製すると多重管理になる 共通部分と差分に分けることで、管理しやすく&わかりやすく

Slide 5

Slide 5 text

ありがちな問題 ありがちな問題

Slide 6

Slide 6 text

マニフェストの肥大化 マニフェストの肥大化 $ find . -name '*.yaml' | xargs wc -l | tail -n1 24261 total 環境やリソースが増えて記述が増えるのは仕方ないが… どこに何があるか分かりにくい あちこちに同じ記述がある というケースがある

Slide 7

Slide 7 text

overlay モリモリ overlay モリモリ どれとどれが関連のあるリソースなのかよくわからない 環境を増やす時にリソースの過不足が起きやすい ファイル名を見て中身の見当が付きにくい とりあえず追記 & 差分確認で済んでしまう → 気付いた時には…みたいなことが起きがち

Slide 8

Slide 8 text

その他の観測している課題 その他の観測している課題 リポジトリ内の構造の見通しが悪い 構成の参考にできそうなものがない (各自で試行錯誤し、それぞれ問題を抱えることに) → kustomize への理解を深め、使いやすい構造について考えてみる

Slide 9

Slide 9 text

kustomize の基本 kustomize の基本

Slide 10

Slide 10 text

kustomize でできることイメージ kustomize でできることイメージ

Slide 11

Slide 11 text

Kustomization Kustomization NamePrefix string `json:"namePrefix,omitempty" yaml:"namePrefix,omitempty"` NameSuffix string `json:"nameSuffix,omitempty" yaml:"nameSuffix,omitempty"` Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` CommonLabels map[string]string `json:"commonLabels,omitempty" yaml:"commonLabels,omitempty"` Labels []Label `json:"labels,omitempty" yaml:"labels,omitempty"` CommonAnnotations map[string]string `json:"commonAnnotations,omitempty" yaml:"commonAnnotations,omitempty"` PatchesStrategicMerge []PatchStrategicMerge `json:"patchesStrategicMerge,omitempty" yaml:"patchesStrategicMerge,omitempty"` PatchesJson6902 []Patch `json:"patchesJson6902,omitempty" yaml:"patchesJson6902,omitempty"` Patches []Patch `json:"patches,omitempty" yaml:"patches,omitempty"` Images []Image `json:"images,omitempty" yaml:"images,omitempty"` ImageTags []Image `json:"imageTags,omitempty" yaml:"imageTags,omitempty"` Replacements []ReplacementField `json:"replacements,omitempty" yaml:"replacements,omitempty"` Replicas []Replica `json:"replicas,omitempty" yaml:"replicas,omitempty"` SortOptions *SortOptions `json:"sortOptions,omitempty" yaml:"sortOptions,omitempty"` Resources []string `json:"resources,omitempty" yaml:"resources,omitempty"` Components []string `json:"components,omitempty" yaml:"components,omitempty"` Crds []string `json:"crds,omitempty" yaml:"crds,omitempty"` ConfigMapGenerator []ConfigMapArgs `json:"configMapGenerator,omitempty" yaml:"configMapGenerator,omitempty"` SecretGenerator []SecretArgs `json:"secretGenerator,omitempty" yaml:"secretGenerator,omitempty"` HelmGlobals *HelmGlobals `json:"helmGlobals,omitempty" yaml:"helmGlobals,omitempty"` HelmCharts []HelmChart `json:"helmCharts,omitempty" yaml:"helmCharts,omitempty"` GeneratorOptions *GeneratorOptions `json:"generatorOptions,omitempty" yaml:"generatorOptions,omitempty"` Configurations []string `json:"configurations,omitempty" yaml:"configurations,omitempty"` Generators []string `json:"generators,omitempty" yaml:"generators,omitempty"` Transformers []string `json:"transformers,omitempty" yaml:"transformers,omitempty"` Validators []string `json:"validators,omitempty" yaml:"validators,omitempty"`

Slide 12

Slide 12 text

Kustomization Kustomization k8s リソース、それらの生成・加工についての記述 構成要素は resource, generator, transformer (+ validator) あらゆる生成・加工は という扱い 最も有名な builtin plugin が PatchTransformer patches, patchesStrategicMerge 等は簡易記法 Kustomization は他の Kustomization を継承して使うことができる よくある使い方は、リソースを記述する base と 環境ごとの差分を patch する overlay の層に分けるパターン プラグイン

Slide 13

Slide 13 text

Kustomization Kustomization

Slide 14

Slide 14 text

Kustomization のレイヤイメージ Kustomization のレイヤイメージ ディレクトリを Kustomization の単位 (1 つの層) として扱う # ==> base/kustomization.yaml <== apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - ./deployment.yaml - ./service.yaml # ==> overlay/kustomization.yaml <== apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - ../base/ patches: # 旧 patchesStrategicMerge - path: ./deployment.patch.yaml

Slide 15

Slide 15 text

Component Component 再利用できる Kustomization Kustomization から読み込んで使う base はもともと再利用できるので、overlay を再利用するイメージ 語弊はあるが、patch 等を使い回せると思ってもらえれば 用途の例 複数の overlay で共通の patch を切り出す 複数の base で共通の resource 記述の一部を切り出す オプショナルな従属リソースを base/overlay から切り出す

Slide 16

Slide 16 text

Component Component # ==> xxxx/kustomization.yaml <== apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - ./deployment.yaml components: - ../component/ # ==> component/kustomization.yaml <== apiVersion: kustomize.config.k8s.io/v1alpha1 kind: Component patches: # 旧 patchesStrategicMerge - path: ./deployment.patch.yaml

Slide 17

Slide 17 text

評価順 評価順 なんとなく書いても動くので、あまり気にしてなかったりしがち generator や transformer を活用するには大事 例えば… configMapGenerator の merge と patch はどっちが優先? base, overlay 両方に images を指定したらどっちが適用される?

Slide 18

Slide 18 text

評価順 評価順 Kustomization 内は resources → generators → components → transformers v5.0 以前は generator と component の順番が逆だった v5.3 時点では、順序制御する手段はない resource/component で他 Kustomization 等の指定があれば再帰的に ※ kustomize v5.1 のアップデートに合わせて後日変更しました

Slide 19

Slide 19 text

評価順 評価順 つまり… Kustomization の base 側から順に評価が確定する Component は参照元 Kustomization のタイミングで Kustomization 内は resources → generators → components → transformers

Slide 20

Slide 20 text

評価順の例 0 評価順の例 0 images を base と overlay それぞれで設定した場合 # ==> base/kustomization.yaml <== images: - name: api newName: asia.gcr.io/xxxx-dev/api newTag: 012345678901234567890123456789 # ==> overlay/kustomization.yaml <== images: - name: api newName: asia.gcr.io/xxxx-prod/api newTag: 012345678901234567890123456789

Slide 21

Slide 21 text

評価順の例 1 評価順の例 1 configMapGenerator と patch # ==> base/kustomization.yaml <== configMapGenerator: - name: sample-cm literals: - foo=1 # どれが優先される? # ==> overlay/kustomization.yaml <== resources: - ../base/ patches: - path: ./sample-cm.patch.yaml configMapGenerator: - name: sample-cm behavior: merge literals: - foo=3 # どれが優先される? # ==> overlay/sample-cm.patch.yaml <== apiVersion: v1 kind: ConfigMap metadata: name: sample-cm data: foo: '2' # どれが優先される?

Slide 22

Slide 22 text

評価順の例 2 評価順の例 2 configMapGenerator と commonLabels apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization commonLabels: app: api # これは反映される? configMapGenerator: - name: sample-cm literals: - foo=1 options: disableNameSuffixHash: true

Slide 23

Slide 23 text

評価順の例 3 評価順の例 3 Kustomization で configMapGenerator、Component で commonLabels # ==> ./base/kustomization.yaml <== apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - ./sample-cm1.yaml components: - ../component/ configMapGenerator: - name: sample-cm2 literals: - foo=2 options: disableNameSuffixHash: true # ==> ./base/sample-cm1.yaml <== apiVersion: v1 kind: ConfigMap metadata: name: sample-cm1 data: foo: '1' # ==> ./component/kustomization.yaml <== apiVersion: kustomize.config.k8s.io/v1alpha1 kind: Component commonLabels: some-label-added-by-component: xxxx # 反映される?

Slide 24

Slide 24 text

評価順の例 4 評価順の例 4 Kustomization で commonLabels、Component で replacement # ==> ./base/kustomization.yaml <== apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization commonLabels: app: configMapGenerator: - name: sample-cm # ==> ./overlay/kustomization.yaml <== apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization commonLabels: app: api resources: - ../base/ components: - ../component/ # ==> ./component/kustomization.yaml <== apiVersion: kustomize.config.k8s.io/v1alpha1 kind: Component replacements: - source: kind: ConfigMap fieldPath: metadata.name targets: - select: kind: ConfigMap fieldPaths: - data.name options: create: true - source: kind: ConfigMap fieldPath: metadata.labels.app targets: - select: kind: ConfigMap fieldPaths: - data.app

Slide 25

Slide 25 text

オススメ構成 (案) オススメ構成 (案)

Slide 26

Slide 26 text

base/overlay/component 構成 base/overlay/component 構成 base や 各overlay の中でモジュール分割する ❌ prod/base, ❌ api/base 開発系/本番系で分けるパターンも見かけるが… 開発環境前提になっているなら分けずに直す 放置すると後から手を入れるのは難しい overlay の肥大化回避が目的なら後述の方法で ワークロード毎に分ける案もあるが… component や overlay の共通リソースが 綺麗に配置できない

Slide 27

Slide 27 text

base base モジュール単位で分ける 必要なものを overlay で選択して使うイメージ ワークロードを中心に、周辺リソースを追加 詰め込み過ぎると、overlay に resource が増えた り、$delete する必要が出てくる 依存のないリソースが同居している場合は注意 迷ったら分割! → 複数の base を overlay で参照できる。統合も簡単 (overlays に合わせて bases が自然?)

Slide 28

Slide 28 text

base base モジュール単位で分ける ぱっと見で分かりやすい モジュールが肥大化すると名前もイマイチに。 設計が悪いと命名も悪くなるのは SW と同じ patch が kind 指定 (name 指定無し) にできる → 使い回せる → component 化 プロダクト間の使い回しもしやすい

Slide 29

Slide 29 text

base base base の中でさらに分割するのも手 base の中でディレクトリを切るのはあり 中にさらに Kustomization を作るのもあり e.g. 追加の deployment を生やしやすくする

Slide 30

Slide 30 text

中間レイヤ 中間レイヤ base と overlay の 2 層ではなく、その間にもう 1 層作るパターン 適度にグルーピングできそうなら導入はあり 本番系と開発系でそれぞれ似ていて、たくさんある場合など component でも実現できるので、メリデメは検討 適用する component が多い場合など、目的があれば 採用するなら overlays/dev/_base のような構造? _ を付けているのは表示順と、AppSet で除外しやすいように

Slide 31

Slide 31 text

overlay overlay base 同様にモジュール単位で 基本は参照先の base ごとに Kustomization を分ける 例: overlays/dev/dev1/api で bases/api を参照 CD ツールを使っていれば不都合はない Argo CD だと Application が増えるが、 ApplicationSet で回避可 手動デプロイ運用の場合、1 つ上の Kustomization で まとめても component の使い回しが効くので

Slide 32

Slide 32 text

overlay overlay 共通リソースは各モジュールの外に ingress など、複数モジュールで共有するものは外に overlays/dev/dev1 や overlays/dev/dev1/common 等 最初にモジュールでディレクトリを切らない利点

Slide 33

Slide 33 text

component component bases、overlays と並べるように配置する 用途例 汎用的なサイドカー・init コンテナ (fluentd, exporter, sysctl 等) affinity/anti-affinity label は replacement で (component と transformer の順序注意) Argo Rollouts 等の別種のリソースへの差し替え 再利用しなくても、overlay モリモリ回避策として

Slide 34

Slide 34 text

replacement 等の transformer replacement 等の transformer どれくらい活用していくべきかは、人によって捉え方が変わりそう 「良いコードとは、言語や処理系の機能を十分に活かしたもの?  誰もがぱっと見で読める平易なもの?」みたいなやつ 今のところ個人的には、 「切り出すのはたいへん、ベタ書きに寄せるのは楽。  ひとまず積極的に使う方向でもいいかも?」

Slide 35

Slide 35 text

ファイル名 ファイル名 適切にモジュール分割していれば、kind そのままでも十分 (foo/deployment.yaml, bar/serviceaccount.yaml) resource 以外のファイルは ..yaml が分かりやすい xxxx.patch.yaml, xxxx.replacement.yaml, xxxx.transformer.yaml, xxxx.configuration.yaml など ものによっては resource と patch どちらか判別しにいことも

Slide 36

Slide 36 text

ファイル名 ファイル名 依存の強いものは 1 ファイルにまとめるのも手。アンカーも有用 ただし詰め込み過ぎは NG 可読性の低下だけでなく、kubectl convert しにくくなる等も アンカーは一時期使えなくなっていたけど、v4.4 あたりで復活 kind: List items: - apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress annotations: networking.gke.io/managed-certificates: &cert-name certs spec: rules: - host: &domain1 dev1.example.com http: paths: ... - host: &domain2 dev2.example.com http: paths: ... - apiVersion: networking.gke.io/v1 kind: ManagedCertificate metadata: name: *cert-name spec: domains: - *domain1 - *domain2

Slide 37

Slide 37 text

加工方法の種類 加工方法の種類

Slide 38

Slide 38 text

namePrefix, nameSuffix ({Prefix,Suffix}Transformer) namePrefix, nameSuffix ({Prefix,Suffix}Transformer) 全リソースの名前に prefix, suffix を付ける むやみに使わない (一部ケースを除いて) 必要になるなら設計が悪いかも リソース名がわかりにくくなる & 横断処理しにくくなる 使い道もある 同じ環境 (ns) に同じ base からなる別リソースを作る時 kustomization は小さく AWS/GCP 側に同名の実体が作られるようなリソース (ないかも?) 使うならターゲットを絞るのがオススメ

Slide 39

Slide 39 text

namespace (NamespaceTransformer) namespace (NamespaceTransformer) 全リソースの namespace を指定する 各リソースに指定するより一括付与がオススメ シンプル化 & ns 変更にも強くなる

Slide 40

Slide 40 text

commonLables, labels (LabelTransformer) commonLables, labels (LabelTransformer) 全リソースにラベルを付ける モジュール単位が適切なら、 selector のラベル付けを任せるのも labels は selector への適用を制御可 deployment の selector は immutable → apiVersion: apps/v1 kind: Deployment metadata: name: pint spec: template: spec: containers: - name: pint image: pint ports: - name: pint containerPort: 8081 commonLabels: app: pint apiVersion: apps/v1 kind: Deployment metadata: labels: app: pint # ← name: pint spec: selector: matchLabels: app: pint # ← template: metadata: labels: app: pint # ← spec: containers: - name: pint image: pint ports: - containerPort: 8081 name: pint

Slide 41

Slide 41 text

commonAnnotations (AnnotationsTransformer) commonAnnotations (AnnotationsTransformer) 全リソースに annotation を付ける あまり使うことはない…かも?

Slide 42

Slide 42 text

patches, patchesStrategicMerge, patchesJson6902 patches, patchesStrategicMerge, patchesJson6902 パッチする patchesStrategicMerge, patchesJson6902 は deprecated patches を使う 今までの patch ファイルは (基本的に) そのまま使える # 一番古い形式 (removed) patches: - ./xxxx.yaml # よくある形式 (deprecated, v1で削除) patchesStrategicMerge: # or patchesJson6902 - ./xxxx.yaml # 現在の形式 patches: # patchesStrategicMerge, patchesJson6902 形式ともに可 - path: ./xxxx.yaml # inline で書く時は "patch:"

Slide 43

Slide 43 text

patches, patchesStrategicMerge, patchesJson6902 patches, patchesStrategicMerge, patchesJson6902 マージ方式 (旧 patchesStrategicMerge) 直感的 通常、同名リソースを対象にするが、target 指定も可 component 化する時に重要 意味単位での YAML ドキュメントの分割がおすすめ パス指定方式 (旧 patchesJson6902) やや書きづらい マージパッチが target 指定できるようになり、登場頻度は低下 複数のフィールドを対象にしたい時などに有用 name や kind の書き換えの時はこっちを使う必要あり (余談) コンテナ名はむやみに変えない方が patch を再利用しやすい

Slide 44

Slide 44 text

images (ImageTagTransformer) images (ImageTagTransformer) コンテナイメージ名・タグを一括置換する # kustomization.yaml images: - name: api newName: asia.gcr.io/xxxx/api newTag: 012345678901234567890123456789 kustomization.yaml の更新は kustomize edit image で可能 CR で使うなら configuration を書く # kustomization.yaml configurations: - ./images_cronworkflow.configuration.yaml # images_cronworkflow.configuration.yaml images: - kind: CronWorkflow path: spec/workflowSpec/templates[]/container/image

Slide 45

Slide 45 text

configMapGenerator (ConfigMapGenerator) configMapGenerator (ConfigMapGenerator) ConfigMap を生成する デフォルトでランダムな suffix が付く 参照箇所も合わせて変更される → apply するとワークロードも更新され、pod が入れ替わる suffix を付けないオプション (disableNameSuffixHash) もある ビルド単位の外から参照される場合に envFrom と相性○ <ワークロード名>-env みたいな cm を作って参照させる kustomization.yaml に環境特有の値を寄せられる

Slide 46

Slide 46 text

NameReferenceTransformer NameReferenceTransformer リソース名が加工された時に、参照側も追従して加工する namePrefix/nameSuffix, configMapGenerator など 普段意識する必要はない CR で参照するなら configuration を書く workloadRef じゃない 等 は URL 指定でも Rollout configuration が用意されている場合

Slide 47

Slide 47 text

replacement (ReplacementTransformer) replacement (ReplacementTransformer) 指定リソース・指定フィールドの値を使って、 任意のリソース・フィールドを書き換える 昔は vars/varReference を使ってた ConfigMap に環境固有の値を切り出すとか configMapGenerator で kustomization.yaml に寄せると良さそう

Slide 48

Slide 48 text

replacement (ReplacementTransformer) replacement (ReplacementTransformer) 特定の Kustomization に依存しない書き方が良い component と合わせて使うのも良い メイン側に合わせてパラメータを変えたい時 便利だけど、評価順を理解していないとハマることも component に含めると、transformer の実行前に置換される component から出す or 1 層増やして対応 例: component 内の replacement でラベルを参照しているが、   ラベルは commonLabel で設定している場合

Slide 49

Slide 49 text

helmCharts (HelmChartInflationGenerator) helmCharts (HelmChartInflationGenerator) helm の chart・values からマニフェストを生成する helmfile とだいたい同じ。違いはキー重複を許容しない点くらい 複数 values ファイルの参照 (additionalValuesFiles) は v5 から

Slide 50

Slide 50 text

helmCharts (HelmChartInflationGenerator) helmCharts (HelmChartInflationGenerator) 例: fargate 環境用に patch が必要となるチャート helmCharts: - name: pyroscope repo: https://pyroscope-io.github.io/helm-chart version: 0.2.92 includeCRDs: true releaseName: pyroscope valuesInline: persistence: enabled: true ... ingress: enabled: true className: alb annotations: alb.ingress.kubernetes.io/scheme: internet-facing ... alb.ingress.kubernetes.io/certificate-arn: '' alb.ingress.kubernetes.io/security-groups: '' hosts: [] # - host: pyroscope.example.com # paths: # - path: / # pathType: Prefix resources: - ./pv.yaml patches: - path: ./deployment.patch.yaml - path: ./pvc.patch.yaml

Slide 51

Slide 51 text

helmCharts (HelmChartInflationGenerator) helmCharts (HelmChartInflationGenerator) helmfile でよくない?という話もあるけど… argocd との相性が良い argocd app にも直接書けるが、間に kustomize をかませる 1 層増やすことで手動実行もできるように helmfile が使えないので実質一択 プラグインを書けば使えるが微妙 monitoring unit で使っているのは kustomize 化予定

Slide 52

Slide 52 text

helmCharts (HelmChartInflationGenerator) helmCharts (HelmChartInflationGenerator) (余談) values を kustomize で merge patch できれば更にいいのに… helm/helmfile と同様に複数ファイルで乗り切るか、 kustomize 特有の手法としては KRM function (プラグイン) もある。 巨大 values はどうやってもつらい。kube-prometheus-stack…

Slide 53

Slide 53 text

tips tips

Slide 54

Slide 54 text

kustomization.yaml の自動修正 kustomization.yaml の自動修正 kustomize edit fix で適切な形式に変換できる bases → resources patchesStrategicMerge → patches patchesJson6902 → patches vars → replacement commonLabels → labels

Slide 55

Slide 55 text

Kustomization の apiVersion と kind Kustomization の apiVersion と kind 省略せずにちゃんと書く (とか言いながら、忘れてる時もあった) いちおうなくても動く 頻繁にバージョンが変わるわけじゃない 今のところだいたい kustomization kustomize init で kustomization.yaml を作るのがオススメ # ==> kustomization.yaml <== apiVersion: kustomize.config.k8s.io/v1beta1 # コレ kind: Kustomization # コレ $ kustomize init $ cat kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization

Slide 56

Slide 56 text

CR の merge patch CR の merge patch CR を適切に加工するためにはスキーマを与える必要がある associative list をパッチすると丸ごと上書きされる 等 openapi フィールドに指定 を上書きすることに注意 c.f. assoviative list の patch k8s では kustomize は なフィールド []map[string]string デフォルトのスキーマ リソース/フィールド毎に決まっている merge key を使って結合を行う

Slide 57

Slide 57 text

component 内で環境依存の値を使う component 内で環境依存の値を使う overlay 側で patch するのも手だが、わかりにくくなる Kustomization が Component に依存することになる replacement の使用を検討 適切な値が取れるリソースがない場合、source は configmap を使う component 内の resource として作成し、local-config: true を付け ておく overlay 側の configMapGenerator で上書きする (behavior: merge) generator は transformer (replacement) より先に動作するので…

Slide 58

Slide 58 text

まとめ まとめ

Slide 59

Slide 59 text

まとめ まとめ kustomize のマニフェストは kustomization のレイヤできている kustomization は resource/transformer/generator でできている 評価順がポイント 設計とても大事。よく考えてから書く 加工方法 (transformer, generator) いろいろ おすすめ構成あるよ