Kubernetes Persistent Volume の概要
Kubernetes では,Persistent Volume として,外部ストレージに Pod のデータの格納する方法が提供されています.Persistent Volume を使うことにより,(1)Pod の削除してもデータを保存し続ける,(2)複数Pod間でファイルを共有することが可能となります.また,Persistent Volume では,外部ストレージにデータが保存されるため,外部ストレージが持つバックアップ機能やリモートコピー機能によりPodのデータを超長期保存やデザスタリカバリに備えることが可能となります.
Kubernetes v1.9 までは,Kubernetes volume plugin として,Kubernetes のソースコードに直に組み込まれた実装("in-tree")がされていました.そのため,3rd パーティのストレージベンダは Kubernetesのソースコードへアップストリームする必要がありました.v1.9 では,まだAlpha ながらも3rd パーティのベンダがKubernetesのソースコードにアップストリームする必要なく独自に開発できるContainer Storage Interface (CSI)が登場しています.
今回の検証では,CSIではなく,ソースコードに直に組み込まれた実装("in-tree")を使っています.
外部ストレージの準備
事前準備として,Persistent Volume として利用する外部ストレージを準備します.
外部ストレージとしては,Volumesに記載されているストレージを利用することが出来ます.
本検証では,minikube の母艦となる Mac OSX(10.12.6) を NFS Server にして設定します.
共有するディレクトリを作成し,NFS Server の設定ファイル(/etc/exports)を作成します.
minikube のIP が192.168.99.100 のため,下記例では192.168.99.0 のネットワークからのアクセスを許可しています.
$ sudo mkdir /share
$ sudo chmod 777 /share
$ sudo vi /etc/exports
/share -mapall=nobody:wheel -network 192.168.99.0 -mask 255.255.255.0
次に,NFS Server のデーモンを起動します.
$ sudo nfsd start
$ sudo nfsd update
$ sudo showmount -e
Exports list on localhost:
/share 192.168.99.0
NFS Server の設定が出来ているかの確認のために,minikube からマウントしてみます.
minikube から母艦の Mac へは 192.168.99.1 でアクセス出来ます.
df コマンドで確認し,Mac の /share ディレクトリを NFS でマウント出来ているのが確認できます.
$ minikube ssh
_ _
_ _ ( ) ( )
___ ___ (_) ___ (_)| |/') _ _ | |_ __
/' _ ` _ `\| |/' _ `\| || , < ( ) ( )| '_`\ /'__`\
| ( ) ( ) || || ( ) || || |\`\ | (_) || |_) )( ___/
(_) (_) (_)(_)(_) (_)(_)(_) (_)`\___/'(_,__/'`\____)
$ sudo mkdir /share
$ sudo mount -t nfs 192.168.99.1:/share /share
$ df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 933M 0 933M 0% /dev
tmpfs 1001M 0 1001M 0% /dev/shm
tmpfs 1001M 22M 979M 3% /run
tmpfs 1001M 0 1001M 0% /sys/fs/cgroup
/dev/sda1 17G 977M 15G 7% /mnt/sda1
/Users 931G 50G 882G 6% /Users
192.168.99.1:/share 931G 49G 882G 6% /share
minikube から抜ける前に,マウントを外しておきましょう.
$ sudo umount /share
Persistent Volume の定義
次に,Kubernetes の Persistent Volume を定義してみましょう.
下記内容を,nfs-pv.yaml
として保存してください.
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs001
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
# PersistentVolumeClaim を削除した時の動作
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow
mountOptions:
- hard
## マウント先のNFS Serverの情報を記載
nfs:
path: /share
server: 192.168.99.1
- Persistent Volume へのアクセスモード(accessModes) は次の3つから指定
- ReadWriteOnce: 1 つのノードからRead/Write でマウントできる
- ReadOnlyMany: 複数のノードからRead Onlyでマウントできる
- ReadWriteMany: 複数のノードからR/Wでマウントできる
accessMode は使用する外部ストレージにより指定できるものが決まっているため,
kuberbetes.io の persistent volumeで事前に確認してください.
- Persistent Volume Claim が削除された時のポリシー(persistentVolumeReclaimPolicy)は次の3つから指定
- Retain: 手動でデータを消す(データは自動で削除されない)
- Recycle: データが削除される(rm -rf /thevolume/*)
- Delete: 関連するストレージリソースを削除する(AWS EBS, GCE PD, Azure Disk, or OpenStack Cinder volume)
Persistent Volume の作成
定義した Persistent Volume を作成をしてみましょう.
$ kubectl create -f nfs-pv.yaml
persistentvolume "nfs001" created
作成できたか確認してみます.
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nfs001 1Gi RWX Recycle Available slow 1m
Persistent Volume Claim の定義
Persistent Volume Claim は,Kubernetes の Pod から Persistent Volume をマウントする際に利用します.Kubernetes は,Persistent Volume Claim で要求された性能を満たすストレージを Persistent Volume の中から選択します.
Persistent Volume Claim を定義してみましょう.
下記内容を,nfs-pvc.yaml
として保存してください.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-claim1
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
storageClassName: slow
Persistent Volume Claim の作成
定義した Persistent Volume Claim を作成をしてみましょう.
$ kubectl create -f nfs-pvc.yaml
persistentvolumeclaim "nfs-claim1" created
作成できたか確認してみます.
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
nfs-claim1 Bound nfs001 1Gi RWX slow 57s
Persistent Volume の CLAIM 情報を見てみると,nfs-claim1 の Persistent Volume Claim にマッチされているのがわかります.
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
nfs001 1Gi RWX Recycle Bound default/nfs-claim1 slow 13m
Persistent Volume をマウントしたPodをデプロイ
次に,Persistent Volume をマウントした Pod を定義してみましょう.
下記内容を,my-nginx-pod.yaml
として保存してください.
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
# マウント対象となる Persistent Volume に対応する
# Persistent Volume Claimを指定
persistentVolumeClaim:
claimName: nfs-claim1
定義した Pod をデプロイします.
$ kubectl create -f my-nginx-pod.yaml
pod "my-nginx-pod" created
マウントされた Persistent Volume の確認
kubectl describe
にて,デプロイした Pod を確認すると,nfs-claim1 の Persistent Volume Claim を使い,マウントしているのがわかります.
$ kubectl describe pod my-nginx-pod
Name: my-nginx-pod
Namespace: default
Node: minikube/192.168.99.100
Start Time: Mon, 15 Jan 2018 15:40:39 +0900
IP: 172.17.0.3
Containers:
nginx:
Container ID: docker://f71d3e738d8eaf6ac1e856b6c8515f2de1987f0e23485618e36e9a991031ce60
Image: nginx
Image ID: docker-pullable://nginx@sha256:285b49d42c703fdf257d1e2422765c4ba9d3e37768d6ea83d7fe2043dad6e63d
Port: 80/TCP
State: Running
Started: Mon, 15 Jan 2018 15:40:44 +0900
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/usr/share/nginx/html from mydata (rw)
/var/run/secrets/kubernetes.io/serviceaccount from default-token-6mqpq (ro)
Conditions:
Type Status
Initialized True
Ready True
PodScheduled True
Volumes:
mydata:
Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
ClaimName: nfs-claim1
ReadOnly: false
default-token-6mqpq:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-6mqpq
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: <none>
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 1m default-scheduler Successfully assigned my-nginx-pod to minikube
Normal SuccessfulMountVolume 1m kubelet, minikube MountVolume.SetUp succeeded for volume "default-token-6mqpq"
Normal SuccessfulMountVolume 1m kubelet, minikube MountVolume.SetUp succeeded for volume "nfs001"
Normal Pulling 1m kubelet, minikube pulling image "nginx"
Normal Pulled 1m kubelet, minikube Successfully pulled image "nginx"
Normal Created 1m kubelet, minikube Created container
Normal Started 1m kubelet, minikube Started container
実際に,デプロイしたコンテナにログインし確認してみましょう.
$ kubectl exec my-nginx-pod -ti /bin/bash
root@my-nginx-pod:/# df -h
Filesystem Size Used Avail Use% Mounted on
overlay 17G 978M 15G 7% /
tmpfs 1001M 0 1001M 0% /dev
tmpfs 1001M 0 1001M 0% /sys/fs/cgroup
/dev/sda1 17G 978M 15G 7% /etc/hosts
shm 64M 0 64M 0% /dev/shm
192.168.99.1:/share 931G 49G 882G 6% /usr/share/nginx/html
tmpfs 1001M 12K 1001M 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs 1001M 0 1001M 0% /sys/firmware
/use/share/nginx/html ディレクトリは,192.168.99.1:/share をマウントしたものであることが確認できます.
試しに,/use/share/nginx/html ディレクトリにファイルを作ってみましょう.
root@my-nginx-pod:~# echo -n "Hello Persistent Volume" > /usr/share/nginx/html/index.html
root@my-nginx-pod:~# ls /usr/share/nginx/html/
index.html
root@my-nginx-pod:~# cat /usr/share/nginx/html/index.html
Hello Persistent Volume
my-nginx-pod 上で作られたファイルが,母艦の Mac に格納されているかを確認します.
別のターミナルを開き,共有したディレクトリを確認します.
$ ls -l /share
total 8
-rw-r--r-- 1 nobody wheel 23 1 15 15:55 index.html
$ cat /share/index.html
Hello Persistent Volume
my-nginx-pod 上で書き込まれたファイルが母艦の Mac に格納されています.
また,ファイルのユーザは,/etc/exports で指定した nobody:wheel となります.
Pod, Persistent Volume Claim, Persistent Volume の削除
作成した Pod を削除します.
$ kubectl delete pod my-nginx-pod
pod "my-nginx-pod" deleted
Pod を削除しても,Persistent Volume に保存したファイルは削除されません.
母艦の Mac にてファイルが削除されていないことを確認します.
$ ls -l /share
total 8
-rw-r--r-- 1 nobody wheel 23 1 15 15:55 index.html
このように Pod が削除されても,再度 Pod より同じディレクトリをマウントすれば,
永続的にデータを保存し続けることが可能となります.
次に,Persitent Volume Claim を削除します.
$ kubectl delete pvc nfs-claim1
persistentvolumeclaim "nfs-claim1" deleted
Persistent Volume Claim が削除されると,母艦の Mac から作成されたファイルも削除されます.
$ ls -l /share
$
Persistent Volume Claim の削除した時の動作は,Persistent Volume の定義,nfs-pv.yaml
の persistentVolumeReclaimPolicyに記載された動作に従います.
Persistent Volume を削除します.
$ kubectl delete pv nfs001
persistentvolume "nfs001" deleted
まとめ
Kubernetes の PersistentVolume, Persistent Volume Claim の動作は上記の検証で試した通りです. ストレージの専門家の視点からみたら,Persistent Volume は,外部ストレージへのエントリーポイントとして比較的理解は出来るかと思いますが,難しいのは, Persistent Volume Claim の存在だと思います.
Kubernetes では,仮想化されたインフラの世界を作り上げることで,ユーザに物理リソースを意識させず,物理障害などを回避するセルフヒーリングなどを実現しています.そのため,Persistent Volume Claim は,仮装化されたインフラの世界を実現するための,ストレージの定義になります.
これを一歩進め,運用シーンについて考えてみると,Persistent Volume は,Kunernetes の下回りのインフラ環境を理解して Kubernetesを構築するインフラ管理者(ストレージ管理者含む)が定義し,Persistent Volume Claim は Kunernetes 上でPod を定義するユーザが定義するのが,しっくりくる運用ではないでしょうか.