記事一覧はこちら
背景・モチベーション
以前に記事を書いて、kustomizeでbuildする時にCRDでpatchesStrategicMerge
が利用できない悲しみを文字に起こしました。
(紛らわしいので当該記事は非公開にしてあります)
ところが、v4.1.0のリリースで可能になったようです!うれしいですね…!ということで早速試してみた記事です。
参考文献
- KEP-2206: OpenAPI Features in Kustomize
- Using a Custom OpenAPI schema
- kustomize handling of CRD inconsistent with k8s handling starting in 1.16
- Server-Side Apply
- kube-openapi/generator/extension.go
どのようにCRDでpatchesStrategicMergeを実現するのか
CRDでのpatchesStrategicMerge
はkustomizeでどのように実現されるのか確認していきます
Kubernetes本体ではv1.16からCRDのapplyがJMPからSMPに変わっている
kubernetesのv1.15までは、CRDをapplyしたときの挙動がkubernetes/kustomizeで足並み揃っていて、下記のような挙動になっていました。
- kubernetesのnativeオブジェクトは
strategicMergePatch(SMP)
を使う - CRDは
jsonMergePatch(JMP)
を使う
例えば、PodTemplate
のコンテナのリストをマージする時などはその違いが顕著です。SMPであれば、各要素のname
をキーとして利用して、同一のキーが存在すれば更新し、無ければ配列に追加するという挙動を取ります。対して、JMPの場合には配列ごと置き換えます。
この状況は、v1.16のServer-Side Apply
のリリースによって変わります。
詳しくは、Merge Strategyを参照いただければと思うのですが、API開発者がList/Map/structのマージ方法をOpenAPIスキーマで定義できるmarker(extension)が追加されました。
ちなみにstrategic merge patch で以前から利用されているx-kubernetes-patch-strategy: merge
とx-kubernetes-patch-merge-key
は、それぞれx-kubernetes-list-type: map
とx-kubernetes-list-map-keys
として解釈されます。
そして、Server-Side Apply
を利用したCRDで定義されたリソース更新では、markerが存在すれば、その定義に基づいてフィールドの更新を行うようになりました。
しかしながら、kustomizeは従来どおりのJsonMergePatch
をCRDのマージで利用する挙動になっていたため、kubernetes v1.16のリリース時からkubernetesとkustomizeの間で挙動の違いが生まれることになってしまいました。
kustomizeでCRDのSMPを行う方法
KubernetesネイティブオブジェクトのSMPに関しては、kustomizeのバイナリにOpenAPIの定義が含まれるので、Merge Strategyはその定義を参照することで確定することができます。対してCRDの定義はバンドルしていません。kustomize自体がどうにかしてCRDの定義を知る必要があります。
そこで、kustomizeはkustomization.yamlファイルにopenapi
フィールドを追加して、そのフィールドを読み込んでスキーマを取得するように機能追加を行いました。openapi
フィールドが指定されていれば指定されたスキーマを読み込み、そうでなければビルトインの定義を利用するような動きをするようになっています。
この機能追加により、CRDを含む全体のスキーマファイルが手元にあれば、より柔軟なマージ戦略を行うことができるようになりました。
ではスキーマの取得が必要になってきます。これはKuberneteのAPIサーバに対して、/openapi/v2
のパスでリクエストすることで可能です。kustomizeはopenapi fetch
というサブコマンドの内部でそれを実行することでスキーマの取得をサポートしています。
流れとしては下記のような流れとなります。
- (CRDのファイルをapplyする)
kubectl openapi fetch
でスキーマのファイルを取得する- kustomizationに
openapi
のフィールドを追加してファイルを指定する
スキーマのfetchの問題点
kubectl openapi fetch
の際の内部処理で/openapi/v2
にリクエストしていると先ほど言及させていただきました(参考)。これはOpenAPIのv2での公開です。
OpenAPI v3で定義されたCRDはv2でサポートされていないフィールドを含んだり、表現できないnullable
を利用したりします(参考)。それらはkubectl v1.13より前のバージョンでは解釈できないため、互換性を守るために公開時に削除されます。
もしかしたら、この挙動がkustomize build
で影響を及ぼす可能性があります。
実際にCRDでpatchesStrategicMergeできるか確かめてみる
自前のGKEクラスタで確認していきます。バージョンは1.18.16
です。
CRDのマニフェストのmergeStrategyを確認する
事前の準備としてCRDのマニフェストのmergeStrategyを確認しておく必要があります。今回はArgoCDのRolloutリソースで確認していきますが、2つのキーの組み合わせで一意に解釈させたい.spec.template.spec.containers.ports
などは既にmarkerが利用されていますが、.spec.template.spec.containers
のarrayはそうなっていません。
今回、自分の方でmarkerを追加して試してみたいと思います。.spec.template.spec.containers
のarrayのみにmarkerを追加いたしました。ちなみにkustomizeの実装を見ていると、x-kubernetes-list-type
を確認せずにx-kubernetes-patch-strategy
のmarkerを確認しに行きます。そのmarkerが見つからないとx-kubernetes-list-map-keys
を確認してくれないみたいなので注意してください(該当コード)。
…本家のServer-Side Apply
でもこうなっているのかな…?後で確認してみようと思います。
CRDのリファレンスを確認すると、x-kubernetes-patch-strategy
が許容するフィールドに存在しないため、schemaをfetchした後に追加してあげる必要があります。辛い…。
確認手順
利用するファイル群はこちらにまとめています。
自前のGKEクラスタで試してみます。
# CRDの登録 $ cd /tmp $ git clone https://github.com/44smkn/kustomize-crd-smp-sample.git $ cd kustomize-crd-smp-sample $ gcloud container clusters get-credentials my-gke-cluster $ kubectl apply -f crds/rollout.yaml --validate=false # kustomizeのインストール $ curl -s "https://raw.githubusercontent.com/\ kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash $ ./kustomize version # v4.1.0以上であることを確認 # スキーマのfetchとキーの挿入 $ ./kustomize openapi fetch | sed -e '1d' > tmp.json $ cat tmp.json | jq '.definitions["io.argoproj.v1alpha1.Rollout"].properties.spec.properties.template.properties.spec.properties.containers |= .+ {"x-kubernetes-patch-strategy": "merge"}' > schema.json # 確認 $ ./kustomize build > actual.yaml $ diff -h actual.yaml expected.yaml
上手く動くところまで確認できましたが、仕様の問題なのか自分のドキュメントの読み込みが甘いのかわかりませんが、まだ実用に持っていくのは難しいような要素を感じました。
細かいところを確認して必要そうならIssue挙げてみようと思います。以上!