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.hostPathpathにはノードのパスを指定します。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かどうか気にする必要がなくなった、というのがメリットですね。

ではまた。

採用情報

「チームアイオス」入団者募集

〜就活で悩むアナタに来て欲しい〜