2026年7月28日
2026年7月28日
KubernetesのPersistentVolumeとPVCの使い方|動的・静的プロビジョニング
Kubernetes の Pod の中のファイルは、Pod を削除したり作り直したりすると消えます。データベースのデータやアップロードされたファイルのように残しておきたいデータは、PersistentVolume(PV)に保存します。
この記事では、PV と PersistentVolumeClaim(PVC)の関係を整理し、次の2つの方法で Pod にストレージを付ける手順を説明します。
- 動的プロビジョニング:PVC を作ると、StorageClass に従って PV が自動で作られる。クラウドのマネージド Kubernetes や多くのクラスターでの通常の方法
- 静的プロビジョニング:管理者が PV を手で作り、PVC をそれに結び付ける
マニフェストと出力は、2026年10月時点の Kubernetes 1.34(k3s、既定の StorageClass は local-path)で確認しています。
PV・PVC・StorageClassの関係
| リソース | 役割 | 作る人 |
|---|---|---|
| PersistentVolume(PV) | 実際のストレージ(ディスク、NFS、クラウドのブロックストレージなど)を表す。クラスター全体のリソース | 管理者、または StorageClass による自動作成 |
| PersistentVolumeClaim(PVC) | 「この容量・このアクセスモードのストレージが欲しい」という要求。名前空間ごとのリソース | アプリを動かす人 |
| StorageClass | PV を自動で作るときの種類(プロビジョナーやパラメーター) | 管理者 |
Pod は PVC を名前で指定し、PVC に結び付けられた(Bound の)PV がマウントされます。Pod を作り直しても、同じ PVC を使えば同じデータが見えます。
まずStorageClassを確かめる
kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path (default) rancher.io/local-path Delete WaitForFirstConsumer false 2s
(default) が付いた StorageClass があれば、PVC を作るだけで PV が自動で作られます(動的プロビジョニング)。表示される名前やプロビジョナーはクラスターによって違います。何も表示されない場合は、静的プロビジョニングで PV を自分で用意するか、ストレージのドライバー(CSI ドライバー)を導入します。
動的プロビジョニングで使う
PVC と、それをマウントする Pod を1つのファイル(web.yaml)に書きます。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: web-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: nginx
image: nginx:1.28
volumeMounts:
- name: data
mountPath: /usr/share/nginx/html
volumes:
- name: data
persistentVolumeClaim:
claimName: web-data
storageClassName を書いていないので、既定の StorageClass が使われます。特定の StorageClass を使う場合は、PVC の spec に storageClassName: 名前 を加えます。
kubectl apply -f web.yaml
kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
web-data Bound pvc-843d1db2-d309-40e5-b160-93506f1dc039 1Gi RWO local-path <unset> 18s
STATUS が Bound になれば、PV が作られて PVC に結び付いています。StorageClass の VOLUMEBINDINGMODE が WaitForFirstConsumer の場合、PVC を使う Pod が作られるまでは Pending のままです。これは正常な動きです。
データが残ることを確かめる
Pod の中でファイルを作り、Pod を削除して作り直しても残っているかを確かめます。
kubectl exec web -- sh -c 'echo hello-pv > /usr/share/nginx/html/index.html'
kubectl delete pod web
kubectl apply -f web.yaml
kubectl exec web -- cat /usr/share/nginx/html/index.html
最後のコマンドで hello-pv と表示されれば、データは PV に保存されています。
実際の運用では、Pod を直接作るより Deployment(複数の Pod でそれぞれ別のボリュームが必要なら StatefulSet の volumeClaimTemplates)から PVC を使うのが一般的です。指定の方法(volumes の persistentVolumeClaim)は同じです。
静的プロビジョニングで使う
既存の NFS サーバーやディスクを使いたい場合など、PV を自分で作る方法です。次の例は、ノードのディレクトリーを使う hostPath の PV です。hostPath は1台のノードでの検証用で、Pod が別のノードで動くとデータが見えなくなるため、本番では使いません。
apiVersion: v1
kind: PersistentVolume
metadata:
name: manual-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: manual
hostPath:
path: /srv/k8s-data/manual-pv
type: DirectoryOrCreate
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: manual-pvc
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
kubectl apply -f manual.yaml
kubectl get pv manual-pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
manual-pv 5Gi RWO Retain Bound default/manual-pvc manual <unset> 3s
ポイントは次のとおりです。
- PV には、
hostPath、nfs、csi、localなどのボリュームの種類を1つ書く必要があります。種類のない PV はspec: Required value: must specify a volume typeというエラーで作れません - PVC は、
storageClassNameが同じで、容量とアクセスモードの条件を満たす PV に結び付きます - PVC の
storageClassNameを省略すると、既定の StorageClass が使われて新しい PV が自動で作られ、手で作った PV には結び付きません。静的な PV を使うときは、PV と PVC の両方に同じstorageClassNameを書きます(StorageClass のリソースを作らずに、manualのような名前を合わせるだけでも結び付きます)
localボリュームを使う場合
ノードに付けたディスクを本番で使う場合は、hostPath ではなく local ボリュームを使います。local の PV には、そのディスクがあるノードを nodeAffinity で指定する必要があり、StorageClass は WaitForFirstConsumer にします。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv-1
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /mnt/disks/vol1
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- worker-1
worker-1 は kubectl get nodes で表示されるノード名に、/mnt/disks/vol1 はそのノードで実際にマウントしてあるディレクトリーに置き換えます。kubernetes.io/no-provisioner は自動で PV を作らないので、ディスクごとに PV を用意します。
アクセスモード
| アクセスモード | 省略形 | 意味 |
|---|---|---|
ReadWriteOnce |
RWO | 1つのノードから読み書きできる(同じノード上の複数の Pod からは使える) |
ReadOnlyMany |
ROX | 複数のノードから読み取りだけできる |
ReadWriteMany |
RWX | 複数のノードから読み書きできる |
ReadWriteOncePod |
RWOP | 1つの Pod だけが読み書きできる |
使えるアクセスモードは、ストレージの種類によって決まります。たとえばクラウドのブロックストレージは通常 RWO だけで、複数のノードの Pod から同じデータを読み書きしたい(RWX)場合は、NFS などの共有ファイルシステムを使います。
回収ポリシー(PVCを削除したときのPV)
PV の persistentVolumeReclaimPolicy で、PVC を削除したときに PV とデータをどうするかが決まります。
| ポリシー | PVC を削除したとき |
|---|---|
Retain |
PV は Released になり、データは残る。管理者が手で後始末する |
Delete |
PV と、元になったストレージ(クラウドのディスクなど)も削除される |
動的プロビジョニングで作られた PV は、StorageClass の reclaimPolicy(多くは Delete)を引き継ぎます。PVC を削除するとデータも消える設定になっていることが多いので、大事なデータの PVC を削除する前に確認してください。Recycle は非推奨で、使いません。
Retain の PV は、PVC を削除すると Released になり、そのままでは新しい PVC に結び付きません。データを確認したうえで同じ PV を再び使う場合は、前の PVC への参照(claimRef)を外すと Available に戻ります。
kubectl patch pv manual-pv --type json -p '[{"op":"remove","path":"/spec/claimRef"}]'
容量を増やす
StorageClass の allowVolumeExpansion が true なら、PVC の spec.resources.requests.storage を大きな値に変えるだけで容量を増やせます。
kubectl patch pvc web-data -p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'
容量を減らすことはできません。allowVolumeExpansion が false(または未設定)の StorageClass では、この変更は拒否されます。
PVCがPendingのままのとき
kubectl describe pvc の最後の Events に理由が表示されます。
kubectl describe pvc PVC名
Events の例 |
原因と対処 |
|---|---|
waiting for first consumer to be created before binding |
WaitForFirstConsumer の StorageClass で、まだ PVC を使う Pod がない。Pod を作れば進む |
storageclass.storage.k8s.io "manual" not found |
指定した storageClassName に合う PV も StorageClass もない。PV の容量・アクセスモード・storageClassName が PVC と合っているか確かめる |
no persistent volumes available for this claim and no storage class is set |
storageClassName が空で、既定の StorageClass もなく、条件に合う PV もない |
Waiting for a volume to be created either by the external provisioner ... が続く |
プロビジョナー(CSI ドライバーなど)が動いていない、またはエラーになっている。プロビジョナーの Pod のログを確認する |
PV の一覧と状態は kubectl get pv で確かめられます。Available は未使用、Bound は使用中、Released は PVC が削除されたあと、Failed は自動の回収に失敗した状態です。
まとめ
- Pod は PVC を指定し、PVC に結び付いた PV がマウントされる。Pod を作り直してもデータは残る
- 既定の StorageClass があれば、PVC を作るだけで PV が自動で作られる(動的プロビジョニング)
- PV を手で作るときは、ボリュームの種類(
hostPath・nfs・localなど)を書き、PV と PVC のstorageClassNameをそろえる - アクセスモードはストレージの種類で決まる。複数ノードからの読み書き(RWX)には共有ファイルシステムが必要
- 動的に作られた PV は
Deleteのことが多く、PVC を削除するとデータも消える。大事なデータはRetainやバックアップで守る - PVC が
Pendingのままなら、kubectl describe pvcのEventsで理由を確かめる
関連記事: