Upgrade to Pro — share decks privately, control downloads, hide ads and more …

20,000+行のmanifestをリファクタリングして分かったKustomizeの美しきアー...

hhiroshell
November 04, 2021

20,000+行のmanifestをリファクタリングして分かったKustomizeの美しきアーキテクチャと拡張性 / Kustomize deep dive

#CNDT2021 で Kustomize の話をしたときの資料です。

hhiroshell

November 04, 2021
Tweet

More Decks by hhiroshell

Other Decks in Technology

Transcript

  1. 宣伝 • 感染対策をしっかりやって遊舎⼯房に⾏きましょう 2 遊舎⼯房さんの店舗はこちら→ ↓現在の@hhiroshellのキーボードたち #crkbd ⾃⼰紹介 @hhiroshell 早川

    博 (はやかわ ひろし) • Cloud Nativeなインフラを開発 するエンジニア。 Yahoo Japan Corporation 所属 • エンジニアコミュニティ 「Cloud Native Developers JP」 オーガナイザー • Developers Summit 2018 Japan Container Days 12.18 CloudNative Days Tokyo 2019 / 2020 登壇 • ⾃作キーボード沼 BMEK
  2. kustomize build を実⾏したときに起きること ⼊⼒となるリソースに対して、複数の加⼯を施した結果を得る 9 入力 出力 KRMリソース KRMリソース kustomization.yaml

    リソースの追加・加工 ? kustomization.yaml リソースの追加・加工 ? • 複数のKRM(Kubernetes Resource Model)リソースを⼊⼒として、リ ソースの追加、加⼯(kustomization)が⾏われた結果を出⼒する – 追加、加⼯の内容はkustomization.yamlに宣⾔的に記述 – 複数のkustomization.yamlを書いて繋げられる kustomization.yaml リソースの追加・加工 ?
  3. kustomization.yamlで指定できるkustomization処理の例 • resources – kustomization処理の⼊⼒ • KRMリソースのパス • 他のkustomization.yamlの出⼒ •

    configMapGenerator – 指定した内容のConfigMapを追加 • commonAnnotations – 共通のannotationを付加 • patches – KRM形式で記述したパッチの適⽤ 1 apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - deployment.yaml - service.yaml configMapGenerator: - name: example-properties files: - example.properties commonAnnotations: hashtag: “#CNDT2021” patches: - patch.yaml
  4. kustomization処理の連鎖的な実⾏ • ResMap: – KRMリソースの集合体。Generator / Transformerによってリソースが追加、加 ⼯される • Generator

    / Transformer: – ResMapを加⼯する処理 複数のGenerator / Transformerによってmanifestが加⼯(kustomization)されていく 12 KRMリソース ResMap ResMap ResMap Generator/Transformer 入力 出力 KRMリソース Generator/Transformer Generator/Transformer
  5. Generator / Transformerの実装を⾒てみる - 1/2 • GeneratorPlugin / TransformerPlugin インタフェースの定義

    1 type GeneratorPlugin interface { Generator Configurable } type TransformerPlugin interface { Transformer Configurable } type Transformer interface { Transform(m ResMap) error } type Generator interface { Generate() (ResMap, error) } type Configurable interface { Config(h *PluginHelpers, config []byte) error } resmap.go
  6. Generator / Transformerの実装を⾒てみる - 2/2 • AnnotationsTransformerPlugin(commonAttionationsに当たる部分の実装) 1 func (p

    *AnnotationsTransformerPlugin) Config(_ *resmap.PluginHelpers, c []byte) (err error) { p.Annotations = nil p.FieldSpecs = nil return yaml.Unmarshal(c, p) } func (p *AnnotationsTransformerPlugin) Transform(m resmap.ResMap) error { if len(p.Annotations) == 0 { return nil } return m.ApplyFilter(annotations.Filter{ Annotations: p.Annotations, FsSlice: p.FieldSpecs, }) } AnnotationsTransformer.go
  7. 【おまけ】おなじみのKustoization機能も実は • ビルトインの機能もkustomzation.yaml上でtrasfomersフィールドに書 くことができる – 普段使っているのはtransformerに特別に与えられた記法 1 apiVersion: kustomize.config.k8s.io/v1beta1 kind:

    Kustomization transformers: - |- apiVersion: builtin kind: PrefixSuffixTransformer metadata: name: myFancyNamePrefixer prefix: bob- fieldSpecs: - path: metadata/name apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namePrefix: bob-
  8. kustomization.yamlとKustomizationディレクトリ • kustomization.yamlは⼀つのディレクトリに⼀つ配置 • リソース追加・加⼯のためのファイル(e.g., patch, resources)を同ディレクトリ に置く 複数のKustomization処理を束ねる単位 16

    Generator/Transformer kustomization.yaml deployment.yaml kustomizationディレクトリ service.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - deployment.yaml - service.yaml commonAnnotations: hashtag: “#cndt2021”
  9. Kustomizationディレクトリの連結 • resourcesに他のKustomizationディレクトリを指定して連結 • 前段のKustomization処理の出⼒が次のKustomization処理の⼊⼒になる Kustomizeディレクトリは他のKustomize処理の結果を⼊⼒にできる 17 Generator/Transformer kustomization.yaml deployment.yaml

    kustomizationディレクトリ service.yaml apiVersion: kustomize.(snip).v1beta1 kind: Kustomization resources: - path/to/another/dir Generator/Transformer kustomization.yaml deployment.yaml kustomizationディレクトリ service.yaml 参照 kustomization
  10. Kustomizationディレクトリの連結 • 多段に連結してkustomizationの流れ(ストリーム)を作る • それぞれのkustomizationディレクトリが意味が意味のあるまとまりになる – e.g., プロダクションクラスタに適⽤する差分のセット ⼀種のストリーム処理でありつつ意味のあるまとまりに分けることもできる 18

    参照 kustomization.yaml deployment.yaml service.yaml Generator/Transformer kustomization.yaml Generator/Transformer kustomization.yaml Generator/Transformer kustomization.yaml deployment.yaml service.yaml Generator/Transformer kustomization.yaml Generator/Transformer kustomization
  11. ストリームの開始点 • ストリームの開始点の選択肢 – ローカルに置いたmanifestファイル (kustomization.yamlのresourcesのファイル) – 他のリポジトリにあるmanifestファイルやkustomizationディレクトリ、Helm Chart ⼊⼒となるKRMリソースを決めるところ。複数の種類が指定できる

    21 kustomization.yaml deployment.yaml kustomizationディレクトリ service.yaml resourcesでローカル ファイルを指定 kustomization.yaml kustomizationディレクトリ some-input.yaml kustomization.yaml some-input-chart resourcesでファイル、 ディレクトリを指定 HelmInfrationGeneratorで チャートを指定 • ローカル • リモート
  12. ストリームの分岐と合流 • 分岐: prod, stgなど環境ごとの差分を作るときに、環境ごとに分岐する • 合流: 共通リソースを複数箇所に取り⼊れて利⽤する Kustomizationディレクトリを⾊々につなぎ合わせて⽬的のmanifestを得る 22

    kustomization.yaml deployment.yaml 入力(アプリ) service.yaml resources kustomization.yaml kustomization.yaml kustomization.yaml rbac.yaml 入力(RBAC系) resources kustomization.yaml kustomization.yaml アプリのProduction用の差分 アプリのDevelopment用の差分 分 岐 合 流 合 流 Production用の最終出力 Development用の最終出力
  13. 2 Kustomizationディレクトリの意味を意識してディレクトリのパスを決めていく ストリームができたらディレクトリ配置を考えてみる kustomization.yaml deployment.yaml service.yaml resources kustomization.yaml rbac.yaml resources

    base/ app/ rbac/ prod/ kustomization.yaml kustomization.yaml app/ rbac/ kustomization.yaml dev/ kustomization.yaml kustomization.yaml app/ rbac/ kustomization.yaml prod用パイプライン Kubernetesクラスタ (production) dev用パイプライン Kubernetesクラスタ (development)
  14. ディレクトリ設計におけるその他のコツ • 個々のKustomizeディレクトリの意味を意識する – 途中のKustomizeディレクトリでもkustomize buildして意味があるmanifestを出 ⼒されるようにする – きれいに意味が分かれていると、分岐の枝を⾜して追加の拡張を⼊れるなど、 再利⽤性、メンテナンス性がよくなる

    • 何も変更しないKustomizeディレクトリがあっても良い – resourcesを書いてストリームをつなぐだけのディレクトリ – 「Development環境向けのmanifest(何も変わってないけど)」ことを意味す るKustomizeディレクトリ 2
  15. Kustomizeプラグインとは • BuiltinのGenerator/Transformerではできない加⼯を⾏える – e.g., ConfigMap内にインラインで書いた設定ファイルを加⼯したい • kustomize buildだけでmanifestを作れるというUXを壊さない –

    独⾃スクリプトを被せてしまうとmanifestを出⼒するときに追加の⼿順が必要 • 加⼯処理のロジックに集中できる – ⼊出⼒や呼び出しはKustomizeの任せ、お作法に合わせてロジックを書けば良い Kustomizeプラグイン ≒ Generator/Transformerの⾃作 27 kustomization.yaml すごい 処理
  16. Kustomizeプラグインの実装⽅法 • Containerized KRM Functions – KRMの加⼯、追加処理をGoで実装してコンテナ化しておく • Exec KRM

    Functions – KRMの加⼯、追加処理を任意の実⾏バイナリに実装する 2 【参考】以下はDEPRECATEDとなっているため本セッションでは詳しくは触れません • Legacy exec plugins • Exec KRM Functionsと同様だが、実⾏バイナリの呼び出し時の⼊⼒フォーマットに違いがある。 シェルスクリプトとの相性が良く、発表者的には⼀番これが好き • Legacy Go plugins • GeneratorPlugin/TransformerPluginインターフェースを実装したロジックを、Go⾔語のプラグイン機 構を利⽤して組み込む。ビルドが壊れやすくて⾟いのなんのって
  17. Kustomizeプラグインの作っていく流れ 1. プラグインの呼び出し⽅法を定義した設定ファイル(KRM)を⽤意 – 呼び出すプラグインやそれに与えるパラメータを指定 2. kustomization.yaml内で上の設定ファイルを指定 3. プラグインの実装を⽤意 –

    実装⽅法に合わせて以下を実施 2 • Containerized KRM Functions – kyamlライブラリを使ってリソースの追 加・加⼯処理を実装 – Dockerfile出⼒などコンテナ化の⽀援も kyamlライブラリがしてくれる • Exec KRM Functions – 任意⾔語でリソースの追加・加⼯を実装 – リソースの⼊出⼒は標準⼊出⼒を利⽤
  18. ConfigMap内のファイルを加⼯するプラグインの例 - 1/4 • まずkustomization.yamlのgeneratorまたはtransformer配下にプラグインの呼 び出し⽅法を定義したファイルを指定 3 apiVersion: kustomize.config.k8s.io/v1beta1 kind:

    Kustomization configMapGenerator: - name: example-properties files: - example.properties transformers: - replace-foo-with-bar.yaml kustomization.yaml foo=@@foo@@ example.properties kustomizeはインラインのファイルを加⼯する機能がないのでプラグインでこれをやってみる
  19. ConfigMap内のファイルを加⼯するプラグインの例 - 2/4 • replace-foo-with-bar.yamlで呼び出すプラグインと⼊⼒に与えるパラ メータを指定 – annotationで実⾏バイナリを指定 – spec配下に⼊⼒パラメータを指定

    3 apiVersion: hhiroshell.github.com kind: SedTransformer metadata: name: replace-foo-with-bar annotations: config.Kubernetes.io/function: | exec: path: ../plugins/sedtransformer.sh spec: replacements: foo: bar replace-foo-with-bar.yaml
  20. ConfigMap内のファイルを加⼯するプラグインの例 - 3/4 • プラグインを実装する – Exec KRM Functionsでは⾔語は問わない •

    実⾏可能なファイルができればよい – ⼊出⼒はstdin/outを使う • ⼊⼒は加⼯元のリソースやパラメータを含む 構造化された情報(KRM) – items 以下に加⼯前のリソース – functionConfig.spec以下に⼊⼒パラメータ • 加⼯後のリソースをstdoutに出⼒する 3 apiVersion: config.kubernetes.io/v1 kind: ResourceList functionConfig: apiVersion: hhiroshell.github.com/v1 kind: sedTransformer metadata: name: replace-foo-with-bar spec: replacements foo: bar items: - apiVersion: v1 kind: ConfigMap metadata: name: example-properties-82cbkgc849 data: example.properties: |- foo=@@foo@@ プラグインに渡される入力(ResourceList)の例
  21. ConfigMap内のファイルを加⼯するプラグインの例 - 4/4 • プラグインの実装を書く 3 #!/bin/bash # 標準入力からResourceListを読み込む。itemsに加工前のリソース、.functionConfig.specにパラメータが入っている resourceList=$(cat)

    items=$(echo "$resourceList" | yq e '.items' - ) replacements=$(echo "$resourceList" | yq e '.functionConfig.spec.replacements' - ) # リソース毎にループして、sedを実行していく for i in `seq 0 $(expr $(echo "$items" | yq e ". | length" - ) - 1)`; do item=$(echo "$items" | yq e ".[$i]" - ) for key in $(echo "$replacements" | yq e ". | keys" - | yq e ".[]" - ); do item=$(echo "$item" | sed -e "s/@@$key@@/"$(echo "$replacements" | yq e ".$key" - )"/g") done # 加工したリソースは標準出力にそのまま吐き出す echo "$item" echo "---" done
  22. kustomize buildを実⾏する • プラグインを利⽤するときはkustomize build実⾏時に所定のフラグを つける必要がある 3 $ kustomize build

    --enable-alpha-plugins --enable-exec ./path/to/kustomize/dir apiVersion: v1 data: example.properties: |- foo=bar kind: ConfigMap metadata: name: example-properties-82cbkgc849 Exec KRM Functions以外のプラグインを使う場合の⼿順については、公式ドキュメントを参照 してください。 • https://kubectl.docs.kubernetes.io/guides/extending_kustomize/ ※ Exec KRM Functionsの場合のフラグ
  23. 【参考】Containerized KRM FunctionsとExec KRM Functions • 複雑なロジックを組んだり、外部システム連携など⾼度な機能を使 うならContainerized KRM Functions

    • ⼿軽にKustomizeの不⾜を補うならExec KRM Functions w/ Shell Script 3 種別 実装⽅法 Pros Cons Containerized KRM Functions KRMの加⼯、追加処理 をGoで実装してコンテ ナ化しておく • Go⾔語 + kyamlライブラリ により、複雑なロジックや 外部システム連携を記述し やすい • プラグイン⾃体のビルドが必 須 • CD環境でkustomizeを実⾏する ときDocker in Dockerが必要 Exec KRM Functions KRMの加⼯、追加処理 を任意の実⾏バイナリ に実装する • 任意の⾔語で実装できる • Shell Scriptを使うなどして、 幅広い環境で動作するプラ グインが作れる • ビルド不要で動作するプラ グインが作れる • リソースの⼊出⼒に標準⼊出 ⼒を利⽤するので、リソース の取り回しの実装に慣れが要 る
  24. おすすめのプラグインの使い所 • インラインドキュメントの加⼯ – ⽂字列置換 – ⾏挿⼊ • yamlのアンカー、エイリアスの展開 →

    2021年9⽉末に標準で対応可能に • Helm Chartの取り込み → 2021年春にビルトインのプラグインに追加 • nameやnamespaceが違うが中⾝がほぼ同じリソースを複数作る – Listリソース + アンカー/エイリアス(後述)で対応できないならアリ これでかゆいところにも⼿が届く… 36
  25. 【おまけ】Listリソース + アンカー/エイリアスの利⽤例 • xxxListという名前で複数のリソースを配 列に含めて1枚のyamlにできる • 1枚のyamlになるので、アンカー/エイリ アスを使ってDRYに書ける •

    Kustomizeの⼊⼒としてアンカー/エイリ アス⼊りのmanifestを使うには、 kustomize v4.4.0+ が必要 同内容のリソースを複数作るときに特に有効 37 apiVersion: v1 kind: PodList items: - apiVersion: v1 kind: Pod metadata: name: busybox-00 spec: &spec containers: - name: busybox image: busybox command: ["/bin/sh", "-c"] args: ["while true; do echo '#CNDT2021' && sleep 1; done"] - apiVersion: v1 kind: Pod metadata: name: busybox-01 spec: *spec
  26. Kustomizeプラグインを使う上での注意点 • manifestの開発体験を損なわないように注意 – kustomize buildするためにプラグインをビルドしたり実⾏環境を⽤意したりす るのは、できればやらないほうが良い • ビルド環境や実⾏環境を⽤意した環境でしか動かない。環境によってkustomize buildに失敗

    したり、動作が異なったりしかねない • CDに組み込むのが⼤変になる – Exec KRM Functions + Shell Scriptで実現できないかをまず考える • プラグインのメンテナンス性も意識しよう – プラグインが「秘伝のスクリプト」化しないように注意 やりすぎ注意…! 38
  27. リファクタリングするmanifest - 1/2 • 管理対象のKubernetesクラスタ群 – 合計16クラスタ、8種のバリエーション(4環境、2クラスタ種別) – 約20コンポーネントを各クラスタにデプロイ –

    バリエーション毎にデプロイするコンポーネントや各コンポーネントの設定 が異なる 4 Development Staging Production-1 Production-2 x1 x3 x1 x3 x1 x3 x1 x3 Type A Type B 全クラスタのmanifestを1リポジトリで管理
  28. リファクタリング前の課題(ほんの⼀例) • ディレクトリの切り⽅が統⼀されてない – それだけでは意味をなさないリソースが単独で置かれていたり – 依存し合わないリソースが同じディレクトリにあったり • 共通部分と差分をうまく切り出せてない –

    baseが複数ディレクトリに分かれている...! – overlayの⽅に同じことを全部書いてる • 独⾃の拡張 – Kustomizeの機能不⾜(と思われていた)を補うための複数の独⾃スクリプト • e.g., ⽂字列の置き換え、内容がほぼ同じだが名前が異なるリソースを複数作成 ...etc 地道な改善では追いつかないと判断し、抜本的に作り変えることに 42
  29. 1本のストリームを1コンポーネントに対応付ける • “1コンポーネント”の例 – Ingressコントローラー、ログ収集エージェント、アプリAなど – そのコンポーネントを動かすためのすべてのリソース(workload, discovery, rbac...)⼀式を1ストリームで扱う •

    1本のストリームでkustomize buildしたときに意味のある単位の manifestを出⼒できるようにする – 1本のストリームで意味をなし、かつ他のストリームと依存しない • コンポーネント単位のスコープで⽇々改善できるようになる 4
  30. component-x/ ※ でき上がったもの 4 component-y/ component-z/ base/ Type-A/ Type-B/ component-x/

    component-y/ component-z/ dev/ Type-A/ component-α/ component-z’/ Type-B/ staging/ dev/Type A クラスタ kustomization.yaml dev/Type A パイプライン kustomization.yaml dev/Type B パイプライン dev/Type B クラスタ 一部コンポーネント は外部リポジトリ から取得 component-α/ Type-A/ Type A,Bで共通のものは BからAを参照 component-z’/ 同時にapplyするため Typeごとに集約 環境ごとに分岐
  31. kubectl diffを使ってクラスタとの差分を確認しながら作業する • 少し⼤胆な構成変更もdiffをチェックすれば安⼼してできる – ストリーム単位(=コンポーネント単位で)で kustomize build + kubectl

    diffしながらmanifestを編集していく • diffを⾒やすくするツールも活⽤するとなお良い – kubectl neat diff: diffの表⽰からmanagedFieldsを除外してくれる – colordiff: ⾊をつけてdiffを表⽰してくれる 4 $ kustomize build ./path/to/kustomize/dir | kubectl diff - | colordiff
  32. 4 kubectl neat diff + colordiff を利⽤したdiffの出⼒例 diff -uN /var/folders/7m/r1f0b69d3cjdj_hmz2t5dgchyl05r1/T/LIVE-020772465/v1.Pod.quantum-sandbox.busybox-00

    /var/folders/7m/r1f0b69d3cjdj_hmz2t5dgchyl05r1/T/MERGED-814475548/v1.Pod.quantum-sandbox.busybox-00 --- /var/folders/7m/r1f0b69d3cjdj_hmz2t5dgchyl05r1/T/LIVE-020772465/v1.Pod.quantum-sandbox.busybox-00 2021-10-21 19:49:35.000000000 +0900 +++ /var/folders/7m/r1f0b69d3cjdj_hmz2t5dgchyl05r1/T/MERGED-814475548/v1.Pod.quantum-sandbox.busybox-00 2021-10-21 19:49:35.000000000 +0900 @@ -14,7 +14,7 @@ command: - /bin/sh - -c - image: busybox:stable + image: busybox:1.33.1 name: busybox resources: limits: diff -uN /var/folders/7m/r1f0b69d3cjdj_hmz2t5dgchyl05r1/T/LIVE-020772465/v1.Pod.quantum-sandbox.busybox-01 /var/folders/7m/r1f0b69d3cjdj_hmz2t5dgchyl05r1/T/MERGED-814475548/v1.Pod.quantum-sandbox.busybox-01 --- /var/folders/7m/r1f0b69d3cjdj_hmz2t5dgchyl05r1/T/LIVE-020772465/v1.Pod.quantum-sandbox.busybox-01 2021-10-21 ...(snip)...
  33. nameとnamespaceは変えない • これを変えるとそのリソースは新規作成となるのでdiffのチェックが効か なくなる • Namespace, Nameの変更は全体の構成を変え終わってから、スコープを 絞ってやる 5 ビルトインの機能でできないことはKustomizeプラグインで

    • 独⾃の加⼯をKustomizeプラグイン以外の⼿段で作らない • Exec KRM Functionsをシェルスクリプトで書くのがおすすめ – ⼤抵の環境でプラグインのビルドなどの⼿間なく kustomize build ⼀発でmanifestが出 ⼒できる – メンテナンスが属⼈化しにくい
  34. yaml 58%減達成 • ⽬に⾒えてライン数を削減 • Pythonスクリプト依存が解消してスッキリ 5 language files code

    YAML 290 20,194 Markdown 12 1,307 Jinja 6 499 Python 2 236 Shell Script 1 59 XML 3 22 pip requirements 1 6 language files code YAML 264 8,437 Markdown 6 1,164 Jinja 0 0 Python 0 0 Shell Script 0 0 XML 3 22 pip requirements 0 0 Properties 21 791 Before After
  35. いかがでしたか? • こんなことを話しました… – Kustomizeはkustomizatinディレクトリの連結と Generator/Transformerを軸に設計されている – ストリームの流れを設計してからディレクトリ構 成を考えよう –

    Kustomizeプラグインをうまく活⽤しよう – 実際にリファクタリングする上でのコツ x 6 Kustomizeと仲良くなれそうでしょうか 53 yamlエンジニア Kustomize Kustomize is マジ God
  36. 【参考⽂献】 • Kustomizeの公式ドキュメント – The Kustomization File • https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/ –

    Extending Kustomize • https://kubectl.docs.kubernetes.io/guides/extending_kustomize/ – Containerized KRM Functions • https://kubectl.docs.kubernetes.io/guides/extending_kustomize/containerized_krm_functions/ – Exec KRM functions • https://kubectl.docs.kubernetes.io/guides/extending_kustomize/exec_krm_functions/ • GitHubリポジトリ – The Kubernetes Resource Model (KRM) • https://github.com/kubernetes/community/blob/master/contributors/design-proposals/architecture/resource- management.md – Kustomize • https://github.com/kubernetes-sigs/kustomize 5