kubectl apply --server-side に移行したい
Server-Side Apply への移行の問題点
kubectl apply
時に、kubernetes 1.23 時点では client-side applyがデフォルトになっている。
これを sever-side apply に移行したい場合、単に、kubectl apply
からkubectl apply --server-side
、のように、
--server-side
フラグをつけるよう運用方法を変更した場合、問題が発生する。
簡単には以下のように問題が再現する。
問題の再現
以下の再現方法は、「Server-side apply: migration from client-side apply leaves stuck fields in the object #99003」に書かれている方法からの引用です。
まずは、通常の client-side applyでConfigMapを作ります。
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
key: value
legacy: unused
EOF
次に、同じマニフェストをserver-side applyします。
cat <<EOF | kubectl apply --server-side -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
key: value
legacy: unused
EOF
apiserverには適用したマニフェストが登録されていることがわかる。
$ kubectl get configmap -o yaml test | egrep -A 3 '^data:'
data:
key: value
legacy: unused
kind: ConfigMap
その後、legacy
データを削除したマニフェストをserver-side applyで登録すると、
cat <<EOF | kubectl apply --server-side -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
key: value
EOF
legacy
データを削除したにも関わらず、apiserver には legacy
データが残ってしまっていることが確認できる。
$ kubectl get configmap -o yaml test | egrep -A 3 '^data:'
data:
key: value
legacy: unused
kind: ConfigMap
何が問題か?
つまり、消したはずのフィールドが残ってしまう。
自分の経験だと、消したはずの環境変数 env
の値が残ってしまったせいで、予期しない挙動が起こって障害を起こしてしまった。
なぜフィールドが消えないか?
詳しくは「わかる!metadata.managedFields」を読もう!
対応策
問題は、manager: kubectl-client-side-apply
になっている .metadata.managedFields
である。とりあえずはこれを消す必要がある。fluxcdのissue では以下のワークアラウンドが紹介されている。
kubectl patch cm test --type=json -p='[{"op": "remove", "path": "/metadata/managedFields/1"}]'
ただ、この方法は、/metadata/managedFields/1
が manager: kubectl-client-side-apply
であることを確認しているわけでは無いため、意図しないフィールドを消してしまう可能性がある。
例えば、以下のスクリプトでfield managerを確認してみると、
$ kubectl get configmap -o json --show-managed-fields | \
jq -r ".items[] | select(.metadata.managedFields != null) |
( .metadata.managedFields | to_entries ) as \$managers | {
name: .metadata.name,
managers: [( \$managers[] |
{ key: .key, manager: .value.manager, operation: .value.operation }
)]
} |
select(.managers != []) |
\"## \" + .name + \"\n\" +
reduce .managers[] as \$m (\"\"; . + \"- \" + (\$m.key|tostring) + \" \"+ \$m.manager + \": \" + \$m.operation + \"\n\")
"
## config
- 0 kubectl-client-side-apply: Update
- 1 kubectl-edit: Update
## kube-root-ca.crt
- 0 kube-controller-manager: Update
## nginx
- 0 kubectl-client-side-apply: Update
## test
- 0 kubectl: Apply
- 1 kubectl-client-side-apply: Update
- 2 kubectl-edit: Update
- 3 kubectl-replace: Update
1 番目に必ずしも manager: kubectl-client-side-apply
がいないことがわかる。
また、他にも手動オペレーションで kubectl edit
や kubectl replace
などをおこなっていた場合は、それらの manager も server-side apply時のコンフリクトの原因となりうることがわかる。ということは、必要なのは kubectl-*
系のmanagerとkubectl: Update
になっているmanagedFields
の削除、ということになる。
つまり、必要なのはこれ。
RESOURCE_NAME=configmap
kubectl get ${RESOURCE_NAME} -o json --show-managed-fields | \
jq -r ".items[] | select(.metadata.managedFields != null) |
( .metadata.managedFields | to_entries ) as \$managers | {
name: .metadata.name,
managers: [( \$managers[] |
{ key: .key, manager: .value.manager, operation: .value.operation } |
select((.manager | startswith(\"kubectl\")) or .manager == \"before-first-apply\") |
select(.manager != \"kubectl\" and .operation != \"Apply\")
)] | sort_by(.key) | reverse
} |
select(.managers != []) |
{name: .name, items: [{op: \"remove\", path: (\"/metadata/managedFields/\" + (.managers[].key|tostring))}]} |
\"kubectl patch ${RESOURCE_NAME} \" + .name + \" --type=json -p='\" + (.items|tostring) + \"';\"
"
注意点
注意点1
注意点としては、上記のパッチは client-side applyからserver-side apply時に何の変更もなく移行した場合にのみ有効ということである。
すでに owner が kubectl-client-side-apply
のみになったフィールドが残ってしまっていた場合は、単に orphan なフィールドができてしまうだけなので、パッチを適用前にすでに想定外のフィールドが適用するオブジェクトに残ってしまっていないか確認しておく必要がある。
注意点2
server-side applyに完全に移行したとしても、運用時の手動オペレーション、例えば kubectl edit
やkubectl replace
で意図しないフィールドマネージャーの変更が起こってしまう。
kubectl edit
などの手動オペレーションを行なった場合は随時上記のパッチを利用してフィールドマネージャーをクリーンナップしておく必要がありそう。
まとめ
FluxCD のコードを見てみたところ、このようなCDツールを使っていた場合は特にユーザはこういったclient-side applyからserver-side applyへの移行に注意する必要がないように見える。
上記のPRでは、FluxCDの場合は今までのclient-side applyのフィールドマネージャーを一括削除し、server-side applyのフィールドマネージャーに移行している。
それ以外の運用者で、kubernetesにネイティブに対応していないCDツールで素のkubectl apply
を利用していた場合、client-side applyからserver-side applyへの移行は注意したほうがよさそう。
(というか、もっと素直な移行ツールがあってよさそうなものだが、何か見逃している気がする。)