Share
Embed
Transcript
59 slides
-
kustomize を kustomize を いい感じに使う方法 いい感じに使う方法 Yuki Osako Yuki Osako
2023/06/30 2023/06/30 -
Table of Contents Table of Contents kustomize とは ありがちな問題 kustomize
の基本 オススメ構成 (案) 加工方法の種類 tips まとめ -
kustomize とは kustomize とは k8s のマニフェストを加工するツール k8s のリソースはマニフェスト (yaml) として記述される
複数環境がある時にマニフェストを複製すると多重管理になる 共通部分と差分に分けることで、管理しやすく&わかりやすく -
マニフェストの肥大化 マニフェストの肥大化 $ find . -name '*.yaml' | xargs wc
-l | tail -n1 24261 total 環境やリソースが増えて記述が増えるのは仕方ないが… どこに何があるか分かりにくい あちこちに同じ記述がある というケースがある -
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"` -
Kustomization Kustomization k8s リソース、それらの生成・加工についての記述 構成要素は resource, generator, transformer (+ validator)
あらゆる生成・加工は という扱い 最も有名な builtin plugin が PatchTransformer patches, patchesStrategicMerge 等は簡易記法 Kustomization は他の Kustomization を継承して使うことができる よくある使い方は、リソースを記述する base と 環境ごとの差分を patch する overlay の層に分けるパターン プラグイン -
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 -
Component Component 再利用できる Kustomization Kustomization から読み込んで使う base はもともと再利用できるので、overlay を再利用するイメージ 語弊はあるが、patch
等を使い回せると思ってもらえれば 用途の例 複数の overlay で共通の patch を切り出す 複数の base で共通の resource 記述の一部を切り出す オプショナルな従属リソースを base/overlay から切り出す -
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 -
評価順 評価順 なんとなく書いても動くので、あまり気にしてなかったりしがち generator や transformer を活用するには大事 例えば… configMapGenerator の
merge と patch はどっちが優先? base, overlay 両方に images を指定したらどっちが適用される? -
評価順 評価順 Kustomization 内は resources → generators → components →
transformers v5.0 以前は generator と component の順番が逆だった v5.3 時点では、順序制御する手段はない resource/component で他 Kustomization 等の指定があれば再帰的に ※ kustomize v5.1 のアップデートに合わせて後日変更しました -
評価順 評価順 つまり… Kustomization の base 側から順に評価が確定する Component は参照元 Kustomization
のタイミングで Kustomization 内は resources → generators → components → transformers -
評価順の例 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 -
評価順の例 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' # どれが優先される? -
評価順の例 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 -
評価順の例 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 # 反映される? -
評価順の例 4 評価順の例 4 Kustomization で commonLabels、Component で replacement #
==> ./base/kustomization.yaml <== apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization commonLabels: app: <!! patch me !!> 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 -
base/overlay/component 構成 base/overlay/component 構成 base や 各overlay の中でモジュール分割する ❌ prod/base,
❌ api/base 開発系/本番系で分けるパターンも見かけるが… 開発環境前提になっているなら分けずに直す 放置すると後から手を入れるのは難しい overlay の肥大化回避が目的なら後述の方法で ワークロード毎に分ける案もあるが… component や overlay の共通リソースが 綺麗に配置できない -
base base モジュール単位で分ける 必要なものを overlay で選択して使うイメージ ワークロードを中心に、周辺リソースを追加 詰め込み過ぎると、overlay に resource
が増えた り、$delete する必要が出てくる 依存のないリソースが同居している場合は注意 迷ったら分割! → 複数の base を overlay で参照できる。統合も簡単 (overlays に合わせて bases が自然?) -
base base モジュール単位で分ける ぱっと見で分かりやすい モジュールが肥大化すると名前もイマイチに。 設計が悪いと命名も悪くなるのは SW と同じ patch が
kind 指定 (name 指定無し) にできる → 使い回せる → component 化 プロダクト間の使い回しもしやすい -
base base base の中でさらに分割するのも手 base の中でディレクトリを切るのはあり 中にさらに Kustomization を作るのもあり e.g.
追加の deployment を生やしやすくする -
中間レイヤ 中間レイヤ base と overlay の 2 層ではなく、その間にもう 1 層作るパターン
適度にグルーピングできそうなら導入はあり 本番系と開発系でそれぞれ似ていて、たくさんある場合など component でも実現できるので、メリデメは検討 適用する component が多い場合など、目的があれば 採用するなら overlays/dev/_base のような構造? _ を付けているのは表示順と、AppSet で除外しやすいように -
overlay overlay base 同様にモジュール単位で 基本は参照先の base ごとに Kustomization を分ける 例:
overlays/dev/dev1/api で bases/api を参照 CD ツールを使っていれば不都合はない Argo CD だと Application が増えるが、 ApplicationSet で回避可 手動デプロイ運用の場合、1 つ上の Kustomization で まとめても component の使い回しが効くので -
component component bases、overlays と並べるように配置する 用途例 汎用的なサイドカー・init コンテナ (fluentd, exporter, sysctl
等) affinity/anti-affinity label は replacement で (component と transformer の順序注意) Argo Rollouts 等の別種のリソースへの差し替え 再利用しなくても、overlay モリモリ回避策として -
replacement 等の transformer replacement 等の transformer どれくらい活用していくべきかは、人によって捉え方が変わりそう 「良いコードとは、言語や処理系の機能を十分に活かしたもの? 誰もがぱっと見で読める平易なもの?」みたいなやつ 今のところ個人的には、
「切り出すのはたいへん、ベタ書きに寄せるのは楽。 ひとまず積極的に使う方向でもいいかも?」 -
ファイル名 ファイル名 適切にモジュール分割していれば、kind そのままでも十分 (foo/deployment.yaml, bar/serviceaccount.yaml) resource 以外のファイルは <name>.<type>.yaml が分かりやすい
xxxx.patch.yaml, xxxx.replacement.yaml, xxxx.transformer.yaml, xxxx.configuration.yaml など ものによっては resource と patch どちらか判別しにいことも -
ファイル名 ファイル名 依存の強いものは 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 -
namePrefix, nameSuffix ({Prefix,Suffix}Transformer) namePrefix, nameSuffix ({Prefix,Suffix}Transformer) 全リソースの名前に prefix, suffix を付ける
むやみに使わない (一部ケースを除いて) 必要になるなら設計が悪いかも リソース名がわかりにくくなる & 横断処理しにくくなる 使い道もある 同じ環境 (ns) に同じ base からなる別リソースを作る時 kustomization は小さく AWS/GCP 側に同名の実体が作られるようなリソース (ないかも?) 使うならターゲットを絞るのがオススメ -
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 -
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:" -
patches, patchesStrategicMerge, patchesJson6902 patches, patchesStrategicMerge, patchesJson6902 マージ方式 (旧 patchesStrategicMerge) 直感的
通常、同名リソースを対象にするが、target 指定も可 component 化する時に重要 意味単位での YAML ドキュメントの分割がおすすめ パス指定方式 (旧 patchesJson6902) やや書きづらい マージパッチが target 指定できるようになり、登場頻度は低下 複数のフィールドを対象にしたい時などに有用 name や kind の書き換えの時はこっちを使う必要あり (余談) コンテナ名はむやみに変えない方が patch を再利用しやすい -
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 -
configMapGenerator (ConfigMapGenerator) configMapGenerator (ConfigMapGenerator) ConfigMap を生成する デフォルトでランダムな suffix が付く 参照箇所も合わせて変更される
→ apply するとワークロードも更新され、pod が入れ替わる suffix を付けないオプション (disableNameSuffixHash) もある ビルド単位の外から参照される場合に envFrom と相性◦ <ワークロード名>-env みたいな cm を作って参照させる kustomization.yaml に環境特有の値を寄せられる -
NameReferenceTransformer NameReferenceTransformer リソース名が加工された時に、参照側も追従して加工する namePrefix/nameSuffix, configMapGenerator など 普段意識する必要はない CR で参照するなら configuration
を書く workloadRef じゃない 等 は URL 指定でも Rollout configuration が用意されている場合 -
replacement (ReplacementTransformer) replacement (ReplacementTransformer) 指定リソース・指定フィールドの値を使って、 任意のリソース・フィールドを書き換える 昔は vars/varReference を使ってた ConfigMap
に環境固有の値を切り出すとか configMapGenerator で kustomization.yaml に寄せると良さそう -
replacement (ReplacementTransformer) replacement (ReplacementTransformer) 特定の Kustomization に依存しない書き方が良い component と合わせて使うのも良い メイン側に合わせてパラメータを変えたい時
便利だけど、評価順を理解していないとハマることも component に含めると、transformer の実行前に置換される component から出す or 1 層増やして対応 例: component 内の replacement でラベルを参照しているが、 ラベルは commonLabel で設定している場合 -
helmCharts (HelmChartInflationGenerator) helmCharts (HelmChartInflationGenerator) helm の chart・values からマニフェストを生成する helmfile とだいたい同じ。違いはキー重複を許容しない点くらい
複数 values ファイルの参照 (additionalValuesFiles) は v5 から -
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: '<!! arn:aws:acm:ap-northeast-1:xxxxxxxx:certificate/xxxxxxxx !!>' alb.ingress.kubernetes.io/security-groups: '<!! sg-xxxxxxxx, sg-xxxxxxxx !!>' hosts: [] # - host: pyroscope.example.com # paths: # - path: / # pathType: Prefix resources: - ./pv.yaml patches: - path: ./deployment.patch.yaml - path: ./pvc.patch.yaml -
helmCharts (HelmChartInflationGenerator) helmCharts (HelmChartInflationGenerator) helmfile でよくない?という話もあるけど… argocd との相性が良い argocd app
にも直接書けるが、間に kustomize をかませる 1 層増やすことで手動実行もできるように helmfile が使えないので実質一択 プラグインを書けば使えるが微妙 monitoring unit で使っているのは kustomize 化予定 -
helmCharts (HelmChartInflationGenerator) helmCharts (HelmChartInflationGenerator) (余談) values を kustomize で merge
patch できれば更にいいのに… helm/helmfile と同様に複数ファイルで乗り切るか、 kustomize 特有の手法としては KRM function (プラグイン) もある。 巨大 values はどうやってもつらい。kube-prometheus-stack… -
kustomization.yaml の自動修正 kustomization.yaml の自動修正 kustomize edit fix で適切な形式に変換できる bases →
resources patchesStrategicMerge → patches patchesJson6902 → patches vars → replacement commonLabels → labels -
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 -
CR の merge patch CR の merge patch CR を適切に加工するためにはスキーマを与える必要がある
associative list をパッチすると丸ごと上書きされる 等 openapi フィールドに指定 を上書きすることに注意 c.f. assoviative list の patch k8s では kustomize は なフィールド []map[string]string デフォルトのスキーマ リソース/フィールド毎に決まっている merge key を使って結合を行う -
component 内で環境依存の値を使う component 内で環境依存の値を使う overlay 側で patch するのも手だが、わかりにくくなる Kustomization が
Component に依存することになる replacement の使用を検討 適切な値が取れるリソースがない場合、source は configmap を使う component 内の resource として作成し、local-config: true を付け ておく overlay 側の configMapGenerator で上書きする (behavior: merge) generator は transformer (replacement) より先に動作するので… -
まとめ まとめ kustomize のマニフェストは kustomization のレイヤできている kustomization は resource/transformer/generator でできている
評価順がポイント 設計とても大事。よく考えてから書く 加工方法 (transformer, generator) いろいろ おすすめ構成あるよ