$30 off During Our Annual Pro Sale. View details »

わかる!metadata.managedFields

Kazuki Suda
February 22, 2022

 わかる!metadata.managedFields

Kubernetes Meetup Tokyo 48 (2022/2/22) / https://k8sjp.connpass.com/event/237734/

近頃 Server-Side Apply (SSA) の利用が広がっています。GitOps ツールである Flux2 は v0.18.0 でマニフェストの適用に SSA を使用するようになりました。そこで重要になってくるのが metadata.managedFields です。

このセッションでは、kubectl v1.21 まで kubectl get -o yaml で表示されていてめっちゃ邪魔だった metadata.managedFields が何のために存在しているのか紹介します。また SSA によりオブジェクトのフィールドを削除したはずが実際には削除されていないなんてこともおきます。なぜそんなことが発生するのか、またその状態をどのように解決するかも紹介します。

Kazuki Suda

February 22, 2022
Tweet

More Decks by Kazuki Suda

Other Decks in Technology

Transcript

  1. Kubernetes Meetup Tokyo #48(2022/02/22) SUDA Kazuki, Preferred Networks, Inc. @superbrothers

    わかる!metadata.managedFields
  2. @superbrothers SUDA Kazuki / @superbrothers ▶ Preferred Networks, Inc. /

    エンジニア ▶ Scalar, Inc. / 技術アドバイザ ▶ Kubernetes Meetup Tokyo 共同主催者 ▶ Cloud Native Ambassador (CNCF) ▶ 「Kubernetes実践⼊⾨」、「みんなのDocker/Kubernetes」共著書 ▶ 「⼊⾨ Prometheus」、「Kubernetes で実践するクラウドネイティブ DevOps」監訳書 2
  3. @superbrothers もくじ ▶ Kubernetes 事件簿: 消えないラベル(事件編、疑惑編) ▶ Server Side Apply(SSA)と

    metadata.managedFields ▶ わかる!metadata.managedFields ▶ Kubernetes 事件簿: 消えないラベル(解決編) ▶ まとめ 3
  4. @superbrothers Kubernetes 事件簿 消えないラベル

  5. @superbrothers 😱 消えないラベル(事件編) ある⽇の、GitOps ツールの Flux2 を導⼊している企業で、開発者がマニフェストファイルのあるラベル を削除してリポジトリに変更をコミットした。GitOps ツール導⼊企業なので、あとは放っておけば ツールがクラスタに変更を適⽤してくれる。

    数時間後、Flux2 による適⽤が成功しているにも関わらず削除したはずのラベルが実際には削除されて いないことに開発者が気がついた。 5 リポジトリ 開発者 1. マニフェストファイルを変更して リポジトリにコミット 2. GitOps ツールがリポジトリに更新があると マニフェストをクラスタに Apply 3. 数時間後にオブジェクトを確認すると 
 変更が反映されていないことが判明 !"!#$%&'()*+,-&$./0123456
  6. @superbrothers 消えないラベル(事件編) 6 証拠品1: Flux2 が適⽤したマニフェスト 証拠品2: 適⽤後のオブジェクトの状態 apiVersion: v1

    kind: ConfigMap metadata: name: config labels: $ kubectl get configmap config -o yaml apiVersion: v1 kind: ConfigMap metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"v1","kind":"ConfigMap","metadata":{"ann creationTimestamp: "2022-02-17T12:22:15Z" labels: test-data: value2 name: config namespace: default resourceVersion: "3758306" uid: 2c4d5bda-1c3b-450d-b7f7-5fa3e8159f83 test-data ラベルを消したはずが 
 オブジェクトから消えていない。 📝$#7(89$:;<=>:?@AB:CDEFGHAIJKL
  7. @superbrothers 消えないラベル(疑惑編) 「最近何か変わったことがあっただろうか...」 「そういえば Flux2 がアップグレードされていたはず。」 
 「変更内容は... SSA(Server Side

    Apply)のサポートだ。」 開発者は、SSA には覚えがあったので SSA でフィールドの管理⽅法がクライアントサイドのときから 
 ⼤きく変わっていることを知っていた。 「まずは久しく⾒てない metadata.managedFields を確認するところからだ。」 7
  8. @superbrothers 嫌われものの metadata.managedFields 8 $ kubectl version --client Client Version:

    version.Info{Major:"1", Minor:"20", GitVersion:"v1.20.15", GitCommit:"8f1e5bf0b9729a899b8df86249b56e2c74aebc55", GitTreeState:"clean", BuildDate:"2022-01-19T17:27:39Z", GoVersion:"go1.15.15", Compiler:"gc", Platform:"linux/amd64"} $ kubectl get po nginx-66f6ffdddf-p2kt5 -o yaml apiVersion: v1 kind: Pod metadata: creationTimestamp: "2022-02-17T05:33:10Z" generateName: nginx-66f6ffdddf- labels: app: nginx pod-template-hash: 66f6ffdddf managedFields: - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:metadata: f:generateName: {} f:labels: .: {} f:app: {} f:pod-template-hash: {} MNOP>QJ6FHRSIJBTU
  9. @superbrothers kubectl v1.21 でデフォルトで出⼒されなくなった 9 $ kubectl version --client Client

    Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.9", GitCommit:"b631974d68ac5045e076c86a5c66fba6f128dc72", GitTreeState:"clean", BuildDate:"2022-01-19T17:51:12Z", GoVersion:"go1.16.12", Compiler:"gc", Platform:"linux/amd64"} $ kubectl get po nginx-66f6ffdddf-p2kt5 -o yaml apiVersion: v1 kind: Pod metadata: creationTimestamp: "2022-02-17T05:33:10Z" generateName: nginx-66f6ffdddf- labels: app: nginx pod-template-hash: 66f6ffdddf name: nginx-66f6ffdddf-p2kt5 namespace: default ownerReferences: - apiVersion: apps/v1 blockOwnerDeletion: true controller: true kind: ReplicaSet name: nginx-66f6ffdddf V*&*WXY#,X7YZ$[\Q\]IK デフォルトで出⼒されなくなっただけで、実際には存在しています。 
 ⾒やすくなってよかったが、⼀⽅で⽬にすることが減り、知る機会がなくなってしまった。 --show-managed-fields ^._`a4b $$$$$$$$$$$cdedfghSi5
  10. $ kubectl get cm config -o yaml --show-managed-fields apiVersion: v1

    kind: ConfigMap metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"v1","kind":"ConfigMap","metadata":{"annotations":{},"labels":{"test-data":"value2"},"name":"config","namespa creationTimestamp: "2022-02-17T12:59:48Z" labels: test-data: value2 managedFields: - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:metadata: f:labels: {} manager: flux-controller operation: Apply time: "2022-02-17T13:00:42Z" - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:metadata: f:annotations: .: {} f:kubectl.kubernetes.io/last-applied-configuration: {} f:labels: F:test-data: {} manager: kubectl-client-side-apply operation: Update time: "2022-02-17T13:00:10Z" name: config namespace: default resourceVersion: "3762113" uid: 5ee6567e-5248-4494-a0aa-d55a5e16b8f3
  11. @superbrothers ユーザが metadata.managedFields を理解する必要がある? 11 $ kubectl explain po.metadata.managedFields KIND:

    Pod VERSION: v1 RESOURCE: managedFields <[]Object> DESCRIPTION: ManagedFields maps workflow-id and version to the set of fields that are managed by that workflow. This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. A workflow can be the user's name, a controller's name, or the name of a specific apply path like "ci-cd". The set of fields is always in the version that the workflow used when modifying the object. ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.
  12. @superbrothers metadata.managedFields と Server Side Apply(SSA)

  13. @superbrothers Server Side Apply(SSA)とは 差分の計算をサーバサイドで実施する Apply(適⽤)のこと ▶ そもそも Kubernetes における

    Apply(適⽤)は、オブジェクトが存在しなければ作成し、 
 存在すれば差分(パッチ)を適⽤する操作 ▶ 差分を計算するには、ユーザが管理しているフィールドを記憶しておく必要がある + 現在のオブジェクトの状態から差分を計算してしまうと、そのフィールドがユーザが管理してい るものなのか、Kubernetes(コントローラ等)が管理しているものなのかわからない ▶ SSA は誰がそのフィールドを管理しているかの情報を metadata.managedFields に保存する + SSA 以外の⼿段によるフィールドの更新も SSA への移⾏や互換性のために同様に記録される ▶ SSA は Kubernetes v1.22 で GA となり、利⽤が広がっている 13 jkl$mno$:$pXq7-rVX&+$ZqX'stXq7,'*Z$uvwxyz{|Ai56
  14. @superbrothers サーバサイドがあるなら、クライアントサイドは?🤔 ▶ クライアントサイドの Apply には問題があることが分かっているため、SSA が開発された + kubectl edit

    でコンテナイメージを変更したあとにマニフェストが適⽤すると変更が巻き戻る + SSA では、フィールドの衝突が検知されて誰かがオブジェクトを変更したことがわかる + クライアントサイドにロジックがあることでGo 以外の⾔語や kubectl 以外のツールから使うこと がむずかしい + SSA はサーバサイドで実⾏されるため、curl からでも利⽤できる + ユーザの管理しているフィールドの情報のみを保存している + SSA は、ユーザに加えてコントローラが管理しているフィールドも含めて保存する ▶ v1.23 時点で kubectl apply はまだクライアントサイドの Apply を使っている + --server-side オプションで SSA に変更できるが、いちユーザが積極的に使う理由はない 14 }()X'+7s}()Xt&X+XZs,-~7*Z+•*qq7,XY•'-&€,W(t*+,-&b FJ•‚ƒ„w_`az…]Ii5L
  15. @superbrothers わかる!metadata.managedFields

  16. @superbrothers 16 apiVersion: v1 kind: ConfigMap metadata: name: test-cm namespace:

    default labels: test-label: test managedFields: - manager: kubectl operation: Apply apiVersion: v1 fields: f:metadata: f:labels: f:test-label: {} - manager: kube-controller-manager operation: Update apiVersion: v1 time: '2019-03-30T16:00:00.000Z' fields: f:data: f:key: {} data: key: new value フィールドのマネージャ名で⾃分で宣⾔する。 ʢkubectl apply --server-side の 
 マネージャ名が kubectl (デフォルト)) どの操作でオブジェクトを変更したか。 SSA は "Apply" 、それ以外で "Update" になる。 managedFields は SSA でしか使われないが 
 既存の⽅法との互換性を持たせるために 
 Update も記録されるようになっている。 このマネージャが管理している 
 フィールドを⽰している kube-controller-manager があとから SSA 以外の⼿段で 
 フィールドを追加したことがわかる V*&*WXY#,X7YZ$[$†()Xt&X+XZ$[{|AIJK‡4 ˆ‰Š‹>ˆŒ•Žd•kB•AI:J‘\J’
  17. @superbrothers フィールド管理 SSA の⽬的の1つは、ユーザと"コントローラ"の協⼒でオブジェクトで管理すること。どのフィールド をどのマネージャが管理しているかがわかるようになっている。 ▶ あるフィールドを1⼈のマネージャが管理している場合 + 基本的に他のマネージャに更新してくれるなということなので、更新されそうになったら 


    コンフリクト(衝突)として失敗させる ▶ あるフィールドを複数のマネージャが管理している場合 + 複数のマネージャが同じフィールドを "同じ値で"設定するとそのフィールドを共同で 
 所有している状態になる 
 共同所有しているマネージャが値を変更しようとしてもコンフリクトとして失敗させる + 所有権を放棄するには、そのフィールドを削除してから Apply する 17
  18. @superbrothers フィールド更新時のコンフリクト 他のマネージャがすでに管理しているフィールドを更新しようとするとコンフリクトが発⽣し、 
 意図しない上書きが発⽣することを防ぐ。コンフリクトが発⽣した場合には3つの選択肢がある。 ▶ 値を上書きして唯⼀のマネージャになる + 意図した上書きの場合は force

    パラメータを true にして Apply することで強制的に上書きする。 また、他のマネージャの所有権が失われる。 + マネージャがコントローラの場合、コンフリクトしたらどうにもならないので 
 基本的に force ありで上書きすることになる ▶ 値を上書きせずに所有権を放棄する + コンフリクトしたフィールドの定義を削除して Apply することで所有権を放棄できる ▶ 値を上書きせずに共同所有者になる + コンフリクトしたフィールドで同じ値を設定して Apply することで共同所有者になれる コンフリクトが発⽣するのは "Apply" 操作だけで "Update" が失敗することはない。 18 }()X'+7$*qq7r$4:$••€-t'X•'-&€7,'+Z$^._`a45’
  19. @superbrothers kubectl apply でのコンフリクト 19 $ kubectl apply -f deploy.yaml

    --server-side error: Apply failed with 1 conflict: conflict with "kubectl-edit" using apps/ v1: .spec.template.spec.containers[name="nginx-unprivileged"].image 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/server-side-apply/#conflicts “SK‚2_`az”gAIQSI•]–—˜™6😊
  20. @superbrothers Kubernetes 事件簿 消えないラベル(解決編)

  21. $ kubectl get cm config -o yaml --show-managed-fields apiVersion: v1

    kind: ConfigMap metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"v1","kind":"ConfigMap","metadata":{"annotations":{},"labels":{"key":"value2"},"name":"config","namespace":"d creationTimestamp: "2022-02-17T12:59:48Z" labels: test-data: value2 managedFields: - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:metadata: f:labels: {} manager: flux-controller operation: Apply time: "2022-02-17T13:00:42Z" - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:metadata: f:annotations: .: {} f:kubectl.kubernetes.io/last-applied-configuration: {} f:labels: F:test-data: {} manager: kubectl-client-side-apply operation: Update time: "2022-02-17T13:00:10Z" name: config namespace: default resourceVersion: "3762113" uid: 5ee6567e-5248-4494-a0aa-d55a5e16b8f3 Flux はたしかに test-data ラベルを所有していない 別のマネージャが test-data ラベルを所有権を保持したままになっている! VX+*Y*+*sV*&WXY#,X7YZ$>5šI‡›œ[•K6
  22. $ kubectl get cm config -o yaml --show-managed-fields apiVersion: v1

    kind: ConfigMap metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"v1","kind":"ConfigMap","metadata":{"annotations":{},"labels":{"test-data":"value2"},"name":"config","namespa creationTimestamp: "2022-02-17T12:59:48Z" labels: test-data: value2 managedFields: - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:metadata: f:labels: {} manager: flux-controller operation: Apply time: "2022-02-17T13:00:42Z" - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:metadata: f:annotations: .: {} f:kubectl.kubernetes.io/last-applied-configuration: {} f:labels: f:test-data: {} manager: kubectl-client-side-apply operation: Update time: "2022-02-17T13:00:10Z" name: config namespace: default resourceVersion: "3762113" uid: 5ee6567e-5248-4494-a0aa-d55a5e16b8f3 ▶ kubectl-client-side-apply マネージャのみがこのフィールドを所有している ▶ このマネージャが所有権を放棄すればこのフィールドが削除される ▶ manager と operation から 
 kubectl apply (クライアントサイド)で変更されていることがわかる 解決策
  23. $ cat <<EOL | kubectl apply -f- apiVersion: v1 kind:

    ConfigMap metadata: name: config labels: EOL configmap/config configured $ kubectl get cm config -o yaml --show-managed-fields apiVersion: v1 kind: ConfigMap metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"v1","kind":"ConfigMap","metadata":{"annotations":{},"labels":null,"name":"config","namespace":"default"}} creationTimestamp: "2022-02-17T12:59:48Z" managedFields: - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:metadata: f:labels: {} manager: flux-controller operation: Apply time: "2022-02-17T13:00:42Z" - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:metadata: f:annotations: .: {} f:kubectl.kubernetes.io/last-applied-configuration: {} manager: kubectl-client-side-apply operation: Update time: "2022-02-17T13:00:10Z" test-data ラベルの所有権を放棄できている ラベルのフィールドが削除されている test-data ラベルの存在しないマニフェストを kubectl apply(クライアントサイド)で適⽤
  24. @superbrothers 「消えないラベル」の真相 1. 開発者は Flux2 による適⽤(SSA)を⽌めて⼿元のマニフェストファイルを適⽤した + 開発者は test-data ラベルの値を変更し適⽤したことでこのフィールドのマネージャになっていた

    
 (kubectl-client-side-apply マネージャがそれ) 2. 開発者はこの変更でうまく機能したので変更をリポジトリにコミットした 3. 開発者は Flux2 による適⽤(SSA)を再開した + test-data ラベルはすでに他のマネージャが所有権を持っている + Flux2 はラベルを同じ値で適⽤したので所有権を取得し、共同所有者になった 4. 開発者はその後ラベルがいらなくなったのでマニフェストを変更してリポジトリにコミットした + Flux2 は test-data ラベルの削除されたマニフェストを適⽤してラベルを削除しようとするが、 
 他に共同所有がいるため、所有権を放棄するだけで実際にフィールドは削除されなかった 24 ž:5šIŸ‘B6
  25. @superbrothers 根本的な解決はむずかしい ▶ 問題1: Flux2 がユーザとは異なるマネージャとして SSA していること + SSA

    を使う前はクライアントサイドの適⽤を使⽤していた("ユーザ"としての適⽤と同等) + SSA を採⽤し独⾃のマネージャを使⽤したことで "ユーザ" として適⽤しなくなった + "ユーザ" として適⽤してくれれば共同所有にならない(上書きになる) + SSA で "ユーザ" として適⽤するには適⽤時に manager 名を kubectl にすればいける ▶ 問題2: kubectl apply がクライアントサイドの適⽤をデフォルトで使⽤していること + クライアントサイドの適⽤は manager が "kubectl-client-side-apply", operation が "Update" + Flux2 が "ユーザ" として SSA してくれてもユーザがクライアントサイドの適⽤を使ったら 
 同じユーザにならない + すべての⼈間が常に kubectl apply に --server-side オプションをつけるのは困難 現実的には⼈間が変更を kubectl apply で適⽤したら、あとで変更前の状態で 
 適⽤し直しておくくらいしかできない。しかし⼈間は忘れるのでむずかしい。 25
  26. @superbrothers まとめ

  27. @superbrothers まとめ ▶ metadata.manageFields は Server Side Apply(SSA)でどのユーザまたはコントローラが 
 オブジェクトのどのフィールドを所有しているのかの管理に使われる

    + SSA は Kubernetes v1.22 で GA となり、利⽤が広がっているが、 
 いちユーザが kubectl apply で積極的に SSA を使う必要はない ▶ kubectl v1.21 からデフォルトで出⼒されなくなったけど、いなくなったわけじゃない ▶ managedFields は Kubernetes が管理しているので"基本的に"直接書き換えたりしてはいけない ▶ SSA における metadata.managedFields を使ったフィールド管理 + 1⼈のマネージャが所有している場合と複数で共同所有している場合がある + 所有するフィールドが削除されたマニフェストを適⽤することでマネージャは所有権を放棄する ▶ SSA で複数のマネージャが同じフィールドを更新しようとするとコンフリクトする 27
  28. 機械学習プラットフォームエンジニア We're hiring! https://www.preferred.jp/ja/careers/ ⼤規模計算基盤エンジニア ▶ ⼤規模な計算基盤の実現に関する企画、設計および研究開発 + CPU/GPU/MN-Coreを組み合わせた 


    ドメイン最適化された計算基盤の企画・設計 + クラスタ型計算機の設計 + 規模性の⾼い計算機を⽀えるインターコネクトネットワーク およびデータセンタ規模ネットワーク技術 + 規模性の⾼い計算機を⽀えるストレージ階層、 
 ストレージシステム ▶ 規模性の⾼い計算機を⽀える技術の研究開発 + エネルギー効率の良い⼤規模計算機システム + ⾼い電⼒密度をもつ⾼性能計算機の構成技術 + ⾼い熱密度をもつ⾼性能計算機の構成技術 + スケーラブルな計算基盤の監視・管理技術 + 管理運⽤しやすい⼤規模計算システム ▶ ⼤規模なクラスタ計算機の継続的な性能改善 + 物理的なシステムメトリクス収集とそれを⽤いた 
 既存システムの改善、新規設計へのフィードバック ▶ ⾃由度・拡張性・使いやすさのトレードオフが取れた⼤規模機械学 習プラットフォームの機能設計と開発 + 例: 機械学習ワークフローツール、実験管理ツール、 
 GPUやMN-Core向け統合開発環境の構築 ▶ ⼤規模機械学習プラットフォームの運⽤と運⽤改善(⾃動化等) + 例: ⾃動サーバプロビジョニング、パブリッククラウド連携に よる運⽤効率化、インフラ健全性の⾃動診断と保守省⼒化 ▶ ⼤規模機械学習プラットフォーム上での計算資源 
 (GPU, MN-Coreを含む)配分の最適化 + 例: Kubernetes Schedulerの機能拡張、 
 リソース利⽤量制限拡張の開発 ▶ 最先端の分散計算基盤技術の Proof of Concept 構築及び 
 プラットフォームでの実⽤化 + 例: Kubernetes上での分散強化学習実⾏ツール
  29. None
  30. @superbrothers Appendix ▶ Server-Side Apply | Kubernetes + https://kubernetes.io/docs/reference/using-api/server-side-apply/ ▶

    KEP 555 - Apply + https://github.com/kubernetes/enhancements/tree/master/keps/sig-api-machinery/555-server-side-apply ▶ Kubernetes 1.21: SIG-CLI (kubectl) 変更内容 - Qiita + https://qiita.com/superbrothers/items/afeea65db4f3d4d1c5ef ▶ Enable kubectl-get to strip managed fi elds by knight42 · Pull Request #96878 · kubernetes/kubernetes + https://github.com/kubernetes/kubernetes/pull/96878 ▶ Kubernetes 1.14: Server-side Apply (alpha) - Qiita + https://qiita.com/superbrothers/items/aeba9406691388b6a19e ▶ Kubernetes: kubectl apply の動作 - Qiita + https://qiita.com/tkusumi/items/0bf5417c865ef716b221 ▶ Server-side reconciliation is coming | Flux + https:// fl uxcd.io/blog/2021/09/server-side-reconciliation-is-coming/ ▶ https://github.com/superbrothers-sandbox/kubernetes-meetup-tokyo-48-metadata-managedFields 30