Di episode ini kita akan membahas secara mendalam PersistentVolume (PV) dan PersistentVolumeClaim (PVC) di Kubernetes. Kamu akan belajar bagaimana Kubernetes mengabstraksi storage, cara PV di-provision, dan bagaimana aplikasi mengklaim storage melalui PVC.

Catatan
Untuk kalian yang ingin membaca episode sebelumnya, bisa click thumbnail episode 21 di bawah ini
Di episode sebelumnya, kita sudah menjelajahi Volume di Kubernetes dan sedikit menyinggung persistentVolumeClaim sebagai salah satu jenis volume. Di episode ini (21.1), kita akan lebih dalam dan fokus sepenuhnya pada PersistentVolume (PV) dan PersistentVolumeClaim (PVC) — dua objek inti yang digunakan Kubernetes untuk memisahkan provisioning storage dari konsumsi storage oleh aplikasi.
Catatan: Disini saya akan menggunakan Kubernetes Cluster yang di install melalui K3s.
Tanpa PV dan PVC, setiap tim yang men-deploy stateful application harus mengelola storage backend secara manual. Dengan PV dan PVC, lifecycle storage bisa dikelola secara independen dari lifecycle aplikasi — sehingga memungkinkan kita menjalankan database, message queue, dan file storage secara andal di Kubernetes.
PersistentVolume (PV) adalah sepotong storage di dalam cluster yang sudah di-provision oleh administrator atau di-provision secara otomatis menggunakan StorageClass. PV adalah resource level cluster — artinya ia exist secara independen dari Pod atau Namespace manapun.
Bayangkan PV seperti disk fisik yang sudah diformat dan disediakan oleh tim IT. Disk itu ada dan siap digunakan. Detail teknis di balik disk itu (NFS server, cloud block storage, local disk) diabstraksi di balik API object PV.
Karakteristik kunci PersistentVolume:
PersistentVolumeClaim (PVC) adalah request storage dari user atau aplikasi. PVC adalah Namespace-scoped dan menentukan kebutuhan seperti ukuran storage dan access mode. Kubernetes akan menemukan PV yang sesuai dan mengikat (binding) keduanya.
Bayangkan PVC seperti purchase order. Developer bilang: "Saya butuh 10Gi storage read-write." Kubernetes kemudian mencari PV yang cocok dan mengikat keduanya.
Karakteristik kunci PersistentVolumeClaim:
Memahami lifecycle penuh ini mencegah kehilangan data dan miskonfigurasi.
PV bisa di-provision dengan dua cara:
StorageClass secara otomatis membuat PV ketika PVC yang cocok di-submitKubernetes control plane memantau PVC yang belum terikat dan mencocokkannya dengan PV yang tersedia berdasarkan:
Setelah kecocokan ditemukan, baik PV maupun PVC berpindah ke state Bound. Sebuah PV hanya bisa di-bind ke satu PVC pada satu waktu.
Sebuah Pod mereferensikan PVC berdasarkan nama. Kubernetes me-mount storage yang mendasarinya ke dalam container di path yang ditentukan.
Ketika sebuah PVC dihapus, PV masuk ke state Released. Apa yang terjadi selanjutnya tergantung pada Reclaim Policy:
rm -rf) dan bisa digunakan kembaliSetelah Released, sebuah PV dengan policy Retain menyimpan data dan tidak bisa di-bind ke PVC baru tanpa intervensi manual. Admin harus menghapus dan membuat ulang PV (atau membersihkan storage yang mendasarinya) untuk menggunakannya kembali.
Manifest PV mendefinisikan storage backend dan karakteristiknya.
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-local-data
labels:
type: local
env: production
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
hostPath:
path: /data/k8s/pv-local-dataapiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-data
spec:
storageClassName: nfs
capacity:
storage: 50Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
server: 192.168.1.100
path: /exports/k8s-data| Field | Deskripsi |
|---|---|
capacity.storage | Ukuran storage yang ditawarkan PV ini |
accessModes | Cara volume bisa di-mount |
storageClassName | Menghubungkan PV ke StorageClass |
persistentVolumeReclaimPolicy | Apa yang terjadi ketika PVC dihapus |
volumeMode | Filesystem (default) atau Block |
nodeAffinity | Membatasi PV ke node tertentu (untuk local volume) |
PVC lebih sederhana — ia mendeskripsikan apa yang dibutuhkan aplikasi tanpa menentukan bagaimana atau di mana.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data-pvc
namespace: production
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10GiapiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data-pvc
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
selector:
matchLabels:
type: local
env: productionTip
Gunakan label selector ketika kamu ingin mem-bind PVC ke PV tertentu, bukan hanya PV manapun yang memenuhi persyaratan ukuran dan access mode. Ini berguna untuk memastikan aplikasi tertentu selalu mendapatkan storage yang sama.
Access mode mendefinisikan bagaimana volume bisa di-mount di berbagai node. Tidak semua storage backend mendukung semua mode.
| Mode | Singkatan | Deskripsi |
|---|---|---|
ReadWriteOnce | RWO | Read-write oleh satu node |
ReadOnlyMany | ROX | Read-only oleh banyak node |
ReadWriteMany | RWX | Read-write oleh banyak node |
ReadWriteOncePod | RWOP | Read-write oleh satu Pod (K8s 1.22+) |
# Database single-node (MySQL, PostgreSQL)
accessModes:
- ReadWriteOnce
# Config atau static asset yang dibaca bersama
accessModes:
- ReadOnlyMany
# Shared writable storage (NFS, CephFS)
accessModes:
- ReadWriteMany
# Jaminan ketat hanya satu Pod
accessModes:
- ReadWriteOncePodWarning
Access mode adalah deklarasi kemampuan, bukan mekanisme enforcement di level node. ReadWriteOnce berarti volume hanya bisa di-mount sebagai read-write pada satu node dalam satu waktu — tetapi beberapa Pod di node yang sama bisa menggunakannya secara bersamaan.
Reclaim policy mengontrol apa yang terjadi pada storage yang mendasarinya ketika sebuah PVC dihapus.
persistentVolumeReclaimPolicy: RetainReleasedpersistentVolumeReclaimPolicy: DeleteapiVersion: v1
kind: PersistentVolume
metadata:
name: db-pv-production
spec:
capacity:
storage: 100Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: fast-ssd
# ... backend specDengan static provisioning, admin harus membuat PV terlebih dahulu. Dengan dynamic provisioning, sebuah StorageClass secara otomatis membuat PV ketika PVC yang cocok di-submit — tidak perlu intervensi admin.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
allowVolumeExpansion: trueapiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: aws-ebs-gp3
provisioner: ebs.csi.aws.com
parameters:
type: gp3
iopsPerGB: "3000"
throughput: "125"
encrypted: "true"
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumerapiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: app-data-pvc
namespace: production
spec:
storageClassName: fast-ssd
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20GiKetika PVC ini dibuat, StorageClass fast-ssd memicu pembuatan dan binding PV secara otomatis. Tidak perlu membuat PV secara manual.
Tip
Atur default StorageClass di cluster kamu agar PVC yang tidak menentukan storageClassName ditangani secara otomatis. Di K3s, local-path adalah StorageClass default.
Ini adalah pola paling umum untuk database yang dikelola sendiri: admin membuat PV di disk lokal yang cepat, dan workload database mengklaim storage tersebut.
apiVersion: v1
kind: PersistentVolume
metadata:
name: postgres-pv
labels:
app: postgres
spec:
storageClassName: manual
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
hostPath:
path: /data/k8s/postgres
type: DirectoryOrCreateKetika multiple Pod perlu membaca dan menulis storage bersama yang sama (misalnya layanan upload file legacy dengan multiple replica), gunakan ReadWriteMany dengan PV berbasis NFS.
Setelah apply manifest, verifikasi binding:
# List semua PersistentVolume
sudo kubectl get pv
# List semua PersistentVolumeClaim
sudo kubectl get pvc
# List PVC di namespace tertentu
sudo kubectl get pvc -n production
# Describe PV tertentu untuk informasi detail
sudo kubectl describe pv postgres-pv
# Describe PVC tertentu
sudo kubectl describe pvc postgres-pvcOutput yang diharapkan untuk PV yang sehat dan terikat:
Output yang diharapkan untuk PVC yang sehat dan terikat:
Jika aplikasi tumbuh melebihi ukuran PVC awal, kamu bisa memperluas PVC tersebut (jika StorageClass memiliki allowVolumeExpansion: true).
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
spec:
storageClassName: fast-ssd
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storage: 50GiApply perubahannya dan Kubernetes akan memicu operasi resize pada storage yang mendasarinya.
Caution
Pengecilan volume tidak didukung. Kamu hanya bisa memperluas PVC, tidak bisa mengecilkannya. Rencanakan ukuran awal secara konservatif, tapi tidak terlalu kecil — sisakan ruang untuk pertumbuhan.
Gejala: PVC tetap Pending tanpa batas waktu.
Penyebab dan cara fix:
| Penyebab | Fix |
|---|---|
| Tidak ada PV dengan access mode yang cocok | Buat PV dengan access mode yang benar |
| Kapasitas PV lebih kecil dari request PVC | Tingkatkan kapasitas PV atau kurangi request PVC |
| StorageClass tidak cocok | Pastikan storageClassName di PV dan PVC sama |
| Tidak ada default StorageClass | Tambahkan default StorageClass atau tentukan satu secara eksplisit |
sudo kubectl describe pvc <pvc-name>
# Lihat bagian "Events" untuk root cause-nyaProblem: Data menghilang setelah Pod restart.
Root cause: Menggunakan emptyDir atau hostPath alih-alih PVC.
# Buruk: data hilang ketika Pod dihapus atau dijadwal ulang
volumes:
- name: data
emptyDir: {}
# Bagus: data persist melewati Pod restart dan penjadwalan ulang
volumes:
- name: data
persistentVolumeClaim:
claimName: app-data-pvcProblem: Tidak sengaja menghapus PVC yang menghapus data aplikasi.
Pencegahan: Gunakan reclaim policy Retain dan lindungi PVC dengan finalizer atau kebijakan RBAC yang mencegah penghapusan yang tidak disengaja.
Warning
Penghapusan PVC dengan reclaim policy Delete akan secara permanen menghancurkan storage yang mendasarinya beserta semua datanya. Tidak ada undo. Selalu gunakan Retain untuk stateful workload di production.
Problem: Pod tidak bisa menemukan PVC.
Root cause: PVC bersifat Namespace-scoped. Pod di namespace: production tidak bisa mereferensikan PVC di namespace: default.
sudo kubectl get pvc -n production
sudo kubectl get pod -n productionProblem: Pod dijadwal ulang ke node yang berbeda dan data storage tidak lagi tersedia.
Root cause: hostPath PV terikat ke node tertentu. Ketika Pod berpindah ke node lain, data tidak ditemukan.
Fix: Gunakan nodeAffinity untuk menjaga Pod tetap di node dengan data hostPath, atau lebih baik — gunakan networked storage yang proper (NFS, CSI driver, cloud volume).
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /data/k8s
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node-01Jangan bergantung pada default. Selalu set persistentVolumeReclaimPolicy secara eksplisit:
# Untuk database production
persistentVolumeReclaimPolicy: Retain
# Untuk ephemeral storage dev/test
persistentVolumeReclaimPolicy: DeleteHindari manajemen PV statis dalam skala besar. Definisikan StorageClass yang jelas untuk tier yang berbeda:
# High-performance SSD untuk database
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
annotations:
storageclass.kubernetes.io/is-default-class: "false"
provisioner: ebs.csi.aws.com
parameters:
type: gp3
reclaimPolicy: Retain
allowVolumeExpansion: true
---
# Standard HDD untuk backup/log
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard-hdd
provisioner: ebs.csi.aws.com
parameters:
type: st1
reclaimPolicy: Delete
allowVolumeExpansion: trueBeri label PV untuk kemudahan filtering dan selector-based binding:
metadata:
name: my-pv
labels:
env: production
app: postgres
tier: databaseSelalu aktifkan allowVolumeExpansion agar kamu bisa memperbesar PVC tanpa downtime:
allowVolumeExpansion: trueWaitForFirstConsumer sebagai Volume Binding ModeIni menunda binding PV sampai Pod dijadwal, memastikan PV dibuat di availability zone yang sama dengan Pod:
volumeBindingMode: WaitForFirstConsumerImportant
Tanpa WaitForFirstConsumer, sebuah PV bisa di-provision di availability zone yang berbeda dari tempat Pod berada, menyebabkan kegagalan scheduling. Ini sangat penting di lingkungan cloud multi-zone.
Integrasikan status PV/PVC ke dalam observability stack kamu:
# Pantau status secara berkelanjutan
sudo kubectl get pv,pvc --all-namespaces -w
# Cek PV yang Released atau Failed (potensi orphan)
sudo kubectl get pv | grep -v BoundPV dan PVC menambah kompleksitas. Terkadang solusi yang lebih sederhana adalah yang tepat:
emptyDir atau tidak perlu volume sama sekali.configMap dan secret.emptyDir sudah cukup.Note
PV dan PVC bersinar ketika kamu perlu menyimpan state aplikasi di dalam cluster. Untuk semua hal lainnya, manfaatkan managed service atau jenis volume yang lebih sederhana.
Pada episode 21.1 ini, kita telah membahas PersistentVolume (PV) dan PersistentVolumeClaim (PVC) secara mendalam. Dua objek ini adalah tulang punggung storage stateful application di Kubernetes.
Key takeaway:
Retain vs Delete) mengontrol apa yang terjadi pada data ketika PVC dihapusRetain untuk database production untuk mencegah kehilangan data yang tidak disengajaWaitForFirstConsumer di cluster multi-zone untuk menghindari kegagalan karena zone-mismatchallowVolumeExpansion pada StorageClass untuk pertumbuhan volume secara onlineMemahami PV dan PVC menempatkan kamu dalam kendali penuh atas manajemen storage di Kubernetes — kemampuan kritis untuk menjalankan stateful application secara andal di production.
Bagaimana, makin jelas kan bagaimana Kubernetes mengelola persistent storage? Jadi, pastikan tetap semangat belajar dan nantikan episode selanjutnya!
Catatan
Untuk kalian yang ingin melanjutkan ke episode selanjutnya, bisa click thumbnail episode 22 di bawah ini