#概要
Kubernetes v1.12よりVolume SnapshotがAlphaサポートされました。このVolume SnapshotはCSI対応のストレージでサポートされています。また、CSIの仕様としてはCSI spec v0.3からSnapshotがサポートしています。つまり、Snapshotを使用するためには、Kubernetes v1.12以降かつCSI spec v0.3以降のCSI対応のストレージが必要となります。(CSIの説明は「KubernetesにおけるContainer Storage Interface (CSI)の概要と検証」を参照)
2018/12現在でVolume Snapshotが利用できるCSI Driverは、Kubernetes CSI Documentationによると以下のストレージです。
- GCE PD
- OpenSDS
- Ceph RDB
- GlusterFS
また、上記に加え検証・開発用に用意されているHostPathを利用したCSI DriverであるCSI HostPath Driverも対応しています。そこで本検証では、CSI HostPath Driverを使いVolume Snapshotの動作検証を行います。
#検証
本検証は以下の環境で行います。
CSI Hostpath Driverを使うため、1 nodeのKubernetesです。
- Kubernetes v1.13.0 (kubeadm で構築、1 node構成(masterのみ))
##Kubernetesの設定
VolumeSnapshotを利用するためには、kubeletとkube-apiserverに--feature-gates
で機能を有効にする必要があります。
まずは、kubeletの設定を行います。KubernetesのNodeにsshなどでログインし/etc/default/kubelet
に--feature-gates
を追加します。
KUBELET_EXTRA_ARGS=--feature-gates=VolumeSnapshotDataSource=true,KubeletPluginsWatcher=true,CSINodeInfo=true,CSIDriverRegistry=true
追加後に、kubeletを再起動します。
$ sudo systemctl daemon-reload
$ sudo systemctl restart kubelet
次に、kube-apiserverの設定を行います。
/etc/kubernetes/manifests/kube-apiserver.yaml
に--feature-gates
を追加します。
spec:
containers:
- command:
- kube-apiserver
...
- --feature-gates=VolumeSnapshotDataSource=true,KubeletPluginsWatcher=true,CSINodeInfo=true,CSIDriverRegistry=true
...
image: k8s.gcr.io/kube-apiserver:v1.13.0
このkube-apiserver.yaml
は編集を行うと、自動でkube-apiserverを再起動してくれます。これで、Kubernetes自身の設定は終わりです。
##CSI HostPath Driverのセットアップ
CSI HostPath Driverをセットアップします。
今回のセットアップでは、以下を順にデプロイしていきます。
- CRD
- CSIDriverRegistry
- CSINodeInfo
- RBAC
- CSI provisioner用
- CSI attacher用
- CSI driver register用
- CSI snapshotter用
- CSI Hostpath provisioner
- CSI Hostpath attacher
- CSI Hostpath plugin
- CSI Hostpath snapshotter
###CRDのデプロイ
はじめに、CRD(Custom Resource Definition)のCSIDriverRegistryとCSINodeInfoをデプロイします。
CSIDriverRegistryのManifestを以下に示します。
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: csidrivers.csi.storage.k8s.io
spec:
group: csi.storage.k8s.io
names:
kind: CSIDriver
plural: csidrivers
scope: Cluster
validation:
openAPIV3Schema:
properties:
spec:
description: Specification of the CSI Driver.
properties:
attachRequired:
description: Indicates this CSI volume driver requires an attach operation,
and that Kubernetes should call attach and wait for any attach operation
to complete before proceeding to mount.
type: boolean
podInfoOnMountVersion:
description: Indicates this CSI volume driver requires additional pod
information (like podName, podUID, etc.) during mount operations.
type: string
version: v1alpha1
CSINodeInfoのManifestを以下に示します。
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: csinodeinfos.csi.storage.k8s.io
spec:
group: csi.storage.k8s.io
names:
kind: CSINodeInfo
plural: csinodeinfos
scope: Cluster
validation:
openAPIV3Schema:
properties:
csiDrivers:
description: List of CSI drivers running on the node and their properties.
items:
properties:
driver:
description: The CSI driver that this object refers to.
type: string
nodeID:
description: The node from the driver point of view.
type: string
topologyKeys:
description: List of keys supported by the driver.
items:
type: string
type: array
type: array
version: v1alpha1
CSIDriverRegistryとCSINodeInfoをデプロイします。
$ kubectl create -f csidriver.yaml
$ kubectl create -f csinodeinfo.yaml
###RBACのデプロイ
CSI Hostpath provisionerを実行するServiceAccountとRBAC(Role Based Access Control)のManifestを以下に示します。
apiVersion: v1
kind: ServiceAccount
metadata:
name: csi-provisioner
namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: external-provisioner-runner
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["list", "watch", "create", "update", "patch"]
- apiGroups: ["snapshot.storage.k8s.io"]
resources: ["volumesnapshots"]
verbs: ["get", "list"]
- apiGroups: ["snapshot.storage.k8s.io"]
resources: ["volumesnapshotcontents"]
verbs: ["get", "list"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: csi-provisioner-role
subjects:
- kind: ServiceAccount
name: csi-provisioner
namespace: default
roleRef:
kind: ClusterRole
name: external-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: external-provisioner-cfg
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "watch", "list", "delete", "update", "create"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: csi-provisioner-role-cfg
namespace: default
subjects:
- kind: ServiceAccount
name: csi-provisioner
namespace: default
roleRef:
kind: Role
name: external-provisioner-cfg
apiGroup: rbac.authorization.k8s.io
作成したManifestをデプロイします。
$ kubectl create -f csi-provisioner-rbac.yaml
次に、CSI Hostpath attacherを実行するServiceAccountとRBACのManifestを以下に示します。
apiVersion: v1
kind: ServiceAccount
metadata:
name: csi-attacher
namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: external-attacher-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: ["csi.storage.k8s.io"]
resources: ["csinodeinfos"]
verbs: ["get", "list", "watch"]
- apiGroups: ["storage.k8s.io"]
resources: ["volumeattachments"]
verbs: ["get", "list", "watch", "update"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: csi-attacher-role
subjects:
- kind: ServiceAccount
name: csi-attacher
namespace: default
roleRef:
kind: ClusterRole
name: external-attacher-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: external-attacher-cfg
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "watch", "list", "delete", "update", "create"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: csi-attacher-role-cfg
namespace: default
subjects:
- kind: ServiceAccount
name: csi-attacher
namespace: default
roleRef:
kind: Role
name: external-attacher-cfg
apiGroup: rbac.authorization.k8s.io
作成したManifestをデプロイします。
$ kubectl create -f csi-attacher-rbac.yaml
CSI Hostpath driverを実行するServiceAccountとRBACのManifestを以下に示します。
apiVersion: v1
kind: ServiceAccount
metadata:
name: csi-driver-registrar
namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: driver-registrar-runner
rules:
- apiGroups: [""]
resources: ["events"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: csi-driver-registrar-role
subjects:
- kind: ServiceAccount
name: csi-driver-registrar
namespace: default
roleRef:
kind: ClusterRole
name: driver-registrar-runner
apiGroup: rbac.authorization.k8s.io
作成したManifestをデプロイします。
$ kubectl create -f csi-driver-registrar-rbac.yaml
RBAC関連の最後に、CSI Hostpath snapshotterを実行するServiceAccountとRBACのManifestを以下に示します。
apiVersion: v1
kind: ServiceAccount
metadata:
name: csi-snapshotter
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: external-snapshotter-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
- apiGroups: ["snapshot.storage.k8s.io"]
resources: ["volumesnapshotclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: ["snapshot.storage.k8s.io"]
resources: ["volumesnapshotcontents"]
verbs: ["create", "get", "list", "watch", "update", "delete"]
- apiGroups: ["snapshot.storage.k8s.io"]
resources: ["volumesnapshots"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["apiextensions.k8s.io"]
resources: ["customresourcedefinitions"]
verbs: ["create", "list", "watch", "delete"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: csi-snapshotter-role
subjects:
- kind: ServiceAccount
name: csi-snapshotter
namespace: default
roleRef:
kind: ClusterRole
name: external-snapshotter-runner
apiGroup: rbac.authorization.k8s.io
作成したManifestをデプロイします。
$ kubectl create -f csi-snapshotter-rbac.yaml
以上で事前準備のRBAC関連のデプロイが終わりました。
CSI HostPath provisionerのデプロイ
次に、CSI HostPath provisionerをデプロイします。
デプロイするCSI HostPath provisionerのManifestを以下に示します。
kind: Service
apiVersion: v1
metadata:
name: csi-hostpath-provisioner
labels:
app: csi-hostpath-provisioner
spec:
selector:
app: csi-hostpath-provisioner
ports:
- name: dummy
port: 12345
---
kind: StatefulSet
apiVersion: apps/v1
metadata:
name: csi-hostpath-provisioner
spec:
serviceName: "csi-hostpath-provisioner"
replicas: 1
selector:
matchLabels:
app: csi-hostpath-provisioner
template:
metadata:
labels:
app: csi-hostpath-provisioner
spec:
serviceAccountName: csi-provisioner
containers:
- name: csi-provisioner
image: quay.io/k8scsi/csi-provisioner:v0.4.1
args:
- "--provisioner=csi-hostpath"
- "--csi-address=$(ADDRESS)"
- "--connection-timeout=15s"
env:
- name: ADDRESS
value: /csi/csi.sock
imagePullPolicy: Always
volumeMounts:
- mountPath: /csi
name: socket-dir
volumes:
- hostPath:
path: /var/lib/kubelet/plugins/csi-hostpath
type: DirectoryOrCreate
name: socket-dir
CSI HostPath provisionerをデプロイします。
$ kubectl create -f csi-hostpath-provisioner.yaml
###CSI HostPath attacherのデプロイ
続いて、CSI HostPath attacherをデプロイしていきます。
デプロイするCSI HostPath attacherのManifestを以下に示します。
kind: Service
apiVersion: v1
metadata:
name: csi-hostpath-attacher
labels:
app: csi-hostpath-attacher
spec:
selector:
app: csi-hostpath-attacher
ports:
- name: dummy
port: 12345
---
kind: StatefulSet
apiVersion: apps/v1
metadata:
name: csi-hostpath-attacher
spec:
serviceName: "csi-hostpath-attacher"
replicas: 1
selector:
matchLabels:
app: csi-hostpath-attacher
template:
metadata:
labels:
app: csi-hostpath-attacher
spec:
serviceAccountName: csi-attacher
containers:
- name: csi-attacher
image: quay.io/k8scsi/csi-attacher:v0.4.1
args:
- --v=5
- --csi-address=$(ADDRESS)
env:
- name: ADDRESS
value: /csi/csi.sock
imagePullPolicy: Always
volumeMounts:
- mountPath: /csi
name: socket-dir
volumes:
- hostPath:
path: /var/lib/kubelet/plugins/csi-hostpath
type: DirectoryOrCreate
name: socket-dir
CSI HostPath attacherをデプロイします。
$ kubectl create -f csi-hostpath-attacher.yaml
CSI HostPath pluginのデプロイ
次に、CSI HostPath pluginをデプロイします。
デプロイするCSI HostPath pluginのManifestを以下に示します。
kind: DaemonSet
apiVersion: apps/v1
metadata:
name: csi-hostpathplugin
spec:
selector:
matchLabels:
app: csi-hostpathplugin
template:
metadata:
labels:
app: csi-hostpathplugin
spec:
serviceAccountName: csi-driver-registrar
hostNetwork: true
containers:
- name: driver-registrar
image: quay.io/k8scsi/driver-registrar:v0.4.1
args:
- --v=5
- --csi-address=/csi/csi.sock
- --kubelet-registration-path=/var/lib/kubelet/plugins/csi-hostpath/csi.sock
env:
- name: KUBE_NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
imagePullPolicy: Always
volumeMounts:
- mountPath: /csi
name: socket-dir
- mountPath: /registration
name: registration-dir
- name: hostpath
image: quay.io/k8scsi/hostpathplugin:v0.4.1
args:
- "--v=5"
- "--endpoint=$(CSI_ENDPOINT)"
- "--nodeid=$(KUBE_NODE_NAME)"
env:
- name: CSI_ENDPOINT
value: unix:///csi/csi.sock
- name: KUBE_NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
imagePullPolicy: Always
securityContext:
privileged: true
volumeMounts:
- mountPath: /csi
name: socket-dir
- mountPath: /var/lib/kubelet/pods
mountPropagation: Bidirectional
name: mountpoint-dir
volumes:
- hostPath:
path: /var/lib/kubelet/plugins/csi-hostpath
type: DirectoryOrCreate
name: socket-dir
- hostPath:
path: /var/lib/kubelet/pods
type: DirectoryOrCreate
name: mountpoint-dir
- hostPath:
path: /var/lib/kubelet/plugins
type: Directory
name: registration-dir
CSI HostPath pluginをデプロイします。
$ kubectl create -f csi-hostpathplugin.yaml
CSI HostPath snapshotterのデプロイ
最後に、Volume Snapshotを使うためのサイドカーコンテナCSI HostPath snapshotterをデプロイします。
以下に、CSI HostPath snapshotterのManifestを示します。
kind: Service
apiVersion: v1
metadata:
name: csi-hostpath-snapshotter
labels:
app: csi-hostpath-snapshotter
spec:
selector:
app: csi-hostpath-snapshotter
ports:
- name: dummy
port: 12345
---
kind: StatefulSet
apiVersion: apps/v1
metadata:
name: csi-hostpath-snapshotter
spec:
serviceName: "csi-hostpath-snapshotter"
replicas: 1
selector:
matchLabels:
app: csi-hostpath-snapshotter
template:
metadata:
labels:
app: csi-hostpath-snapshotter
spec:
serviceAccount: csi-snapshotter
containers:
- name: csi-snapshotter
image: quay.io/k8scsi/csi-snapshotter:v0.4.1
args:
- "--csi-address=$(ADDRESS)"
- "--connection-timeout=15s"
env:
- name: ADDRESS
value: /csi/csi.sock
imagePullPolicy: Always
volumeMounts:
- mountPath: /csi
name: socket-dir
volumes:
- hostPath:
path: /var/lib/kubelet/plugins/csi-hostpath
type: DirectoryOrCreate
name: socket-dir
CSI HostPath snapshotterをデプロイします。
$ kunectl create -f csi-hostpath-snapshotter.yaml
これで、CSI HostPath Driverのデプロイが完了です。
次から、このCSI HostPath Driverを使いVolume Snapshotの検証を行います。
##Volume Snapshotの動作検証
いよいよ、CSI HostPath Driverを使いVolume Snapshotを検証します。
###StorageClassのデプロイ
まず初めに、CSI HostPath Driverを使い、PV(PersistentVolume)を作成するためのStorageClassをデプロイします。
以下に、StorageClassのManifestを示します。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: csi-hostpath-sc
provisioner: csi-hostpath
reclaimPolicy: Delete
volumeBindingMode: Immediate
StorageClassをデプロイし確認します。
$ kubectl create -f csi-storageclass.yaml
$ kubectl get sc
NAME PROVISIONER AGE
csi-hostpath-sc csi-hostpath 4h8m
###VolumeSnapshotClassのデプロイ
次に、VolumeSnapshotClassをデプロイします。
VolumeSnapshotClassはSnapshotにおけるStorageClassのような働きをするリソースになります。
以下に、VolumeSnapshotClassのMainfestを示します。
apiVersion: snapshot.storage.k8s.io/v1alpha1
kind: VolumeSnapshotClass
metadata:
name: csi-hostpath-snapclass
snapshotter: csi-hostpath
VolumeSnapshotClassをデプロイし確認します。
$ kubectl create -f csi-snapshotclass.yaml
$ kubectl get VolumeSnapshotClass
NAME AGE
csi-hostpath-snapclass 4h
以上で、Volume Snapshotを利用する準備が整いました。
###Snapshotの元となるPVの準備
Snapshotを実行する前に、元となるPV(PersistentVolume)、PVC(PersistentVolumeClaim)およびPodをデプロイします。
以下に、PVCのManifestを示します。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: csi-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: csi-hostpath-sc
storageClassNameにはデプロイしたCSI HostPath Driver用StorageClassのcsi-hostpath-sc
を指定します。
作成したPVCをデプロイし確認します。
$ kubectl create -f csi-pvc.yaml
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
csi-pvc Bound pvc-5a8d9855-fc78-11e8-af45-080027571559 1Gi RWO csi-hostpath-sc 9s
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-5a8d9855-fc78-11e8-af45-080027571559 1Gi RWO Delete Bound default/csi-pvc csi-hostpath-sc 12s
PVCがデプロイされると、Dynamic Provisioningにより、PVCの要求にマッチしたPVがStorageClass(csi-hostpath-sc
)から作成されます。
次に、デプロイしたPVをマウントするPodをデプロイします。
PodのManifestを以下に示します。
kind: Pod
apiVersion: v1
metadata:
name: my-csi-app
spec:
containers:
- name: my-frontend
image: busybox
volumeMounts:
- mountPath: "/data"
name: my-csi-volume
command: [ "sleep", "1000000" ]
volumes:
- name: my-csi-volume
persistentVolumeClaim:
claimName: csi-pvc
このPodでは/data
ディレクトリにCSI HostPath Driverで作成された永続ストレージのPVをマウントします。
Podをデプロイします。
$ kubectl create -f csi-app.yaml
デプロイされたPodへ入り、PVにデータを書き込みます。
$ kubectl get pod |grep my-csi-app
my-csi-app 1/1 Running 0 35s
$ kubectl exec -ti my-csi-app /bin/sh
/ # cd /data
/data # echo "hoge" > hello.txt
/data # cat hello.txt
hoge
/data # exit
PVにhello.txt
ファイルを作成し、hoge
という文字列を書き込みました。
###Snapshotの作成
では準備が全て整ったので、Snapshotを行います。
Snapshotは、VolumeSnapshotリソースをデプロイすることで作成します。
VolumeSnapshotのManifestを以下に示します。
apiVersion: snapshot.storage.k8s.io/v1alpha1
kind: VolumeSnapshot
metadata:
name: snapshot-20181210
spec:
snapshotClassName: csi-hostpath-snapclass
source:
name: csi-pvc
kind: PersistentVolumeClaim
VolumeSnapshotでは、spec.snapshotClassNameに先ほどデプロイしたVolumeSnapshotClass(csi-hostpath-snapclass
)を指定します。
spec.sourceには、先ほど準備したPVにBoundされているPVC(csi-pvc
)を指定します。
また、今回は作成するSnapshotが、いつの時点のデータのSnapshotかわかりやすくするため、日付を含んだ名前をspec.metadata.nameに設定しています。
VolumeSnapshotをデプロイし確認します。
$ kubectl create -f csi-snapshot.yaml
volumesnapshot.snapshot.storage.k8s.io/snapshot-20181210 created
$ kubectl get VolumeSnapshot
NAME AGE
snapshot-20181210 14s
次に、Snapshotを確認するために、再度PV上のファイル(hello.txt
)に新たな文字列(foo
)を追記します。
$ kubectl exec -ti my-csi-app /bin/sh
/ # cd data
/data # ls
hello.txt
/data # cat hello.txt
hoge
/data # echo "foo" >>hello.txt
/data # cat hello.txt
hoge
foo
/data # exit
###SnapshotからのRestore
作成したSnapshotからRestoreします。
Restoreでは、作成したVolumeSnapshotを指定したPVCをデプロイし、Snapshotをとった時点のデータが保存されたPVを作成します。
以下に、Restore用のPVCのManifestを示します。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: restore-20181210
spec:
storageClassName: csi-hostpath-sc
dataSource:
name: snapshot-20181210
kind: VolumeSnapshot
apiGroup: snapshot.storage.k8s.io
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
spec.dataSourceにRestoreするVolumeSnapshotを指定します。ここでは先ほどSnapshotをとったVolumeSnapshotのsnapshot-20181210
を指定しています。
Restore用のPVCをデプロイします。
$ kubectl create -f csi-restore.yaml
PVCがデプロイされると、Dynamic ProvisioningによりPVが自動生成されます。
この作成されたRestore用のPVをマウントするPodをデプロイします。
ここでは、先ほど作成したcsi-app.yaml
をコピー&編集し、csi-app2.yaml
を作成しています。
PodのManifestを以下に示します。
kind: Pod
apiVersion: v1
metadata:
name: my-csi-app2
spec:
containers:
- name: my-frontend
image: busybox
volumeMounts:
- mountPath: "/data"
name: my-csi-volume
command: [ "sleep", "1000000" ]
volumes:
- name: my-csi-volume
persistentVolumeClaim:
claimName: restore-20181210
変更点としては、Podの名前(metadata.name)とspec.volumes.persistentVolumeClaim.claimNameです。
claimNameにはcsi-restore.yaml
で作成したrestore-20181210
を指定しています。
Podをデプロイした後、Podに入りデータを確認してみます。
$ kubectl create -f csi-app2.yaml
$ kubectl exec -ti my-csi-app2 /bin/sh
/ # cd data
/data # cat hello.txt
hoge
/data # exit
デプロイされたmy-csi-app2
のPodに入り、PV(/data
)のhello.txt
を見てみると、最初に書き込んだ文字列hoge
のみとなっています。
つまり、
-
hoge
を書き込み - Snapshotを取る
-
foo
を書き込み
の順で操作を行ってきたので、無事foo
が書き込まれる前の状態のファイルにRestoreされています。
KubernetesのVolume Snapshotにより正しくSnapshotが動作しました。
##クリーンアップ
検証環境を削除します。
$ kubectl delete -f csi-app2.yaml
$ kubectl delete -f csi-restore.yaml
$ kubectl delete -f csi-snapshot.yaml
$ kubectl delete -f csi-app.yaml
$ kubectl delete -f csi-pvc.yaml
$ kubectl delete -f csi-snapshotclass.yaml
$ kubectl delete -f csi-storageclass.yaml
$ kubectl delete -f csi-hostpath-snapshotter.yaml
$ kubectl delete -f csi-hostpathplugin.yaml
$ kubectl delete -f csi-hostpath-provisioner.yaml
$ kubectl delete -f csi-hostpath-attacher.yaml
$ kubectl delete -f csi-snapshotter-rbac.yaml
$ kubectl delete -f csi-provisioner-rbac.yaml
$ kubectl delete -f csi-driver-registrar-rbac.yaml
$ kubectl delete -f csi-attacher-rbac.yaml
$ kubectl delete -f csinodeinfo.yaml
$ kubectl delete -f csidriver.yaml
以上で検証終わりです。
感想
今回の検証では、Kubernetes v1.12から登場したVolume Snapshotを検証しました。
ストレージのSnapshotは、変更箇所のみSSDやHDDなどに保存されるため、ミラーなどのフルバックアップに比べて容量が少なく済むことが特徴のバックアップ機能です。
このような特徴をもったSnapshotがKubernetesから利用できるようになることで、より一層PVのユースケースが広がります。
これまでは単にDBなどStatefulアプリケーションを動作させるためにPVを使うことが多かったのではないでしょうか。新しいユースケースの例としては「機械学習モデルをSeldon Coreを使いKubernetesでサービス化」で紹介した機械学習のモデルデータの保存先にVolume Snapshotを使う方法があります。機械学習では、新しい学習データを使って何度も繰り返しモデルデータをアップデートしていく運用パターンが多いかと思います。さらには、過去の再現性や比較のために過去のモデルデータも大量に保存している組織も多いのではないでしょうか。また、このモデルデータはニューラルネットワークにおけるニューロン間の重みの数値が大半を占め、その重みの値の大半は、ニューラルネットワークのモデルを大きく変更しない限り、同じ値の重みが多いといった性質があります。つまり、このモデルデータにVolume Snaoshotを適用することで、ストレージのSnapshotの変更箇所のみ保存する特徴が活きます。これまで、過去のモデルデータも保存したいため、毎回モデルデータを更新するたびに新たにPVを作るか、もしくはコンテナの中にモデルデータを抱え込んだコンテナイメージを新規に作っていたかと思います。この方法ですと、変更されていない箇所も全て新たなデータ領域に保存されるため、ストレージを無駄遣いします。これからは、Volume Snapshotを使うことで、過去のモデルデータも保存しつつストレージの消費容量を少なくモデルデータの更新ができるようになります。
このように、Volume SnapshotはPVを活用したコンテナのユースケースが広がる可能性を秘めた機能かと思います。今後、Volume Snapshotをうまく活用した事例がカンファレンスなどで発表されるのが非常に楽しみです。ただ、現時点で残念なことは、対応しているストレージが少ないという点です。ストレージベンダの早期の対応を期待しています。