はじめに
Kubernetes v1.19まではボリュームがPodにマウントされた後、ボリュームのステータスを監視する方法がありませんでした。
そこで、KubernetesのSIG Storageでは、KEP-1432にてVolume Health Monitorが提案され、Kubernetes v1.19からAlpha機能として追加、v1.21でSecond Alphaとしてアップデートされています。CSIの仕様としてはCSI v1.3.0から登場しています。
ボリュームを提供するストレージやネットワークやノード自体の障害により、KubernetesでPodがマウントしているにもボリュームが利用できなくなっている場合があります。
このような場合、Kubernetesでボリュームを示すリソースであるPV(PersistentVolume)およびPVC(PersistentVolumeClaim)のステータスでは判断できず、Podの中のコンテナで動作するアプリケーションにてRead/Writeを実行したことで始めて障害に気がつくことがあります。
もしかすると、頻繁にRead/Writeが発生するアプリケーションであれば、すぐに障害に気がつくかもしれません。しかし、稀にしかRead/Writeが発生しないアプリケーションの場合は、障害に気がつかないままとなっている場合があります。
そこで、ボリュームの状態を定期的に監視するための機能としてVolume Health Monitorが開発されています。
2021/4時点のVolume Health Monitorでは、イベントとログでの報告のみですが、将来的には自動復旧する方法も目指しているとのことです。
[障害の例]
- Kubernetesの外部でボリュームが誤って削除される可能性があります。
- ボリュームが存在するディスクは、メンテナンスのために一時的に取り外すことができます。
- ボリュームが存在するディスクに障害が発生する可能性があります。
- ボリュームの状態に影響を与える、基盤となるストレージシステムの構成の問題がある可能性があります。
- ボリュームが容量不足になっている可能性があります。
- ディスクが劣化している可能性があり、パフォーマンスに影響します。
- 読み取り/書き込みI/Oエラーが発生する可能性があります。
- ボリューム上のファイルシステムが破損している可能性があります。
- ファイルシステムの容量が不足している可能性があります。
- Kubernetesの外部で誤ってボリュームがアンマウントされる可能性があります。
アーキテクチャ
Volume Health Monitorは、kubernetes-csi/external-health-monitorのexternal-health-monitor-controllerとexternal-health-monitor-agentの2つをサイドカーコンテナとしてCSI Driverとあわせてデプロイします。
external-health-monitor-controller
CSI controller driverのサイドカーコンテナとしてデプロイします。
CSI controller driverのListVolumes, ControllerGetVolumeを定期的に呼び出し、ボリュームの状態をチェックします。
各チェックの実装は以下を参照下さい。
- ListVolumes
- ControllerGetVolume
external-health-monitor-agent
CSI node driverのサイドカーコンテナとしてデプロイします。
CSI node driverのNodeGetVolumeStatsを定期的に呼び出し、ボリュームの状態をチェックします。
各チェックの実装は以下を参照ください。
- NodeGetVolumeStats
CSI Driver(csi-driver-host-path)におけるボリュームの状態チェック
上記に述べたようにexternal-health-monitorの2つのコンテナにより、CSI DriverのListVolumes, ControllerGetVolume, NodeGetVolumeStatsのAPIが定期的に呼び出されボリュームの状態がチェックされます。
これらのAPIでのボリュームの状態チェック方法やチェック項目については、各CSI Driverによって異なります。
以下に、本検証で利用するCSI Driverのサンプル実装として参照されることの多いcsi-driver-host-pathでの実装箇所を以下に記します。どの項目をどのようにチェックしているかは実装を参照してください。
- ListVolumes() -> doHealthCheckInControllerSide()
- ControllerGetVolume() ->doHealthCheckInControllerSide()
- NodeGetVolumeStats() -> doHealthCheckInNodeSide
external-health-monitor-controllerコンテナから呼び出されるListVolumesとControllerGetVolumeは同じチェックとなっています。
動作検証
Volume Health Monitorの動作検証を行います。
検証環境
- minikube v1.19.0
- Kubernetes v1.21.0
- CSI Driver: csi-driver-host-path
検証内容
minikubeを使いKubernetes v1.21.0を--feature-gates=CSIVolumeHealth=true
を指定し立ち上げます。
Feature GatesでCSIVolumeHealthを有効にすることで、Volume Health Monitorでチェックされたボリュームの状態をイベントとして報告されるようになります。
$ minikube start --vm-driver=virtualbox --kubernetes-version=1.21.0 --feature-gates=CSIVolumeHealth=true
minikubeによりKubernetes v1.21.0が起動したことを確認します。
次に、Volume Health Monitorに対応しているcsi-driver-host-pathをセットアップします。
$ git clone git@github.com:kubernetes-csi/csi-driver-host-path.git
$ cd csi-driver-host-path/
$ deploy/kubernetes-latest/deploy.sh
...
csidriver.storage.k8s.io/hostpath.csi.k8s.io created
/Users/ysakashi/code/golang/src/github.com/kubernetes-csi/csi-driver-host-path/deploy/kubernetes-latest/hostpath/csi-hostpath-plugin.yaml
using image: k8s.gcr.io/sig-storage/csi-external-health-monitor-agent:v0.2.0
using image: k8s.gcr.io/sig-storage/csi-external-health-monitor-controller:v0.2.0
...
error: unable to recognize "/Users/ysakashi/code/golang/src/github.com/kubernetes-csi/csi-driver-host-path/deploy/kubernetes-latest/snapshotter/csi-hostpath-snapshotclass.yaml": no matches for kind "VolumeSnapshotClass" in version "snapshot.storage.k8s.io/v1"
VolumeSnapshotClassでエラーが出ますが、今回はVolumeSnapshot関連のリソースをデプロイしていないため、無視します。
上記のデプロイ時の出力をみると、csi-external-health-monitor-agent:v0.2.0
とcsi-external-health-monitor-controller:v0.2.0
がcsi-hostpath-pluginのサイドカーコンテナとしてデプロイされていることがわかります。(csi-hostpath-pluginはCSI controller driverとCSI node driverの両方を備え持つStatefulSetです)
csi-driver-host-pathのPodが起動していることを確認します。
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
csi-hostpath-attacher-0 1/1 Running 0 3m20s
csi-hostpath-provisioner-0 1/1 Running 0 3m17s
csi-hostpath-resizer-0 1/1 Running 0 3m16s
csi-hostpath-snapshotter-0 1/1 Running 0 3m15s
csi-hostpath-socat-0 1/1 Running 0 3m15s
csi-hostpathplugin-0 5/5 Running 0 3m18s
次に、StorageClassを作成します。
$ kubectl apply -f examples/csi-storageclass.yaml
storageclass.storage.k8s.io/csi-hostpath-sc created
$ kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
csi-hostpath-sc hostpath.csi.k8s.io Delete Immediate true 3m32s
standard (default) k8s.io/minikube-hostpath Delete Immediate false 24m
PVC/PVを作成しPodにマウントします。
ここでは、exampleディレクトリ以下に用意されているサンプルを利用します。
$ kubectl apply -f examples/csi-pvc.yaml -f examples/csi-app.yaml
persistentvolumeclaim/csi-pvc created
pod/my-csi-app created
$ kubectl get pod,pvc,pv
NAME READY STATUS RESTARTS AGE
...
pod/my-csi-app 1/1 Running 0 16s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/csi-pvc Bound pvc-63fd7293-c543-4c83-8f65-01aacb098ab3 1Gi RWO csi-hostpath-sc 16s
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-63fd7293-c543-4c83-8f65-01aacb098ab3 1Gi RWO Delete Bound default/csi-pvc csi-hostpath-sc 16s
Pod,PVC,PVが正しく起動していることを確認した後、kubectl describe
でPodのイベントを確認します。
$ kubectl describe pod my-csi-app
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
...
Normal VolumeConditionNormal 16s (x10 over 16s) csi-pv-monitor-agent-hostpath.csi.k8s.io The Volume returns to the healthy state
すると、Volume Health Monitorによりチェックされたボリュームの状態のイベントがあがっているのを確認できます。
このボリュームの状態のイベントのTypeはNormal
, ReasonはVolumeConditionNormal
, MessageはThe Volume returns to the healthy state
となっており、ボリュームの状態が健全であることがわかります。
では、ここでボリュームに障害を発生させます。
今回は、 csi-driver-host-pathのNodeGetVolumeStatsにて実装されているケースを試します。本ケースは上記で述べた「ボリュームが存在するディスクは、メンテナンスのために一時的に取り外すことができます。」「Kubernetesの外部で誤ってボリュームがアンマウントされる可能性があります。」を想定したチェック項目になります。
ボリュームのマウントを外すため、ノードにログインし作業します。
$ minikube ssh
_ _
_ _ ( ) ( )
___ ___ (_) ___ (_)| |/') _ _ | |_ __
/' _ ` _ `\| |/' _ `\| || , < ( ) ( )| '_`\ /'__`\
| ( ) ( ) || || ( ) || || |\`\ | (_) || |_) )( ___/
(_) (_) (_)(_)(_) (_)(_)(_) (_)`\___/'(_,__/'`\____)
# rootで作業します
$ sudo bash
# マウントパスを調べます
$ mount |grep pvc
tmpfs on /var/lib/kubelet/pods/0ed3b369-d5a6-4b99-b489-1bb651710c60/volumes/kubernetes.io~csi/pvc-63fd7293-c543-4c83-8f65-01aacb098ab3/mount type tmpfs (rw,relatime,size=5357156k)
tmpfs on /mnt/sda1/var/lib/kubelet/pods/0ed3b369-d5a6-4b99-b489-1bb651710c60/volumes/kubernetes.io~csi/pvc-63fd7293-c543-4c83-8f65-01aacb098ab3/mount type tmpfs (rw,relatime,size=5357156k)
# マウントを外します
$ umount /mnt/sda1/var/lib/kubelet/pods/0ed3b369-d5a6-4b99-b489-1bb651710c60/volumes/kubernetes.io~csi/pvc-63fd7293-c543-4c83-8f65-01aacb098ab3/mount
# ノードからログアウトします
$ exit
exit
$ exit
logout
ノードにて、Podからマウントされているボリュームのマウントを外しました。
次に、PodにてVolume Health Monitorによるイベントを確認します。
$ kubectl describe pod my-csi-app
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
...
Normal VolumeConditionNormal 5m53s (x141 over 19m) csi-pv-monitor-agent-hostpath.csi.k8s.io The Volume returns to the healthy state
Warning VolumeConditionAbnormal 53s (x11 over 20m) csi-pv-monitor-agent-hostpath.csi.k8s.io The volume isn't mounted
その結果、Warning
, VolumeConditionAbnormal
,The volume isn't mounted
のボリュームの異常を示すイベントがあがっているのが確認できました。
ログからも確認します。
csi-driver-host-pathにて、csi-external-health-monitorをサイドカーで持つPod(csi-hostpathplugin-0
)のログを確認します。
$ kubectl logs csi-hostpathplugin-0 csi-external-health-monitor-agent
...
I0420 09:39:53.317402 1 connection.go:182] GRPC call: /csi.v1.Node/NodeGetVolumeStats
I0420 09:39:53.317429 1 connection.go:183] GRPC request: {"staging_target_path":"/var/lib/kubelet/plugins/kubernetes.io/csi/pv/pvc-63fd7293-c543-4c83-8f65-01aacb098ab3/globalmount","volume_id":"47f67155-a1b9-11eb-8b74-0242ac110003","volume_path":"/var/lib/kubelet/pods/0ed3b369-d5a6-4b99-b489-1bb651710c60/volumes/kubernetes.io~csi/pvc-63fd7293-c543-4c83-8f65-01aacb098ab3/mount"}
I0420 09:39:53.320345 1 connection.go:185] GRPC response: {"usage":[{"available":5485727744,"total":706768896,"unit":1,"used":4778958848}],"volume_condition":{"abnormal":true,"message":"The volume isn't mounted"}}
I0420 09:39:53.320410 1 connection.go:186] GRPC error: <nil>
csi-hostpathplugin-0 のサイドカーコンテナcsi-external-health-monitor-agentからNodeGetVolumeStatsが呼ばれ、volume_condition":{"abnormal":true,"message":"The volume isn't mounted"
が返っていることが確認できます。
感想
Volume Health Monitorは、Kubernetes v1.21時点ではAlpha機能ですが、PVC/PVを安心・安全に運用するために重要なポジションになる機能かと睨んでいます。Volume Health Monitorでは、Kubernetesのノード内のボリュームの状態を定期的に監視することで、障害を未然に防ぐことができるポテンシャルを備えています。まだ、2021/4時点の機能では、イベントやログにボリュームの状態が出力されるだけです。そのため、ボリュームの異常を示すVolumeConditionAbnormal
のイベントをピックアップしアラートを生成するなどの設定を行い検知できるようにすると共に、障害発生時には管理者が手動で対応する必要があります。ただし、KEP-1432でも一言だけですが述べられているように、将来的にはボリュームの異常状態を検知し自動復旧するための、最初の一歩になる重要な機能です。ノードでのボリュームに関する障害について自動復旧できるようになり、ストレージに自信のない管理者でも安心・安全に運用できる世界が来ることを予見させる機能であり、今後のVolume Health Monitorの成長が楽しみです。