はじめに
本記事では、Kubernetes v1.28にて GA となった Non Graceful Node Shutdownの動作検証を行います。
Kubernetesのノードをシャットダウンした際、このシャットダウンが正常に完了しなかった場合、Volumeなどが外れないことがあります。
このような状態になると、iSCSIなどのブロックストレージでは、既にRead/Writeが可能な状態でマウントされているノードがあると、他ノードにてRead/WriteにてVolumeのアタッチが出来なくなることがあります。
複数ノードから1つのVolumeをアタッチすることをMulti Attachと呼び、上記のエラーケースはMulti Attach エラーと呼ばれます。
このような状態になった場合に、復旧する手段として登場したのが Non Graceful Node Shutdownです。
この機能の注意点としては、自動復旧するものではないという点です。
ユーザがノードが正常にシャットダウンされていないと判断したのち、taint(node.kubernetes.io/out-of-service
)を付与してあげることで、この機能が実行されます。
なお、本機能のサポートは以下のバージョンからサポートされています
- Kubernetes v1.24: アルファ (デフォルト無効)
- Kubernetes v1.26: ベータ (デフォルト有効)
- Kubernetes v1.28: GA
検証環境
- Kubernetes v1.25.9, 1.26.4
- ノードOS: Ubuntu 20.04
- ストレージ: NetApp ONTAP AFF9.9 (iSCSIを利用)
検証
この検証では、Non Graceful Node Shutdownの挙動を確認するため、本機能が無効化されているKubernetesを使い本機能がない場合の挙動を検証します(検証1)。
その次に、本機能が有効化されているKubernetesを使い、本機能の挙動を確認します(検証2)。
[検証1] Non Graceful Node Shutdownが無効の場合 (Kubernetes v1.25以前)
まずNon Graceful Node Shutdownが無効の場合の挙動について示します。
ここではNon Graceful Node ShutdownのFeature Gates(NodeOutOfServiceVolumeDetach
)がデフォルトで無効となっているKubernetes v1.25を使い検証します。
始めに、StatefulSet(sts.yaml
)を使いPod,PVC,PVを作成します。
- sts.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: pv-test
spec:
serviceName: pv-test
replicas: 1
selector:
matchLabels:
app: pv-test
template:
metadata:
labels:
app: pv-test
spec:
containers:
- name: pv-test
image: ubuntu:20.04
volumeMounts:
- name: block
mountPath: /mnt/block
command:
- sleep
- infinity
volumeClaimTemplates:
- metadata:
name: block
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: ontap-block
resources:
requests:
storage: 10Gi
Manifest(sts.yaml
)をデプロイし、Pod,PVC,PVを確認します。
$ kubectl apply -f sts.yaml
statefulset.apps/pv-test created
$ kubectl get pod,pvc,pv -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/pv-test-0 1/1 Running 0 55s 10.26.107.43 demo-ysakashita-w-default-a7102877-mmh9d <none> <none>
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE VOLUMEMODE
persistentvolumeclaim/block-pv-test-0 Bound pvc-adc68176-5517-4274-85fa-33d5dfcd81c2 10Gi RWO ontap-block 55s Filesystem
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE VOLUMEMODE
persistentvolume/pvc-adc68176-5517-4274-85fa-33d5dfcd81c2 10Gi RWO Delete Bound default/block-pv-test-0 ontap-block 52s Filesystem
次に、Podがデプロイされているノード(demo-ysakashita-w-default-a7102877-mmh9d
)にsshで入り、OS Panicを発生させます。
ubuntu@demo-ysakashita-w-default-a7102877-mmh9d:~$ sudo bash
root@demo-ysakashita-w-default-a7102877-mmh9d:/home/ubuntu# vi /etc/sysctl.conf
root@demo-ysakashita-w-default-a7102877-mmh9d:/home/ubuntu# sysctl -p
kernel.panic = 0
root@demo-ysakashita-w-default-a7102877-mmh9d:/home/ubuntu# echo c > /proc/sysrq-trigger
OS Panicが発生した後、ノードのステータスを確認します。
$ kubectl get node
NAME STATUS ROLES AGE VERSION
...
demo-ysakashita-w-default-a7102877-mmh9d NotReady <none> 176m v1.25.9
該当のノードがNotReadyとなりました。 しばらく待ち、Podのステータスも確認します.
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pv-test-0 1/1 Terminating 0 10m 10.26.107.43 demo-ysakashita-w-default-a7102877-mmh9d <none> <none>
10分以上経ってもTerminatingのままとなります。
この状態で、ノードにtaint (node.kubernetes.io/out-of-service=nodeshutdown:NoExecute
)を付与します。
Kubernetes v1.25では、Non Graceful Node Shutdownが無効のため、意味がないことを確認します。
$ kubectl taint node demo-ysakashita-w-default-a7102877-mmh9d "node.kubernetes.io/out-of-service=nodeshutdown:NoExecute"
node/demo-ysakashita-w-default-a7102877-mmh9d tainted
taintを付与した後、20分放置しましたがPodのステータスはTerminatingのままで復旧はしませんでした。 想定通りの挙動です。
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pv-test-0 1/1 Terminating 0 30m 10.26.107.43 demo-ysakashita-w-default-a7102877-mmh9d <none> <none>
[検証2] Non Graceful Node Shutdownが有効の場合 (Kubernetes v1.26以前)
Kubernetes v1.26にてNon Graceful Node ShutdownのFeature Gates(NodeOutOfServiceVolumeDetach
)がベータとなり、デフォルト有効となりました。
そこで、Kubernetes v1.26を使い Non Graceful Node Shutdown の検証を行います。
上記の検証で用いたsts.yaml
を使いPod,PVC,PVを作成します。
$ kubectl apply -f sts.yaml
statefulset.apps/pv-test created
$ kubectl get pod,pvc,pv -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/pv-test-0 1/1 Running 0 47s 10.26.26.23 demo-ysaka-net-w-default-4282754a-px2wm <none> <none>
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE VOLUMEMODE
persistentvolumeclaim/block-pv-test-0 Bound pvc-e5b39376-33bb-44af-8753-85a2bba44a6a 10Gi RWO ontap-block 47s Filesystem
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE VOLUMEMODE
persistentvolume/pvc-e5b39376-33bb-44af-8753-85a2bba44a6a 10Gi RWO Delete Bound default/block-pv-test-0 ontap-block 45s Filesystem
次に、Podがデプロイされているノード(demo-ysaka-net-w-default-4282754a-px2wm
)にsshで入り、OS Panicを起こします。
ubuntu@demo-ysaka-net-w-default-4282754a-px2wm:~$ sudo bash
root@demo-ysaka-net-w-default-4282754a-px2wm:/home/ubuntu# vi /etc/sysctl.conf
root@demo-ysaka-net-w-default-4282754a-px2wm:/home/ubuntu# sysctl -p
kernel.panic = 0
root@demo-ysaka-net-w-default-4282754a-px2wm:/home/ubuntu# echo c > /proc/sysrq-trigger
ノードのステータスを確認します。
$ kubectl get node
NAME STATUS ROLES AGE VERSION
...
demo-ysaka-net-w-default-4282754a-px2wm NotReady <none> 3h52m v1.26.4
該当のNodeがNotReadyとなっていることを確認しました。 しばらく待ち、Podの状態を確認します。
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pv-test-0 1/1 Terminating 0 8m43s 10.26.26.23 demo-ysaka-net-w-default-4282754a-px2wm <none> <none>
この状態で、先の検証と同様にノードにtaint(node.kubernetes.io/out-of-service=nodeshutdown:NoExecute
)を付与します。
$ kubectl taint node demo-ysaka-net-w-default-4282754a-px2wm "node.kubernetes.io/out-of-service=nodeshutdown:NoExecute"
node/demo-ysaka-net-w-default-4282754a-px2wm tainted
taintを付与後、すぐに別のノードにスケジューリングされPodがRunningになり復旧しました。
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pv-test-0 1/1 Running 0 30s 10.26.99.39 demo-ysaka-net-w-default-4282754a-4s68h <none> <none>
$ kubectl describe pod pv-test-0
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 102s default-scheduler Successfully assigned default/pv-test-0 to demo-ysaka-net-w-default-4282754a-4s68h
Normal SuccessfulAttachVolume 102s attachdetach-controller AttachVolume.Attach succeeded for volume "pvc-e5b39376-33bb-44af-8753-85a2bba44a6a"
Normal Pulled 97s kubelet Container image "ubuntu:20.04" already present on machine
Normal Created 97s kubelet Created container pv-test
Normal Started 97s kubelet Started container pv-test
感想
本記事では、Non Graceful Node Shutdownの検証を行いました。
この機能が登場する以前は、シャットダウンが正しく行われなかったノードに入りVolumeを強制アンマウントやiSCSIなどストレージとのセッションのログアウトを行う必要がありました。
ただし、シャットダウンが正しく行われないノードは、何らかの異常状態に陥っているため、そもそもSSHなどでノードに入ることができない場合もあり、この手段が取れないことも多々あります。
そのような場合は、iSCSIのセッションタイムアウトまで待つか、ストレージ側でセッションを強制的に切断しなければなりませんでした。
このように、ノードが正しくシャットダウンできなかった場合の復旧手順は複雑かつノードのroot権限やストレージの管理者の権限が必要となるなど実施できる管理者も限られていました。
今回検証したNon Graceful Node Shutdownでは、ノードが正しくシャットダウンできなかった場合でも Kubernetesのノードにtaintを設定できる権限をもった管理者であれば、比較的容易に復旧させることができます。
ただし、注意しなければならないのは、taintを手動で設定する必要がある点です。
シャットダウンが正しく行われたかどうかを正確に判断することは容易ではありません。
そのため、自動で復旧することは難しく管理者が「正しくシャットダウンがされていない」と判断したノードに対してtaintを設定することで復旧させるように設計されています。
つまり、管理者は「正しくシャットダウンされいない」ことを検知できるようにしておく必要があります。
簡単なやり方としては、ノードのステータスがReady
でなくなってから、ある程度長い時間(正しいシャットダウンではあり得ない時間)停止してしまっていることを検知する方法です。
Non Graceful Node Shutdownは障害発生時に素早く復旧するために有効な手段ですので使い方を理解するとともに、この機会に障害検出から復旧までが短時間で行われるよう運用方法を確認することをお勧めします。