はじめに
Kubernetes 1.22でGAされたServer-Side Apply1 (以降SSAと記載)について、2回に渡って投稿します。SSAはkubectlで実行するパターンと、controller-runtimeを使ってコード上で実行するパターンがあります。controller-runtimeでControllerを実装してSSAするのが一般的であるため、まずは概念理解のためにkubectlパターンを確認していきます。最終的にはコードでSSAするのを目標にします。
SSAとは
まず、Applyについて説明しますと、KubernetesではクライアントサイドのApply(CSA)とサーバーサイドのApply(SSA)があります。SSA登場前はCSAでのApplyが広く使われており、kubectl applyコマンドでリソースを作成したまたは変更を適用した場合、Kubernetesはkubectl.kubernetes.io/last-applied-configuration
というannotationにオブジェクトの情報を保持していました。そして、次回Apply時は、対象オブジェクトをGETし、このannotationを読み込んで差分計算後、適用を行うといったクライアントサイドでの差分計算を行う流れとなっていました。2 3ただ、誰かがFieldを更新した後に、他の誰かが次回kubectl applyした際は、Fieldのデグレを起こしてしまう更新ができてしまうといった問題がありました。そこで、SSAの登場です。SSAはFieldに所有者(manager)を持たせ、そのFieldの更新を所有者のみにしか許可しないといったことを行い、差分計算をし易くした機能です。仮に所有者以外が更新を行うとコンフリクトが起こるようになっています。
error: Apply failed with 1 conflict: conflict with "junya": .spec.containers[name="test"].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 https://kubernetes.io/docs/reference/using-api/server-side-apply/#conflicts
SSAを利用することで誰かにFieldを更新され、デグレを起こすといった問題をなくすことが可能となります。
kubectlでのSSAパターン
まず、以下のようになyamlファイルを用意します。
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
app: test
name: test
spec:
containers:
- image: nginx
name: test
その後、以下コマンドでServer-Side Applyを手動で行います。
--server-side
がServer-Side Applyでのリソース適用、
--field-manager
がFieldの所有者
を表しています。
$ kubectl apply -f test.yaml --server-side --field-manager=my-manager
pod/test serverside-applied
apply後は--show-managed-fields=true
フラグを立てることでFieldの所有者を確認可能です。
各Fieldのプレフィックスはここから確認してください。
$ kubectl get pod test -oyaml --show-managed-fields=true
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2022-07-18T12:08:57Z"
labels:
app: test
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:metadata:
f:labels:
f:app: {}
f:spec:
f:containers:
k:{"name":"test"}:
.: {}
f:image: {}
f:name: {}
manager: my-manager
operation: Apply
time: "2022-07-18T12:08:57Z"
上記の場合だと、my-managerという所有者が、適用したyamlの全Filedを保持していることを表しています。
ちなみに、CSAの場合は以下のようにannotationに情報を保持します。
$ kubectl get pod -oyaml
apiVersion: v1
items:
- apiVersion: v1
kind: Pod
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"creationTimestamp":null,"labels":{"app":"test"},"name":"test","namespace":"default"},"spec":{"containers":[{"image":"nginx","name":"test"}]}}
仮に別の所有者を指定してlabelを更新してみましょう。
label fieldはmy-managerが所有しているのでコンフリクトが発生します。ただ、その後の取り得るアクションを提示してくれるのは非常に親切です。
$ cat << EOT | kubectl apply --server-side --field-manager=updater -f -
> apiVersion: v1
> kind: Pod
> metadata:
> creationTimestamp: null
> labels:
> app: test-update
> name: test
> spec:
> containers:
> - image: nginx
> name: test
> EOT
error: Apply failed with 1 conflict: conflict with "my-manager": .metadata.labels.app
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 https://kubernetes.io/docs/reference/using-api/server-side-apply/#conflicts
アドバイス通りにしてみましょう。
--force-conflictsを指定することでCSA同じ動作にし、コンフリクトを無視することができます。
$ cat << EOT | kubectl apply --server-side --field-manager=updater --force-conflicts -f -
> apiVersion: v1
> kind: Pod
> metadata:
> creationTimestamp: null
> labels:
> app: test-update
> name: test
> spec:
> containers:
> - image: nginx
> name: test
> EOT
pod/test serverside-applied
その他、全く同じyamlを適用することでFieldの共同所有者になることも可能です。
$ kubectl apply -f test.yaml --server-side --field-manager=my-updater
$ kubectl get pod -oyaml --show-managed-fields=true
apiVersion: v1
items:
- apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2022-07-18T12:36:10Z"
labels:
app: test
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:metadata:
f:labels:
f:app: {}
f:spec:
f:containers:
k:{"name":"test"}:
.: {}
f:image: {}
f:name: {}
manager: my-manager
operation: Apply
time: "2022-07-18T12:36:10Z"
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:metadata:
f:labels:
f:app: {}
f:spec:
f:containers:
k:{"name":"test"}:
.: {}
f:image: {}
f:name: {}
manager: my-updater
operation: Apply
time: "2022-07-18T12:36:25Z
その後、最初の所有者のoptionalなfieldを削除してapplyすることで所有権を放棄することも可能となります。今回の場合.metadata.labels fieldを削除してapplyしています。
$ cat test-update-lable.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
name: test
spec:
containers:
- image: nginx
name: test
$
$ kubectl apply -f test-update-lable.yaml --server-side --field-manager=my-manager
pod/test serverside-applied
$ kubectl get po -oyaml --show-managed-fields=true
apiVersion: v1
items:
- apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2022-07-18T12:36:10Z"
labels:
app: test
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:metadata:
f:labels:
f:app: {}
f:spec:
f:containers:
k:{"name":"test"}:
.: {}
f:image: {}
f:name: {}
manager: my-updater
operation: Apply
time: "2022-07-18T12:36:25Z"
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:spec:
f:containers:
k:{"name":"test"}:
.: {}
f:image: {}
f:name: {}
manager: my-manager
operation: Apply
time: "2022-07-18T12:45:53Z"
ユーザ側でSSAを意識する必要はあるのか?
ありません。
さいごに
今回はSSAを手動で試しました。
ユーザ側でのSSAの意識は不要ですが、Controllerで実装することで差分計算やkube-apiseverへのアクセス回数の削減などメリットがあります。現状SSA実装の公式ドキュメントは少ないですが、次回からは今回と同様の動作をControllerでの実現を目指します。