KubernetesのVolume、PersistentVolume、PersistentVolumeClaimの違いを理解する
皆さん、こんにちは。技術開発グループのn-ozawanです。
まだ梅雨の季節ですが、夏の暑さがじわりと来たなと思う、今日この頃。
本題です。
Kubernetes、難しいですよね。コンテナでシステムを構築をする際、永続化は1つの課題になることでしょう。永続化する方法は主にDBが一般的だと思いますが、中にはファイルで永続化したいときもあると思います。今日はKubernetesでストレージをマウントする方法として、Volume、PersistentVolume、PersistentVolumeClaimの違いを整理します。
目次
Volume、PersistentVolume、PersistentVolumeClaimの違い
はじめに
コンテナ内部にファイルを保存することは出来ますが、コンテナを停止したタイミングで保存したファイルは消えてしまいます。なので、ファイルを永続化したいときは、コンテナの外にファイルを保存する必要があります。
Kubernetesが提供するVolume、PersistentVolume、PersistentVolumeClaimは、データを永続化するための仕組みを提供します。
ゴール
AWSのElastic File System (EFS)にファイルを永続化できるところまでを今回のゴールとします。AWSでの永続化には他にElastic Block Store (EBS)がありますが、EBSは1つのノードしかマウントできません。複数ノードで冗長化できるKubernetesでは不都合がありますので、EFSを利用すると良いでしょう。

Volumeとは
Volumeはあらかじめ用意されたボリュームを、リソースを作成することなく、マニフェストに直接指定することで利用可能にするものです。この「あらかじめ用意されたボリューム」には色々な種類が用意されています。詳しくはKubernetesのページ「Volumes」を参照してください。
Volumeでよく使われる種類は、おそらくConfigMapとSecretだと思います。ただ、この2つを説明すると記事が1本書けてしまうので、別の機会にお話しします。今日は、コンテナが稼働しているノードの領域をマウントするhostPathについて、簡単に説明します。以下は、hostPathのイメージとマニフェストです。

apiVersion: v1
kind: Pod
metadata:
name: sample-pod
spec:
containers:
- name: nginx-container
image: nginx:latest
volumeMounts:
- name: template-volume
mountPath: /home/hoge
volumes:
- name: template-volume
hostPath:
path: /tmp
type: Directory
spec.volumes
には「あらかじめ用意されたボリューム」の情報を記述します。spec.volumes.hostPath
のpath
にはノードのパスを指定します。type
には指定したパスがディレクトリであることを明示します。
spec.containers.volumeMounts
にはボリュームのマウントを指定します。name
にはマウントしたいspec.volumes.name
を指定します。mountPath
にはマウント先のパスを指定します。これはコンテナ内部のパスです。
上記のマニフェストにより、コンテナが稼働しているノードの/tmpを、コンテナ内部の/home/hogeにマウントする、という意味になります。
PersistentVolume (PV) とは
PersistentVolume (以降、PV) とは、外部の永続ボリュームを提供するシステムと連携して、永続化領域を確保するボリュームです。AWSでは外部の永続ボリュームとしてEFSを提供しています。
PVは個別にリソースを作成する必要があります。PVはボリュームリソースを表現するだけで、それ自体で何かが出来るわけではありません。PVを利用するには、コンテナとPVを結ぶ、PersistentVolumeClaimが必要になります。PersistentVolumeClaimについては後述します。
以下はAWS EFSと、そのPVとなります。EFSの作成方法について今回の趣旨と異なりますので割愛します。

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: efs-sc
provisioner: efs.csi.aws.com
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: efs-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: efs-sc
csi:
driver: efs.csi.aws.com
volumeHandle: fs-xxxxxxxx
PVを作成する前に、StorageClassリソースを作成しています。StorageClassはストレージの種類を記述します。例えば、今回であれば、AWS EFSを利用したストレージである、となります。provisioner
に「efs.csi.aws.com」を指定しているのがそれです。
次にPVを作成します。spec
にPVに関する情報を記述します。capacity.storage
にはそのストレージの容量を記述します。EFSは容量の上限がないので意味のない記述ですが、PVでは必須項目となっているため、それっぽい数値を当てています。
accessModes
はそのボリュームへのマウントについて記述します。ReadWriteMany
は、そのボリュームが複数のノードで読み取り/書き込みとしてマウントされることを示します。
persistentVolumeReclaimPolicy
はそのボリュームの利用が終了したとき(コンテナが停止したなど)、そのボリュームの後処理を指定します。「Retain」はデータを消さずにそのまま残す、という意味です。他には「Delete」があり、その名の通り、ボリューム利用後はデータを削除します。
storageClassName
は先ほど作成したStorageClassの名前を指定します。
csi
には実際に利用するEFSを指定します。driver
は「efs.csi.aws.com」固定です。volumeHandle
はEFSのファイルシステムIDを指定します。これにより、PVとEFSのリソースが紐付きます。
PersistentVolumeClaim (PVC) とは
先にも述べた通り、PVはそれ単体では意味を成しません。PersistentVolumeClaim (以降、PVC) によりコンテナとPVを紐づけます。「Claim」には「請求する」や「要求する」という意味があります。つまり、PVCには、そのコンテナが欲してるボリュームを記述します。これにより、Kubernetesはコンテナが欲しているボリュームと紐付けてくれます。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: efs-claim
spec:
accessModes:
- ReadWriteMany
storageClassName: efs-sc
resources:
requests:
storage: 5Gi
spec
には要望するボリュームの条件が記述されます。上記のマニフェストでは、容量、アクセスモード、StorageClassが条件として記述されています。他にはボリュームに付与されたラベルでも条件指定が可能です。
PVをコンテナにマウントする
では実際にコンテナにマウントしてみましょう。
apiVersion: v1
kind: Pod
metadata:
name: sample-pod
spec:
containers:
- name: nginx-container
image: nginx:latest
volumeMounts:
- name: persistent-storage
mountPath: /home/hoge
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: efs-claim
先ほど「Volumeとは」で説明した通り、spec.volumes
には「あらかじめ用意されたボリューム」の情報を記述します。今回用意されたボリュームはPVとPVCです。なので、spec.volumes.persistentVolumeClaim.claimName
には先ほど作成したPVCの名前を指定します。これにより、PVCで要望したボリュームがこのコンテナのボリュームとして紐づきます。
spec.containers.volumeMounts
も「Volumeとは」で説明した通りです。mountPath
にはマウント先のパスを指定します。
以上により、EFSのファイルシステムが、コンテナ内部の/home/hogeにマウントされます。
おわりに
難しいですね。コンテナが利用するボリュームはKubernetesが決める、という仕様を理解するまで、私はPVとPVCの関係性を理解するのに少し時間がかかってしまいました。しかし、このPVとPVCの関係により、コンテナとボリュームが疎結合となり、コンテナの可搬性が高まります。今回の話で言うと、コンテナはボリュームの正体がEFSかどうか気にする必要がなくなった、というのがメリットですね。
ではまた。