はじめに
本ドキュメントでは、Kubernetes v1.30: Uwubernetesで紹介されたPrevent unauthorized volume mode conversion during volume restoreについて動作検証を行います。
まず、PVにはVolumeModeが存在します。
このVolumeModeはPV(Volume)を作成する際にCSIドライバで設定しているファイルシステム(ext4など)でフォーマットするFilesystem
か、フォーマットしないRaw Volumeとして作成するBlock
を指定するパラメータがあります。
本機能は、VolumeSnapshotからリストアする際に元のVolumeで指定されているVolumeModeと異なるVolumeModeが指定された場合に、リストアを抑止する機能となります。
例えば、元のVolumeのVolumeModeがFilesystem
で設定されている状態で、リストアする際に間違ってVolumeModeをBlock
にしてしまうといったオペミスを防ぎます。
検証環境
- minikube version: v1.33.0
- Kubernetes v1.30.0
検証環境の構築
本検証では、各種設定やバージョンアップが必要となるため、事前に環境を準備します。
まず、minikubeでKubernetes v1.30.0を立ち上げます。
$ minikube start --kubernetes-version=v1.30.0
...
🏄 終了しました!kubectl がデフォルトで「minikube」クラスターと「default」ネームスペースを使用するよう設定されました
VolumeSnapshotが利用できるように、Addonsのcsi-hostpath-driver
とvolumesnapshots
を有効にします。
$ minikube addons enable csi-hostpath-driver
...
🌟 'csi-hostpath-driver' アドオンが有効です
$ minikube addons enable volumesnapshots
...
🌟 'volumesnapshots' アドオンが有効です
kube-system
ネームスペースにsnapshot-controller-XX
とcsi-hostpathXXX
のPodがデプロイされRunningになっていることを確認します。
$ kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-7db6d8ff4d-jh2bq 1/1 Running 0 5m32s
csi-hostpath-attacher-0 1/1 Running 0 2m51s
csi-hostpath-resizer-0 1/1 Running 0 2m51s
csi-hostpathplugin-fpc27 6/6 Running 0 2m51s
etcd-minikube 1/1 Running 0 5m48s
kube-apiserver-minikube 1/1 Running 0 5m48s
kube-controller-manager-minikube 1/1 Running 0 5m48s
kube-proxy-qxnzh 1/1 Running 0 5m32s
kube-scheduler-minikube 1/1 Running 0 5m46s
snapshot-controller-745499f584-jgpj5 1/1 Running 0 45s
snapshot-controller-745499f584-m4trn 1/1 Running 0 45s
storage-provisioner 1/1 Running 2 (5m30s ago) 5m44s
StorageClassとVolumeSnapshotClassを確認します。
$ kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
csi-hostpath-sc hostpath.csi.k8s.io Delete Immediate false 6m40s
...
$ kubectl get volumesnapshotclass
NAME DRIVER DELETIONPOLICY AGE
csi-hostpath-snapclass hostpath.csi.k8s.io Delete 4m57s
以降の検証では、StorageClass(csi-hostpath-sc
)とVolumeSnapshotClass(csi-hostpath-snapclass
)を使います。
Prevent Unauthorised Volume Mode Conversionでは、csi-provisioner(external-provisioner)がv4.0.0以降, csi-snapshotter(external-snapshotter)がv7.0.0以降となっている必要があるため、これらのバージョンを確認します。
$ kubectl images -n kube-system csi-hostpathplugin
[Summary]: 1 namespaces, 1 pods, 6 containers and 6 different images
+--------------------------+----------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| Pod | Container | Image |
+--------------------------+----------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| csi-hostpathplugin-fpc27 | csi-external-health-monitor-controller | registry.k8s.io/sig-storage/csi-external-health-monitor-controller:v0.7.0@sha256:80b9ba94aa2afe24553d69bd165a6a51552d1582d68618ec00d3b804a7d9193c |
+ +----------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| | node-driver-registrar | registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.6.0@sha256:f1c25991bac2fbb7f5fcf91ed9438df31e30edee6bed5a780464238aa09ad24c |
+ +----------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| | hostpath | registry.k8s.io/sig-storage/hostpathplugin:v1.9.0@sha256:92257881c1d6493cf18299a24af42330f891166560047902b8d431fb66b01af5 |
+ +----------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| | liveness-probe | registry.k8s.io/sig-storage/livenessprobe:v2.8.0@sha256:cacee2b5c36dd59d4c7e8469c05c9e4ef53ecb2df9025fa8c10cdaf61bce62f0 |
+ +----------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| | csi-provisioner | registry.k8s.io/sig-storage/csi-provisioner:v3.3.0@sha256:ee3b525d5b89db99da3b8eb521d9cd90cb6e9ef0fbb651e98bb37be78d36b5b8 |
+ +----------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| | csi-snapshotter | registry.k8s.io/sig-storage/csi-snapshotter:v6.1.0@sha256:291334908ddf71a4661fd7f6d9d97274de8a5378a2b6fdfeb2ce73414a34f82f |
+--------------------------+----------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------
確認にはkubectlのpluginのkubectl-imagesを使用しています。
バージョンが古いため、csi-provisionerとcsi-snapshotterをバージョンアップします。
$ kubectl edit ds csi-hostpathplugin
# csi-provisionerとcsi-snapshotterのimageを変更します
$ kubectl images -n kube-system csi-hostpathplugin
[Summary]: 1 namespaces, 1 pods, 6 containers and 6 different images
+--------------------------+----------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| Pod | Container | Image |
+--------------------------+----------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| csi-hostpathplugin-89bxj | csi-external-health-monitor-controller | registry.k8s.io/sig-storage/csi-external-health-monitor-controller:v0.7.0@sha256:80b9ba94aa2afe24553d69bd165a6a51552d1582d68618ec00d3b804a7d9193c |
+ +----------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| | node-driver-registrar | registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.6.0@sha256:f1c25991bac2fbb7f5fcf91ed9438df31e30edee6bed5a780464238aa09ad24c |
+ +----------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| | hostpath | registry.k8s.io/sig-storage/hostpathplugin:v1.9.0@sha256:92257881c1d6493cf18299a24af42330f891166560047902b8d431fb66b01af5 |
+ +----------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| | liveness-probe | registry.k8s.io/sig-storage/livenessprobe:v2.8.0@sha256:cacee2b5c36dd59d4c7e8469c05c9e4ef53ecb2df9025fa8c10cdaf61bce62f0 |
+ +----------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| | csi-provisioner | registry.k8s.io/sig-storage/csi-provisioner:v4.0.0 |
+ +----------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
| | csi-snapshotter | registry.k8s.io/sig-storage/csi-snapshotter:v7.0.0 |
+--------------------------+----------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------+
次に、本機能が有効となるのはsnapshot-controller v6.0.1以降となるため、これのバージョンも確認します。
$ kubectl images -n kube-system snapshot-controller
[Summary]: 1 namespaces, 2 pods, 2 containers and 1 different images
+--------------------------------------+----------------------------+--------------------------------------------------------------------------------------------------------------------------------+
| Pod | Container | Image |
+--------------------------------------+----------------------------+--------------------------------------------------------------------------------------------------------------------------------+
| snapshot-controller-745499f584-jgpj5 | volume-snapshot-controller | registry.k8s.io/sig-storage/snapshot-controller:v6.1.0@sha256:823c75d0c45d1427f6d850070956d9ca657140a7bbf828381541d1d808475280 |
+--------------------------------------+ + +
| snapshot-controller-745499f584-m4trn | | |
+--------------------------------------+----------------------------+--------------------------------------------------------------------------------------------------------------------------------+
v6.1.0となっているため、バージョンはこのままとします。
次に、VolumeSnapshotContentsのCRDを確認します。
具体的には、本機能でキモとなるVolumeSnapshotContentsのspec.sourceVolumeMode
が定義されているかを確認します。
筆者は、CRDの変更に気が付かずハマりました
$ kubectl get crd volumesnapshotcontents.snapshot.storage.k8s.io -o yaml |grep -i sourceVolumeMode
$
minikubeのaddonsでセットアップされたCRDでは、spec.sourceVolumeMode
がなかったため、CRDをアップデートします。
$ git clone git@github.com:kubernetes-csi/external-snapshotter.git
$ cd external-snapshotter
$ git checkout v7.0.0
$ kubectl apply -k client/config/crd/
customresourcedefinition.apiextensions.k8s.io/volumesnapshotclasses.snapshot.storage.k8s.io configured
customresourcedefinition.apiextensions.k8s.io/volumesnapshotcontents.snapshot.storage.k8s.io configured
customresourcedefinition.apiextensions.k8s.io/volumesnapshots.snapshot.storage.k8s.io configured
spec.sourceVolumeMode
の定義を確認します。
$ kubectl get crd volumesnapshotcontents.snapshot.storage.k8s.io -o yaml |grep -i sourceVolumeMode
...
sourceVolumeMode:
description: SourceVolumeMode is the mode of the volume whose snapshot
正しくspec.sourceVolumeMode
が定義されていることを確認しました。
念の為、csi-hostpathplugin, snapshot-controller を再起動しておきます。
$ kubectl rollout restart deployment -n kube-system snapshot-controller
deployment.apps/snapshot-controller restarted
$ kubectl rollout restart ds -n kube-system csi-hostpathplugin
daemonset.apps/csi-hostpathplugin restarted
また、本機能はsnapshot-validation-webhookも利用するため、snapshot-validation-webhookをデプロイします。
なお、今回は検証目的のためdefault
ネームスペースにデプロイします。
先程、git clone したexternal-snapshotterのディレクトリで作業します。
$ ./deploy/kubernetes/webhook-example/create-cert.sh --service snapshot-validation-service --secret snapshot-validation-secret --namespace default
$ cat ./deploy/kubernetes/webhook-example/admission-configuration-template | ./deploy/kubernetes/webhook-example/patch-ca-bundle.sh > ./deploy/kubernetes/webhook-example/admission-configuration.yaml
$ kubectl apply -f ./deploy/kubernetes/webhook-example
snapshot-validation-webhook のバージョンもv7.0.0以降であるかを確認します。
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
snapshot-validation-deployment-5575887fb-696lz 1/1 Running 0 35s
snapshot-validation-deployment-5575887fb-cfn84 1/1 Running 0 35s
snapshot-validation-deployment-5575887fb-qfchm 1/1 Running 0 35s
$ kubectl images snapshot-validation
[Summary]: 1 namespaces, 3 pods, 3 containers and 1 different images
+------------------------------------------------+---------------------+----------------------------------------------------------------+
| Pod | Container | Image |
+------------------------------------------------+---------------------+----------------------------------------------------------------+
| snapshot-validation-deployment-5575887fb-696lz | snapshot-validation | registry.k8s.io/sig-storage/snapshot-validation-webhook:v6.3.1 |
+------------------------------------------------+ + +
| snapshot-validation-deployment-5575887fb-cfn84 | | |
+------------------------------------------------+ + +
| snapshot-validation-deployment-5575887fb-qfchm | | |
+------------------------------------------------+---------------------+----------------------------------------------------------------+
snapshot-validation-webhookのバージョンをv7.0.0にアップデートします。
$ kubectl images snapshot-validation
[Summary]: 1 namespaces, 3 pods, 3 containers and 1 different images
+-------------------------------------------------+---------------------+----------------------------------------------------------------+
| Pod | Container | Image |
+-------------------------------------------------+---------------------+----------------------------------------------------------------+
| snapshot-validation-deployment-798dc7fcb9-569rr | snapshot-validation | registry.k8s.io/sig-storage/snapshot-validation-webhook:v7.0.0 |
+-------------------------------------------------+ + +
| snapshot-validation-deployment-798dc7fcb9-d27t8 | | |
+-------------------------------------------------+ + +
| snapshot-validation-deployment-798dc7fcb9-w66fx | | |
+-------------------------------------------------+---------------------+----------------------------------------------------------------+
次に、本機能を有効にするためにはsnapshot-controller, external-provisioner(csi-provisioner), snapshot-validation-webhookの起動オプションに--prevent-volume-mode-conversion=true
を追加する必要があるため、これらを設定します。
筆者は本オプションに気が付かず再びハマりました
# 以下でそれぞれのargsに--prevent-volume-mode-conversion=trueを付与
$ kubectl edit deployments -n kube-system snapshot-controller
$ kubectl edit deployments -n kube-system snapshot-controller
$ kubectl edit deployments snapshot-validation-deployment
以上で本機能の検証環境の準備が完了しました。
動作検証
まずはVolumeSnapshotの元となるPVCを作成します。
作成するPVCのManifestは以下です。
使用するStorageClassは事前準備で確認したcsi-hostpath-sc
を使います。
- pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-pv
spec:
storageClassName: csi-hostpath-sc
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
pvc.yamlをデプロイし、PVC/PVの作成を確認します。
$ kubectl apply -f pvc.yaml
persistentvolumeclaim/test-pv created
$ kubectl get pvc,pv -o wide
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE VOLUMEMODE
persistentvolumeclaim/test-pv Bound pvc-2cd519af-bb9f-4758-a4d0-e01944d8e75c 1Gi RWO csi-hostpath-sc <unset> 2m40s Filesystem
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE VOLUMEMODE
persistentvolume/pvc-2cd519af-bb9f-4758-a4d0-e01944d8e75c 1Gi RWO Delete Bound default/test-pv csi-hostpath-sc <unset> 2m25s Filesystem
PVC/PVが作成されVolumeModeがFilesystem
になっていることが確認できます。
なお、このVolumeModeは、ファイルシステム(ext4など)でフォーマットされたVolumeであることを示すものであり、ブロックストレージ(iSCSIなど)/ファイルストレージ(NFSなど)の区別とは関係ありません。
次に、作成したPVC/PVを対象にVolumeSnapshotを作成します。
VolumeSnapshotのManifestは以下です。
使用するVolumeSnapshotClassはcsi-hostpath-snapclass
です。
- volumesnapshot.yaml
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
name: snapshot-test1
spec:
volumeSnapshotClassName: csi-hostpath-snapclass
source:
persistentVolumeClaimName: test-pv
volumesnapshot.yamlをデプロイした後、VolumeSnapshot/VolumeSnapshotContentを確認します。
$ kubectl apply -f volumesnapshot.yaml
volumesnapshot.snapshot.storage.k8s.io/snapshot-test1 created
$ kubectl get volumesnapshot,volumesnapshotcontent
NAME READYTOUSE SOURCEPVC SOURCESNAPSHOTCONTENT RESTORESIZE SNAPSHOTCLASS SNAPSHOTCONTENT CREATIONTIME AGE
volumesnapshot.snapshot.storage.k8s.io/snapshot-test1 true test-pv 1Gi csi-hostpath-snapclass snapcontent-72f81d88-319d-4465-b2d1-395dc31e7668 40s 40s
NAME READYTOUSE RESTORESIZE DELETIONPOLICY DRIVER VOLUMESNAPSHOTCLASS VOLUMESNAPSHOT VOLUMESNAPSHOTNAMESPACE AGE
volumesnapshotcontent.snapshot.storage.k8s.io/snapcontent-72f81d88-319d-4465-b2d1-395dc31e7668 true 1073741824 Delete hostpath.csi.k8s.io csi-hostpath-snapclass snapshot-test1 default 40s
問題なくVolumeSnapshot, VolumeSnapshotContentが作成できています。
作成されたVolumeSnapshotContent(snapcontent-72f81d88-319d-4465-b2d1-395dc31e7668
)を確認します。
$ kubectl get volumesnapshotcontents snapcontent-72f81d88-319d-4465-b2d1-395dc31e7668 -o yaml
...
spec:
sourceVolumeMode: Filesystem
...
spec.sourceVolumeMode
が正しく設定されていることが確認できます。
もし、古いコントローラを利用し作成したVolumeSnapshotContentがあり本パラメータが存在していない場合には、kubernetes.ioのドキュメントによるとkubectl edit などを使い手動で追加しても問題ないようです。
このVolumeSnapshotからリストアを試します。
まずは、同じVolumeMode(Filesystem
)のPVCを使いリストアを試します。
- restore-pvc.yaml
kind: PersistentVolumeClaim
metadata:
name: restore-pv-filesystem
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
storageClassName: csi-hostpath-sc
resources:
requests:
storage: 1Gi
dataSource:
apiGroup: snapshot.storage.k8s.io
kind: VolumeSnapshot
name: snapshot-test1
restore-pvc.yaml をデプロイしPVCを確認します。
$ kubectl get pvc -o wide
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE VOLUMEMODE
restore-pv-filesystem Bound pvc-bae61ab6-9769-4746-a0f2-91fa653e3074 1Gi RWO csi-hostpath-sc <unset> 8s Filesystem
test-pv Bound pvc-2cd519af-bb9f-4758-a4d0-e01944d8e75c 1Gi RWO csi-hostpath-sc <unset> 23m Filesystem
問題なくリストアされたPVC/PV(restore-pv-filesystem/pvc-bae61ab6-9769-4746-a0f2-91fa653e3074)が作成されました。
次に、本機能である異なるVolumeMode(Block
)でリストアを試します。
Manifestは以下です。 PVC名とvolumeModeのみ先ほどのPVCとは異なります。
- restore-pvc2.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: restore-pv-block
spec:
accessModes:
- ReadWriteOnce
volumeMode: Block
storageClassName: csi-hostpath-sc
resources:
requests:
storage: 1Gi
dataSource:
apiGroup: snapshot.storage.k8s.io
kind: VolumeSnapshot
name: snapshot-test1
restore-pvc2.yamlをデプロイしPVCを確認します。
$ kubectl get pvc -o wide
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE VOLUMEMODE
restore-pv-block Pending csi-hostpath-sc <unset> 81s Block
restore-pv-filesystem Bound pvc-bae61ab6-9769-4746-a0f2-91fa653e3074 1Gi RWO csi-hostpath-sc <unset> 4m45s Filesystem
test-pv Bound pvc-2cd519af-bb9f-4758-a4d0-e01944d8e75c 1Gi RWO csi-hostpath-sc <unset> 28m Filesystem
Pending
のままとなりました。
詳細を確認します。
$ kubectl describe pvc restore-pv-block
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Provisioning 56s (x7 over 119s) hostpath.csi.k8s.io_csi-hostpathplugin-rc5pt_08514e20-b05a-442e-a352-57bc803814ff External provisioner is provisioning volume for claim "default/restore-pv-block"
Warning ProvisioningFailed 56s (x7 over 119s) hostpath.csi.k8s.io_csi-hostpathplugin-rc5pt_08514e20-b05a-442e-a352-57bc803814ff failed to provision volume with StorageClass "csi-hostpath-sc": error getting handle for DataSource Type VolumeSnapshot by Name snapshot-test1: requested volume default/restore-pv-block modifies the mode of the source volume but does not have permission to do so. snapshot.storage.kubernetes.io/allow-volume-mode-change annotation is not present on snapshotcontent snapcontent-72f81d88-319d-4465-b2d1-395dc31e7668
Normal ExternalProvisioning 3s (x12 over 2m5s) persistentvolume-controller Waiting for a volume to be created either by the external provisioner 'hostpath.csi.k8s.io' or manually by the system administrator. If volume creation is delayed, please verify that the provisioner is running and correctly registered.
error getting handle for DataSource Type VolumeSnapshot by Name snapshot-test1: requested volume default/restore-pv-block modifies the mode of the source volume but does not have permission to do so.
のメッセージが出力されており、元のPVC/PV(Volume)とは異なるVolumeModeへのリストアを防いでくれていることが確認できました。
本機能が設定されている場合に、どうしてもVolumeModeを元のVolumeと変えたい場合には、VolumeSnapshotContentにsnapshot.storage.kubernetes.io/allow-volume-mode-change: "true"
のannotationsを付与すれば許可されるとのことです。
本検証では割愛します。
感想
本検証では、Prevent Unauthorised Volume Mode Conversionの動作検証を行いました。
これまでのVolumeSnapshotのリストアでは、VolumeModeのチェックは存在していなかったため、元のVolumeがFilesystemでフォーマットされたもの(VolumeMode: Filesystem)のVolumeSnapshotであっても、リストア時にRaw Volume(VolumeMode: Block)でリストア出来てしまいました(この逆も可能)。
本機能により、オペミスなどによるこのチグハグなリストアの指定を防ぐことが可能となります。
VolumeSnapshotからのリストアを行う場合は、障害復旧など心穏やかでない状態で作業を行うことが多いかと思います。
そのような状態でも、オペミスを起こさないように本機能を有効化しておくことで、より安全に復旧作業を行えるのではないでしょうか。
参考情報
- https://kubernetes.io/blog/2024/04/17/kubernetes-v1-30-release/#prevent-unauthorized-volume-mode-conversion-during-volume-restore-sig-storage-https-github-com-kubernetes-community-tree-master-sig-storage
- https://kubernetes.io/docs/concepts/storage/volume-snapshots/#convert-volume-mode
- https://kubernetes-csi.github.io/docs/prevent-volume-mode-conversion.html#prevent-unauthorised-volume-mode-conversion