MIXI DEVELOPERS NOTE
👾

Argo CDにおける`must have at most 262144 bytes のSync Error`の解決策とその比較

2024/06/17に公開

Argo CDでkube-prometheus-stackなどを管理しようとすると、 Too long: must have at most 262144 bytes でSyncFailedになってしまうことがあります。

今回このエラーに遭遇し、対処を自信を持って行うために詳細まで調べたのでそれをまとめます。同じエラーに遭遇した方が安心して対処できるようになると幸いです。

先に結論

結論としては、Too long: must have at most 262144 bytes は対象のマニフェストファイルのサイズが大きすぎると発生します。Syncを実行する際にエラーが出るresouceのみを選択して、REPLACEかSERVER-SIDE APPLYというオプションをつければSyncすることが可能です。

そして、REPLACEは kubectl replace/create を実行することになり、これ以降このresourceに対するSyncが通常のままだとうまくいかない可能性があります。そのため、できる限りSERVER-SIDE APPLYを利用するのが良さそうです。

詳しく見てみる

エラー内容について

エラー内容はToo long: must have at most 262144 bytes となっており、これは対象のマニフェストファイルのサイズが256kbを超えていると出ます。kube-prometheus-stackにはそれを超えるようなCRDがあり、今回はそれに引っかかってしまったのでこのエラーに遭遇しました。

ではなぜマニフェストのファイルサイズが256kbを超えるとエラーになるのでしょうか?その理由はKubernetesにおけるapplyの仕組みにあります。より深掘るためにそこについても触れていきます。

Kubernetesにおけるapplyの仕組み

Argo CDはSyncを実行するときに、内部的には kubectl applyを実行しています。kubectl apply を実行した際に、Kubernetesではマニフェストの差分を追加のフィールドにおいては現在の実際のKubernetesの状態から、削除されるフィールドにおいては前回最後にapplyした際の状態から確認します。

ここら辺についてはKubernetes完全ガイド 第2版が詳しいのでぜひそちらを参考にしてください。

今回はここで利用する「前回最後にapplyした際の状態」の保持方法が問題になってきます。Kubernetesではこれを保持するために last-applied-configuration というannotationを各オブジェクトで持っています。これはapplyが行われるたびに対象のマニフェストをjson形式でそのまま持つ形で更新されます。そして、annotationは上限が256kbまでになっています。そのため、対象のマニフェストファイルが256kbを超えるとannotationの上限を超えてしまうのでエラーになってしまうというのが根本の原因です。

解決方法

この問題を解決するには、以下の2つの方法があります。

  • REPLACE optionを利用する
  • SERVER-SIDE APPLY optionを利用する

順に見ていきます。

REPLACE optionを利用する

REPLACE optionはSync実行時に指定できるオプションの1つです。これはSyncしようとしている対象がすでに存在していれば kubectl replace を行い、存在していなければ kubectl create を行います。

kubectl replace , kubectl create は両方ともkubectlの実行時のオプションとして --save-config を利用しない場合、 last-applied-configuration を保持しません。

Argo CDのSyncにおけるREPLACE optionでは --save-config を利用しないので、 last-applied-configuration の保持を避けることができ、結果としてSyncができるということになります。

SERVER-SIDE APPLY optionを利用する

SERVER-SIDE APPLY optionもSync実行時に指定できるオプションの1つです。これは kubectl apply --server-side を行います。 --server-side はKubernetesのv1.22からStableになった機能です。先ほど紹介した last-applied-configuration を利用した通常のapplyをClient-Side Applyと呼び、 --server-side オプションを利用したapplyをServer-Side Applyと呼びます。

Server-Side Applyでは「どのフィールドにどういった差分があるか?」ではなくて、「だれがそのフィールドの所有権をもっているか?」に焦点を当ててapplyを実行します。差分自体は今回applyするマニフェストと現状のKubernetesの状態から確認します。これを管理するために managedFields というmetadataを持っています。これはmanagerというapplyの実行者、つまりフィールドの所有者を記録するためのものです。Server-Side Applyではmanagerを kubectl apply --server-side --field-manager=manager-1 というような形式で指定することが可能です。

このように、Server-Side Applyでは managedFields を保持するだけで、「前回最後にapplyした際の状態」を保持しません。そのため、それを利用したArgo CDのSERVER-SIDE APPLY optionではファイルサイズには関係なくSyncすることが可能です。

ちなみに、Kubernetes本来のServer-Side Applyにはフィールドにapplyをしたmanager以外がそのフィールドにapplyを試みるとconflictが発生して、そのままだとapplyできないという仕様になっています。執筆時の2024年6月現在のArgo CDでは基本的にmanagerを上書きするようになっているため、Argo CD利用者は特に意識せずapplyをその後もできます。

比較

これだけ見ると、どちらを利用してもSyncすることは可能ですが比較していきます。

REPLACEとSERVER-SIDE APPLYの一番の違いは、それ以降applyで更新することができるかできないかになります。

REPLACEの場合、文字通り毎回作り替えることになります。 last-applied-configuration を持たないので、削除分の差分計算ができません。そのため、更新する際には毎回オブジェクトを削除して、作り直すような挙動になります。これは意図しない不具合を引き起こす原因になり得ます。

一方でSERVER-SIDE APPLYの場合、保持するのは「だれがそのフィールドの所有権を持っているか?」だけで、差分自体は追加分も削除分も実際のKubernetesの状態との比較で行います。そのため、こちらは巨大なマニフェストファイルの一部分を変更ないし、削除をしたときもその部分だけ更新することができます。しかも執筆時は仮にmanagerが異なる場合でも、Argo CD上からはmanagerという概念を考えなくてもSyncを行うことができます。

これらのことから、新規にオブジェクトを作成する際にはどちらでも特に違いはないが、その後対象のマニフェストを更新する場合には、SERVER-SIDE APPLYを利用しておいた方が意識するべきことは減ると思います。

ちなみに、REPLACEとSERVER-SIDE APPLYの両方のオプションを選択すると、REPLACEが優先されるのでお気をつけください。

まとめ

Argo CD固有の問題というよりは、Kubernetesのapplyの仕組みに関わるエラーになっていました。ちょっと調べてみると同じエラーに当たっている人が多かったです。

解決策として2つのオプションを提示している記事や、Argo CDのSyncに関する公式ドキュメントにも解決策は書いてあります。ただ、それらが根本的にどのようになっていて、両者の比較まで行っているものがなかったので今回記事にしました。

問題の解決の参考になれば幸いです。

参考にした本・リンク

GitHubで編集を提案
MIXI DEVELOPERS NOTE
MIXI DEVELOPERS NOTE

Discussion