この投稿では、KubernetesのServer-Side Apply(SSA)において、部分的なマニフェストを繰り返し適用した場合の挙動について解説します。特に、フィールドマネージャーが管理していたフィールドを次の適用で省略するとどうなるのか、実験を通じて詳しく見ていきます。
コントローラーを開発していて、「SSAで部分的なマニフェストを適用したら、送信しなかったフィールドはどうなるのか?」という疑問が浮びました。もし送信した部分だけが差分としてマージされるなら、管理したいフィールドだけを送ればよいのでコードはシンプルになります。一方で、毎回管理対象のフィールド全体を送る必要があるなら、それを意識してコーディングしないと、意図せずフィールドを消してしまうようなバグを生み出してしまいます。
誤った思い込みのままコントローラーを開発してしまうと、リソースを意図せず破壊するバグを作りかねません。SSAの基礎的な仕様の理解ではあるものの、はっきり理解しておくことが不可欠だと思ったのでこの投稿を書いています。
なお、この記事で言及している実験コードは、以下のリポジトリで公開しています。
この投稿で学べること
- Server-Side Applyで部分的なマニフェストを適用した際の挙動
- フィールドの所有権とフィールドマネージャーの役割
- 管理対象フィールドを省略した場合に起こる削除の仕組み
- 複数のフィールドマネージャーによる協調的な管理
実験の目的
この実験の目的は、あるフィールドマネージャーが一度適用したマニフェストに含まれていたフィールドを、次の適用で省略した場合に何が起こるかを理解することです。
実際のコントローラー開発では、「最初はbreedフィールドを管理していたが、後からcolorフィールドだけを更新したい」というケースが発生することがあります。このとき、breedフィールドはどうなるのでしょうか。
実験1:同じマネージャーが管理するフィールドを省略したらどうなる?
実験では、Cat
というカスタムリソースを使います。フィールドの所有権を管理する「フィールドマネージャー」としてcat-owner
を指定します。
ステップ1:最初のApply(breedフィールドのみ)
まず、Catリソースmy-cat
に対して、spec.breed
(品種)だけを含む部分的なマニフェストを適用します。
cat := applycatv1.Cat("my-cat", "default").
WithSpec(applycatv1.CatSpec().
WithBreed("Maine Coon"))
err := cl.Apply(ctx, cat, client.FieldOwner("cat-owner"), client.ForceOwnership)
この操作後、リソースの状態は以下のようになります。cat-owner
がspec.breed
を管理していることがmanagedFields
に記録されています。
"spec": {
"breed": "Maine Coon"
},
"managedFields": [
{
"manager": "cat-owner",
...
"fieldsV1": {
"f:spec": {
"f:breed": {}
}
}
}
]
ステップ2:2回目のApply(colorフィールドのみ、breedは省略)
次に、同じmy-cat
リソースに対し、今度はspec.color
(色)だけを含むマニフェストを、同じcat-owner
で適用します。重要なのは、このマニフェストにはspec.breed
が含まれていないことです。
cat := applycatv1.Cat("my-cat", "default").
WithSpec(applycatv1.CatSpec().
WithColor("Black")) // breadはない
err := cl.Apply(ctx, cat, client.FieldOwner("cat-owner"), client.ForceOwnership)
観察結果
2回目のApply後、リソースにはspec.color
が追加された一方で、spec.breed
フィールドが跡形もなく消えてしまいました。
"spec": {
"color": "Black"
},
"managedFields": [
{
"manager": "cat-owner",
...
"fieldsV1": {
"f:spec": {
"f:color": {}
}
}
}
]
managedFields
を見ると、cat-owner
の管理対象がspec.breed
からspec.color
に更新されています。
なぜフィールドが削除されたのか?
この挙動は、Server-Side Applyの「宣言的な所有権管理」という部分に関連しています。Kubernetesの公式ドキュメントでは、まさにこのケースについて説明されています。
If you remove a field from a manifest and apply that manifest, Server-Side Apply checks if there are any other field managers that also own the field. If the field is not owned by any other field managers, it is either deleted from the live object or reset to its default value, if it has one. The same rule applies to associative list or map items.
日本語に訳すと、以下のようになります。
「マニフェストからフィールドを取り除いてそのマニフェストを適用すると、Server-Side Applyは他にそのフィールドを所有しているフィールドマネージャーがいるかどうかをチェックします。もし他に所有者がいなければ、そのフィールドは現状のオブジェクトから削除されるか、デフォルト値があればそれにリセットされます」
この説明で、今回の実験結果をしっかり説明できると思います。
- 最初のApplyで、
cat-owner
はspec.breed
フィールドの唯一の所有者となった - 2回目のApplyで、
cat-owner
はspec.breed
をマニフェストに含めなかった。これは「私はもうspec.breed
を管理しません」という意思表示と解釈される - 他に
spec.breed
の所有者がいなかったため、Server-Side Applyはライブオブジェクトからこのフィールドを削除した
これは、管理されなくなったフィールドが残り続けるのを防ぐためのクリーンアップ機能というわけです。フィールドを意図せず削除しないようにするためには、自分が管理したいすべてのフィールドを毎回Applyリクエストに乗せる必要があると理解しておくといいでしょう。
補足実験:別のフィールドマネージャーが登場したらどうなる?
では、別のフィールドマネージャーが登場した場合はどうなるのでしょうか。SSAの所有権に関する側面を見ていきましょう。
ステップ3:別のマネージャーによる3回目のApply
先ほどの状態(spec.color
のみが存在)から、age-controller
という新しいフィールドマネージャーがspec.age
だけを含むマニフェストを適用します。
観察結果
適用後、リソースはspec.color
とspec.age
の両方を持つ状態になります。cat-owner
が管理していたspec.color
は影響を受けません。
"spec": {
"color": "Black",
"age": 3
},
"managedFields": [
{
"manager": "age-controller",
...
"fieldsV1": {
"f:spec": {
"f:age": {}
}
}
},
{
"manager": "cat-owner",
...
"fieldsV1": {
"f:spec": {
"f:color": {}
}
}
}
]
managedFields
には、age-controller
とcat-owner
の2つのエントリーが記録され、それぞれがどのフィールドを所有しているかが明確に追跡されています。
age-controller
は、自分が以前に所有していたフィールド(今回は存在しない)を何も省略していないため、単純にspec.age
の所有権を新たに主張しただけです。cat-owner
が所有するフィールドには干渉しませんでした。
まとめ
今回の実験から、Server-Side Applyの使いかたとして次の2つが理解できました。
- マニフェスト完全性=フィールドはすべて含めること: あるフィールドマネージャーが、自身が管理していたフィールドを次のApplyマニフェストから省略すると、そのフィールドは(他に所有者がいなければ)削除される。なので、管理下に置くフィールドは、常にマニフェストに含めて、マニフェストには完全性を持たせましょう。
- 完全性は所有者ごとでOK: 複数のフィールドマネージャーが、同じリソースの異なるフィールドを互いに干渉することなく安全に管理できるので、他者管理のフィールドまでマニフェストに含める必要はない。
Server-Side Applyは、複数のコントローラーなどが1つのリソースを協調して管理するKubernetes環境において、重要な機能です。フィールドが「消える」現象はバグではなく、宣言的構成管理をより堅牢にするための強力な「仕様」というわけです。この挙動を理解することで、より安全なリソース管理ができるようになると思います。
最後までお読みくださりありがとうございました。