Kubernetes Local Volume の概要
Kubernetesではv1.10からBetaとしてLocal Volumeがサポートされました。Local Volumeとは、KubernetesのNodeのサーバ内蔵ディスク(SSD含む)をPodから利用する機能です。これにより、Podが削除されても、データを削除することなくNodeのサーバ内蔵ディスクに、データを保存し続けることが可能となります。
外部ストレージを準備できない環境や、内蔵ディスクに余力がある方は使ってみると良いかと思います。ただし、あくまで内蔵ディスクですので、外部ストレージと異なりRAIDやErasure Codingなどのデータ保護は出来ませんので、Node障害でデータが消えてしまったり、アクセスできなくなって困るようなデータの格納には向かないので注意してください。
Kubernetes v1.10での制限としては、StatefulSetなどと一緒に用いることの多いPod生成時に自動でVolumeを生成してくれる機能Dynamic Provisioningが非サポートとなっています。その代わり、Dynamic Provisioningほど強力ではありませんが、実際にPodにVolumeが割り当てられるまでVolumeを使用しないようにする設定が可能です。
検証
検証環境
- Kubernetes環境: Kubernetes v1.10.5
(kubeadmを使いVirtual Box上に構築したMaster, WorkerNode x 2VMの合計3VMのクラスタ環境)
Local Volumeの準備
はじめに workerノード(k8s-node1)に、sshでログインし、local volumeとして利用するディレクトリを作成します。
今回は、k8s-node1のLocal Volumeのみ割り当てます。
$ kubectl get node
NAME STATUS ROLES AGE VERSION
k8s Ready master 56d v1.10.5
k8s-node1 Ready <none> 56d v1.10.5
k8s-node2 Ready <none> 56d v1.10.5
$ ssh k8s-node1
$ sudo mkdir -p /mnt/disks/vol1
$ sudo chmod 777 /mnt/disks/vol1
$ exit
StorageClass の定義
次に、Local Volumeに対応したStorageClassをsc-lv.yaml
ファイルに定義します。
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
- provisionerは、Dynamic Provisioningが非サポートとのため、
kubernetes.io/no-provisioner
を指定します。 - volumeBindingModeは、
WaitForFirstConsumer
を設定することで、実際にPodにVolumeが割り当てられるまでVolumeを使用しないようになります。
StorageClass のデプロイ
kubectlを使ってStorageClassをデプロイします。
$ kubectl create -f sc-lv.yaml
storageclass.storage.k8s.io "local-storage" created
StorageClassがデプロイされていることを確認します。
$ kubectl get sc
NAME PROVISIONER AGE
local-storage kubernetes.io/no-provisioner 1m
PersistentVolume の定義
続いて、PersistenvVolumeをpv-lv.yamlファイルに定義します。
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /mnt/disks/vol1
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-node1
- spec.accessModesは、Local Volumeは他のPodから共有で利用することが出来ず、1つのPodからしかRead/Writeができないため、
ReadWriteOnce
を指定します。 - persistentVolumeReclaimPolicyは、PersistentVolumeClaimが削除された時にデータ削除のポリシーを指定できます。Local Volumeの場合は
Retain
を指定してください。persistentVolumeReclaimPolicy にはデータを削除するRecycle
、Volumeを同時に削除するDelete
が指定可能です。しかし、Recycle
はデータを削除するのであれば、そもそもLocal Volumeを使う理由がありませんし、Delete
はDynamic Provisioningが非サポートのため、指定してもPersistentVolumeClaimされるとPersistetVolumeのステータスがFAILになります。 - storageClassNameは、先ほどデプロイしたStorageClassのnameの
local-storage
を指定します - spec.local.pathは、Local Volumeの準備で作成したディレクトリ
/mnt/disks/vol1
を指定します。 - nodeAffinityは、どのNodeを使うかの条件を指定します。今回は、Nodeの
k8s-node1
のみ選択されるようにkubernetes.io/hostname
をキーとして指定しています。
PersistentVolume のデプロイ
kubectlを使ってPersistentVolumeをデプロイします。
$ kubectl create -f pv-lv.yaml
persistentvolume "local-pv" created
PersistentVolumeがデプロイされていることを確認します。
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
local-pv 1Gi RWO Retain Available local-storage 29s
PersistentVolumeClaim の定義
次に、ユーザがVolumeを要求するための定義PersistentVolumeClaimをpvc-lv.yamlファイルに定義します。
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: local-claim
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-storage
resources:
requests:
storage: 1Gi
- spec.storageClassName には、先に定義したStorageClassのnameである
local-storage
を指定します。
PersistentVolumeClaim のデプロイ
kubectlを使ってPersistentVolumeClaimをデプロイします。
$ kubectl create -f pvc-lv.yaml
persistentvolumeclaim "local-claim" created
PersistentVolumeClameがデプロイされていることを確認します。
この段階では、STATUSはPending
となります。
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
local-claim Pending local-storage 52s
Local Volumeを使ったPodの定義
定義したPersistentVolumeClaimのlocal-claim
を使い、PodへLocal Volumeを割り当てます。
ここでは、nginx-lv.yamlとしてPodを定義します。
apiVersion: v1
kind: Pod
metadata:
name: my-nginx-pod
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: mydata
volumes:
- name: mydata
persistentVolumeClaim:
claimName: local-claim
- spec.containers.volumeMounts.mountPathに、Pod内のLocal Volumeのマウント先を指定します。
- spec.volumes.persistentVolumeClaimに、先に定義したPersistentVolumeClaimのnameである
local-claim
を指定します。
Local Volumeを使ったPodのデプロイ
kubectlを使ってLocal Volumeを使ったPodをデプロイします。
$ kubectl create -f nginx-lv.yaml
pod "my-nginx-pod" created
Local Volumeを使ったPodがデプロイされていることを確認します。
このPodがPersistentVolumeClaimを経由しPersistentVolumeをマウントした段階で、PersistentVolumeClaimのSTATUSがBound
に変わります。
さらに、そのPersistentVolumeClaimから選出されたPersistentVolumeのSTATUSがBound
に変わり、CLAIMにdefault/local-claim
が設定されます。
つまり、StorageClassにて定義したvolumeBindingModeのWaitForFirstConsumer
の指定通り、PodがLocal Volumeをマウントした際に、初めてPersistentVolumeが使用され始めます。
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
local-claim Bound local-pv 1Gi RWO local-storage 3m
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
local-pv 1Gi RWO Retain Bound default/local-claim local-storage 5m
クリーンアップ
デプロイしたPod, PersistentVolumeClaimを削除します。
$ kubectl delete -f nginx-lv.yaml
pod "my-nginx-pod" deleted
$ kubectl delete -f pvc-lv.yaml
persistentvolumeclaim "local-claim" deleted
PersistentVolumeClaimを削除したことで、PersistentVolumeのSTAUTSがReleased
に変わります。
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
local-pv 1Gi RWO Retain Released default/local-claim local-storage 6m
続いて、PersistentVolumeとStorageClassを削除します。
$ kubectl delete -f pv-lv.yaml
persistentvolume "local-pv" deleted
$ kubectl delete -f sc-lv.yaml
storageclass.storage.k8s.io "local-storage" deleted
最後に、K8s-node1のNodeにsshでログインし、/mnt/disks
ディレクトリ配下を削除します。
$ ssh k8s-node1
$ cd /mnt
$ sudo rm -rf disks/
$ exit
まとめ
今回、Local Volumeを検証しました。Local Volumeは、データの重要性は低いが、Podと一緒に削除されて欲しくない、そういうデータを保存するのに向いています。そういうケースはなんでしょうか?たとえば、データベースのSlaveノードなどには向いているかと思います。Slaveノードは、Masterノードが生きていれば、Slaveノードのデータは障害などでデータが削除したとしても復旧可能です。また、Slaveノードがバージョンアップによるローリングアップデートなどで再作成する場合には、データは保持されたままなので、MasterとSlave間の同期は短時間で完了します。
その他、Local Volumeのユースケースはなにかあるでしょうか?感の良い人ならば思いつくかと思います。Kubernetesに対応していないレガシーの外部ストレージを利用するのに利用できます。つまり、レガシーの外部ストレージのVolumeをNodeにマウントし、そのマウントしたVolumeを、今回紹介したLocal Volumeとして利用します。これにより、レガシーの外部ストレージのVolumeをPodで使用することが可能となります。ただし、Nodeのサーバに外部ストレージのVolumeを割り当てないといけないため、管理者の負担は増えてしまいます。レガシーの外部ストレージをPod(コンテナ)で利用する場合の奥の手として持っておくのには、有効な方法かと思います。