1クール続けるブログ

とりあえず1クール続けるエンジニアの備忘録

kustomizeはCRDのpatchesStrategicMergeに対応していなくて辛い話(2021/1現在)

記事一覧はこちら

CRDでkustomize使いたい…

ということがたまーに出てきますよね(圧)!
基本的に、CRDを利用する局面ってHelmを使ってインストールすることが多いと思います。その場合は、value.yamlとの差分を上書きして利用すれば環境差分だけを抽出して別々のファイルで宣言することができます。例えば、MySQL OperatorやPrometheus Operatorを導入するときがそうですよね。
ですが、ArgoCDをデプロイに利用するとなると、RolloutAnalysisTemplateといったCRDをkustomizeで構成したくなります。Helmやksonnetなどもサポートされていますが、kustomizeがシンプルかつ柔軟という気がしていて僕は好きです。

kustomizeはCRDのSMPに対応していない

してないんですよ!!!!!!!
GitHubのIssueをななめ読みしているだけなのでしてるかもしれない。もしそうなら、すみません!
SMPはStrategic Merge Patchesの略で、githubのIssueとはこのように略されていることが多いように思えます。いわゆるkubectl patchでのデフォルトのtypeで利用されるもので、Json Merge Patchと違い、常にリストをまるごと置き換えるのではなく、patchMergeKeyが指定されているリストに関しては、キーがかぶらなければ追加するというマージ方法を取ります。
ただ、CRDの場合には、SMPが利用できないため柔軟なマージを行うことができません。

SMP対応依頼のIssue発見

ぐぬぬ…」と思いつつGitHubのIssueを見ていると、光明を見つけた(ように思われました)。

github.com

Kubernetesのv1.16でSSA(Server Side Apply)がベータリリースされました(v1.16のリリースノード)。このタイミングで下記の対応も入っています。

Server-side apply will now use the openapi provided in the CRD validation field to help figure out how to correctly merge objects and update ownership. (#77354, @jennybuckley)

Server Side Applyマニフェストのフィールドごとに所有者の情報をもたせる機能で、マニフェストをapplyするときに予期せぬ変更を検知することができます(ざっくり)。
Issueを発行した方いわく、このServer Side Applyでの対応により「k8sはv1.16からkubectl apply/patchがJMPではなくSMPになるというのです。このことから、kustomizeとk8sの動作の差分が出てしまうため使用者の誤解を招く可能性がある点について指摘し、kustomizeもCRDのSMPに対応するようにと提案しています。

v1.16以降のkubectl patchの挙動

ですが、katacodaのk8s playgroundでv1.18のクラスタに対して、CRDに対してkubectl patchしようとすると下記のようにエラーが出ます。
また、KubernetesCRDのドキュメントページにも、strategic-merge-patchはサポートされないとあります。

controlplane $ kubectl patch servicemonitor example-app -p "$(cat patch.yaml)"
Error from server (UnsupportedMediaType): the body of the request was in an unknown format - accepted media types include: application/json-patch+json, application/merge-patch+json, application/apply-patch+yaml

kubectl patchはサポートされていないことが分かりました。では、Server-side Applyはどうでしょうか?

Server-side Apply時の挙動

まず、k8sのnative objectであるDeployment.spec.template.spec.containers[]のマージが可能かをやってみます。結論から言うと、当たり前ですが可能でした。

controlplane $ wget https://raw.githubusercontent.com/kubernetes/website/master/content/ja/examples/controllers/nginx-deployment.yaml
controlplane $ kubectl apply --server-side -f nginx-deployment.yaml             # port宣言でprotocolが不足しているので追加
controlplane $ vi nginx-deployment.yaml   # container[].nameを nginx から nginx-opts に変更します
controlplane $ kubectl apply --server-side -f nginx-deployment.yaml --field-manager=external --validate=false
controlplane $ kubectl get deploy nginx-deployment -o yaml
# コンテナの配列が1 -> 2に増えたのでStrategicMergeされていました

次に、ArgoCDのRolloutリソースでマージが可能かやってみましょう。
コンフリクトしてしまいました。リストごと置換するようになっていたからと思います。CRDのマージキーのあたりの設定が入っていれば、native objectと同じような挙動になったのかもしれませんが、公式のCRDの定義をそのまま使う限りには難しいようです。
k8s側でもSMP入るようになれば、kustomize側も実装してくれるのかもですね…!

controlplane $ kubectl create namespace argo-rollouts
controlplane $ kubectl apply -n argo-rollouts -f https://raw.githubusercontent.com/argoproj/argo-rollouts/stable/manifests/install.yaml
controlplane $ kubectl apply --server-side -f https://raw.githubusercontent.com/argoproj/argo-rollouts/master/docs/getting-started/basic/rollout.yaml
controlplane $ vi rollout.yaml  # # container[].nameを rollout-demo から rollout-demo-opts に変更します
controlplane $ kubectl apply --server-side --field-manager=external --validate=false -f rollout.yaml
Please review the fields above--they currently have other managers. Here
are the ways you can resolve this warning:
* If you intend to manage all of these fields, please re-run the apply
  command with the `--force-conflicts` flag.
* If you do not intend to manage all of the fields, please edit your
  manifest to remove references to the fields that should keep their
  current managers.
* You may co-own fields by updating your manifest to match the existing
  value; in this case, you'll become the manager if the other manager(s)
  stop managing the field (remove it from their configuration).
See http://k8s.io/docs/reference/using-api/api-concepts/#conflicts

kustomizeのSMP対応が待たれる

フォークしてCRDのSMPに対応したリポジトリが存在するので、そちらを利用するのも一つの手かもしれません。
マニフェストはできるだけ綺麗かつ体系だった形にしたいですよね!以上!

github.com