はじめまして。NECソリューションイノベータの毛利と申します。
今日は、Kubernetes v1.24で新しく実装された機能「non-graceful node shutdown」について紹介したいと思います。
私もコミュニティでの議論やコードレビューに参加したので、この機能が実装され、無事にリリースされてとても嬉しいです!
TL;DR
- Kubernetes ver1.24で「non-graceful node shutdown」機能が新しく実装された。
- Persistent Volume(以降「PV」)がアタッチされたpodが動いている場合、ホストノードがハードウェア障害などで正常なシャットダウンができなかった時でも、non-graceful node shutdown機能を使うことでpodの削除・PVのデタッチができるようになる。
背景
まずはこの機能が生まれた背景から説明したいと思います。
KubernetesクラスタのWorkerノードがハードウェア故障等によりダウンした際、Controllerノードはその障害ノード上の pod 群を他のノード上で再作成します。
現状、 kubectl delete pod -f
コマンドによりpodを強制削除することで回避することが可能ですが、podがたくさんある場合は大変ですよね。
この件に関しては数年前からKubernetesコミュニティで議論が行われていたものの、実装方針についてなかなか結論が出ず議論が停滞していました。
non-graceful node shutdown機能の概要
workerノードがダウンした際に、そのノードに対してユーザは node.kubernetes.io/out-of-service
taint(ver1.24で新規追加)を付与する必要があります。
kube-controller-managerのPod GC Controllerは、podの状態を確認し、Terminating
状態のpodを見つけると、そのpodが動いているノードの状態が NotReady
になっている、かつ、ノードに node.kubernetes.io/out-of-service
taintが付与されている場合は、Terminating
状態のpodを強制削除します。また、 effectに NoExecute
を設定して node.kubernetes.io/out-of-service
taintを付与すると、 node.kubernetes.io/out-of-service
taintが付与されたノード上のpodはevictされ、また、そのノード上に新しくpodがスケジューリングされなくなります。(podがtolerationを持つ場合以外は)
podが強制削除されると、attachdetach reconciler(Podに対するPVのアタッチ/デタッチを管理する役割を持つkube-controller-managerのVolume Controllerの仕組み)はノードに node.kubernetes.io/out-of-service
taintが付与されているかどうかをチェックします。taintが付与されていると、デフォルトで設定されている6分間の待機時間を待つことなく、ボリュームの強制デタッチを行います。taintについては https://kubernetes.io/docs/reference/labels-annotations-taints/ に記載されています。
ちなみに、reconcilerというのは、Kubernetesリソースの「あるべき姿」に「実際の姿」を合わせる処理を定期的に行う仕組みです。Kubernetesに詳しい方なら常識だと思うので偉そうに言うことじゃないですけど、、あと調べるとたくさん出てきます。
機能実装のPull Request(以降「PR」)は https://github.com/kubernetes/kubernetes/pull/108486 です。そんなに難しい箇所はないと思うので、見てみると面白いと思います。実際にロジックが変更されているのは以下の2つのファイルです。
- pkg/controller/podgc/gc_controller.go
- pkg/controller/volume/attachdetach/reconciler/reconciler.go
graceful node shutdown機能との違い
「non-graceful node shutdown」と聞いて、ver1.21でβ実装された「graceful node shutdown」を思い出した方も多いと思います。
なので、ここでは non-graceful node shutdown
機能と graceful node shutdown
機能の違いを説明します。
ドキュメント https://kubernetes.io/ja/docs/concepts/architecture/nodes/#graceful-node-shutdown で説明されていますが、 graceful node shutdown
というのは、ノードで shutdown
シグナルが発生した際に、そのノードの上のpod終了までの猶予期間を設定できる機能です。
ノードのシャットダウン時にその上のpodが異常終了してしまう、という課題を解決するために提案された機能なのですが、ハードウェア障害などでノードがシャットダウンしてしまった場合は、 shutdown
シグナルによるシャットダウンではなく意図せぬシャットダウンなので(例えば電プチとかですね)、シャットダウンまで待つ、ということができないという課題があります。そこで今回提案されたのが non-graceful node shutdown
機能です。
non-graceful node shutdown機能を使ってみよう
まず、non-graceful node shutdown機能はver1.24ではα機能のためデフォルトでは無効になっています。有効にするには NodeOutOfServiceVolumeDetach
Feature Gateを有効にする必要があります。NodeOutOfServiceVolumeDetach
Feature Gateについては https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/#feature-gates-for-alpha-or-beta-features に書かれています。
準備ができたらさっそく動作確認してみましょう。
私の環境では以下のノードを用意しました。Controllerノード1台、Workerノード2台です。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
test-control-plane Ready control-plane 103s v1.24.0-beta.0.113+7380fc735aca59-dirty
test-worker Ready <none> 69s v1.24.0-beta.0.113+7380fc735aca59-dirty
test-worker2 Ready <none> 69s v1.24.0-beta.0.113+7380fc735aca59-dirty
CSIドライバは、私の環境ではNFSサーバを使っているのでNFS CSIドライバを使いました。NFS CSIドライバについてはこちら https://github.com/kubernetes-csi/csi-driver-nfs
動作確認に使うサンプルアプリも用意しましょう。NFS CSIドライバにはサンプルアプリが用意されているのでそれを使います。https://github.com/kubernetes-csi/csi-driver-nfs/blob/master/hack/verify-examples.sh を実行するとアプリが作成されます。
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/statefulset-nfs-0 1/1 Running 0 71s
NAME READY AGE
statefulset.apps/statefulset-nfs 1/1 71s
PVCも作成されています。
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistent-storage-statefulset-nfs-0 Bound pvc-e174d35e-0a83-4fbb-bf8f-28aa8ec8b7f8 10Gi RWO nfs-csi 109s
こちらのサンプルアプリは、1秒おきに現在時刻をファイルに出力するというものになっています。
$ more /home/yuiko/foo/pvc-e174d35e-0a83-4fbb-bf8f-28aa8ec8b7f8/outfile
Fri May 13 04:34:27 UTC 2022
Fri May 13 04:34:29 UTC 2022
Fri May 13 04:34:30 UTC 2022
podが動いているノードを確認しましょう。
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
statefulset-nfs-0 1/1 Running 0 2m48s 10.244.1.3 test-worker2 <none> <none>
test-worker2ですね。
私はKubernetes環境としてKind( https://kind.sigs.k8s.io/ )を使っているのですが、Kindのノードの正体はdockerコンテナなので、こんな感じでworkerノードを止めます。
$ docker stop test-worker2
test-worker2の状態がNotReadyになっていることが確認できました。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
test-control-plane Ready control-plane 10m v1.24.0-beta.0.113+7380fc735aca59-dirty
test-worker Ready <none> 9m30s v1.24.0-beta.0.113+7380fc735aca59-dirty
test-worker2 NotReady <none> 9m30s v1.24.0-beta.0.113+7380fc735aca59-dirty
podの状態もTerminatingになり、そのまま他のノードにevictしません、、
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
statefulset-nfs-0 1/1 Terminating 0 11m
ダウンしているtest-worker2ノードにout-of-service Taintを付与します。
$ kubectl taint nodes test-worker2 node.kubernetes.io/out-of-service=nodeshutdown:NoExecute
node/test-worker2 tainted
podがtest-workerノードに移動しました!
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
statefulset-nfs-0 1/1 Running 0 6s 10.244.2.4 test-worker <none> <none>
PVとPVCも確認してみましょう。STATUSが Bound
になっていますね。
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-e174d35e-0a83-4fbb-bf8f-28aa8ec8b7f8 10Gi RWO Delete Bound default/persistent-storage-statefulset-nfs-0 nfs-csi 1d
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistent-storage-statefulset-nfs-0 Bound pvc-e174d35e-0a83-4fbb-bf8f-28aa8ec8b7f8 10Gi RWO nfs-csi 1d
サンプルアプリの出力ファイルも見てみましょう。
$ tail /home/yuiko/foo/pvc-e174d35e-0a83-4fbb-bf8f-28aa8ec8b7f8/outfile
Fri May 13 05:15:38 UTC 2022
Fri May 13 05:15:39 UTC 2022
Fri May 13 05:15:40 UTC 2022
Fri May 13 05:21:56 UTC 2022 ←6分ほどファイル出力されていない
Fri May 13 05:21:57 UTC 2022
Fri May 13 05:21:58 UTC 2022
本来は1秒おきに現在時刻が出力されるはずですが、途中6分ほどファイル出力が行われていないことがわかります。
今後の予定
コミュニティでの議論の結果、ver1.24では手動でtaintをノードに付与することでノードからのボリュームデタッチが許可される、という方針で合意形成がされましたが、将来的にはこのtaintを自動で付与したいという話も出ています。
以下のKEPのセクションにこのように記載されています。
https://github.com/kubernetes/enhancements/tree/master/keps/sig-storage/2268-non-graceful-shutdown#goals
Once the feature in this KEP is developed and tested, we plan to work on automatic way of detecting and fencing node for shutdown/failure use cases and re-evaluating some approaches listed in the Alternatives section.
まとめ
今回はKubernetes ver1.24で新しく実装された「non-graceful node shutdown」機能を紹介しました。長年コミュニティで問題視されていたので、今回のリリースで恩恵を受ける方はたくさんいらっしゃると思います!ガンガン使ってコミュニティにもフィードバックを投げましょう!
おまけ
この件は、もともとは社内で改善要望が出たことがきっかけでコミュニティでの開発に参加したのですが、私が参加するだいぶ前から議論が行われており、他の企業でも改善要望が多かったことがうかがえます。Kubernetesを始めとする比較的大きいOSSコミュニティでは、新しい機能の実装を提案する場合はKEPと呼ばれる機能提案書のようなものを提出し、KEPが承認されれば開発に進むことができます。今回、KEPの承認に向けて議論はかなりの長期戦になり、とても大変でした、、。本件がsig-nodeとsig-storageの両方に関係するためステークホルダーが多いのもその原因かと思います。方向性の合意ができてKEPが無事承認された後は機能実装はトントン拍子に進み、ver1.24の開発期間に機能実装PRまで一気に完了しました!
参考
- Kubernetes Documentation https://kubernetes.io/docs/concepts/cluster-administration/node-shutdown/#non-graceful-node-shutdown
- Kubernetes blogでの紹介記事 https://kubernetes.io/blog/2022/05/20/kubernetes-1-24-non-graceful-node-shutdown-alpha/
- Kubernetes ver1.24リリースノート https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.24.md
- NECのOSS/Linuxへの取り組み紹介記事 https://jpn.nec.com/oss/community/OSSJ2021/OSSJ2021-1.html