Belajar Kubernetes - Episode 21.1 - PersistentVolume dan PersistentVolumeClaim

Belajar Kubernetes - Episode 21.1 - PersistentVolume dan PersistentVolumeClaim

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.

Arman Dwi Pangestu
Arman Dwi PangestuMarch 26, 2026
0 views
10 min read

Pendahuluan

Catatan

Untuk kalian yang ingin membaca episode sebelumnya, bisa click thumbnail episode 21 di bawah ini

Episode 21Episode 21

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.

Apa Itu PersistentVolume?

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:

  • Cluster-scoped - Tidak terikat ke Namespace manapun
  • Lifecycle independen - Exist melampaui lifecycle Pod manapun
  • Backend yang pluggable - NFS, iSCSI, cloud volume (EBS, GCE PD, Azure Disk), local disk, CSI driver
  • Access mode - RWO, ROX, RWX, RWOP
  • Reclaim policy - Apa yang terjadi pada PV ketika di-release
  • Storage capacity - Kapasitas tetap yang didefinisikan saat pembuatan

Apa Itu PersistentVolumeClaim?

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:

  • Namespace-scoped - Tergabung dalam Namespace tertentu
  • Declarative request - Menentukan ukuran, access mode, StorageClass
  • Binding - Kubernetes mengikat PVC ke PV yang cocok
  • Pod-consumable - Digunakan di spec Pod sebagai sumber volume
  • Dynamic provisioning - Bisa memicu pembuatan PV secara otomatis melalui StorageClass

Lifecycle PV dan PVC

Memahami lifecycle penuh ini mencegah kehilangan data dan miskonfigurasi.

Fase 1: Provisioning

PV bisa di-provision dengan dua cara:

  • Static provisioning - Admin secara manual membuat objek PV yang mengarah ke storage yang sudah ada
  • Dynamic provisioning - StorageClass secara otomatis membuat PV ketika PVC yang cocok di-submit

Fase 2: Binding

Kubernetes control plane memantau PVC yang belum terikat dan mencocokkannya dengan PV yang tersedia berdasarkan:

  1. Access mode yang diminta
  2. Ukuran storage yang diminta (PV harus >= request PVC)
  3. Nama StorageClass (jika ditentukan)
  4. Label selector (opsional)

Setelah kecocokan ditemukan, baik PV maupun PVC berpindah ke state Bound. Sebuah PV hanya bisa di-bind ke satu PVC pada satu waktu.

Fase 3: Using

Sebuah Pod mereferensikan PVC berdasarkan nama. Kubernetes me-mount storage yang mendasarinya ke dalam container di path yang ditentukan.

Fase 4: Release

Ketika sebuah PVC dihapus, PV masuk ke state Released. Apa yang terjadi selanjutnya tergantung pada Reclaim Policy:

  • Retain - PV tetap ada, data dipertahankan, memerlukan pembersihan manual sebelum dapat digunakan kembali
  • Delete - PV dan storage yang mendasarinya dihapus secara otomatis
  • Recycle (deprecated) - Basic scrub (rm -rf) dan bisa digunakan kembali

Fase 5: Reclaiming

Setelah 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.

Spec PersistentVolume

Manifest PV mendefinisikan storage backend dan karakteristiknya.

Contoh Static PV (Local Path)

Kubernetespv-local.yml
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-data

Contoh Static PV (NFS)

Kubernetespv-nfs.yml
apiVersion: 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

Penjelasan Field Spec PV

FieldDeskripsi
capacity.storageUkuran storage yang ditawarkan PV ini
accessModesCara volume bisa di-mount
storageClassNameMenghubungkan PV ke StorageClass
persistentVolumeReclaimPolicyApa yang terjadi ketika PVC dihapus
volumeModeFilesystem (default) atau Block
nodeAffinityMembatasi PV ke node tertentu (untuk local volume)

Spec PersistentVolumeClaim

PVC lebih sederhana — ia mendeskripsikan apa yang dibutuhkan aplikasi tanpa menentukan bagaimana atau di mana.

Contoh PVC Dasar

Kubernetespvc-basic.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: app-data-pvc
    namespace: production
spec:
    storageClassName: manual
    accessModes:
        - ReadWriteOnce
    resources:
        requests:
            storage: 10Gi

PVC dengan Label Selector

Kubernetespvc-with-selector.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: app-data-pvc
spec:
    storageClassName: manual
    accessModes:
        - ReadWriteOnce
    resources:
        requests:
            storage: 5Gi
    selector:
        matchLabels:
            type: local
            env: production

Tip

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

Access mode mendefinisikan bagaimana volume bisa di-mount di berbagai node. Tidak semua storage backend mendukung semua mode.

ModeSingkatanDeskripsi
ReadWriteOnceRWORead-write oleh satu node
ReadOnlyManyROXRead-only oleh banyak node
ReadWriteManyRWXRead-write oleh banyak node
ReadWriteOncePodRWOPRead-write oleh satu Pod (K8s 1.22+)
Kubernetesaccess-modes-reference.yml
# 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:
    - ReadWriteOncePod

Warning

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

Reclaim policy mengontrol apa yang terjadi pada storage yang mendasarinya ketika sebuah PVC dihapus.

Retain

Kubernetesyml
persistentVolumeReclaimPolicy: Retain
  • PV berpindah ke state Released
  • Data dipertahankan di storage backend
  • PV tidak bisa di-bind ulang ke PVC baru secara otomatis
  • Admin harus secara manual menghapus dan membuat ulang PV untuk menggunakannya kembali
  • Terbaik untuk: Database production di mana kehilangan data tidak dapat diterima

Delete

Kubernetesyml
persistentVolumeReclaimPolicy: Delete
  • PV dan resource storage yang mendasarinya dihapus secara otomatis
  • Terbaik untuk: Dynamic provisioning di mana ephemeral storage bisa diterima (lingkungan dev/test)

Perbandingan Retain vs Delete

apiVersion: v1
kind: PersistentVolume
metadata:
    name: db-pv-production
spec:
    capacity:
        storage: 100Gi
    accessModes:
        - ReadWriteOnce
    persistentVolumeReclaimPolicy: Retain
    storageClassName: fast-ssd
    # ... backend spec
Gunakan Retain untuk database production — data survive setelah PVC dihapus
Gunakan Retain untuk database production — data survive setelah PVC dihapus

StorageClass dan Dynamic Provisioning

Dengan 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.

Mendefinisikan StorageClass

Kubernetesstorageclass.yml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
    name: fast-ssd
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
allowVolumeExpansion: true

StorageClass dengan Cloud Provisioner (AWS EBS)

Kubernetesstorageclass-aws.yml
apiVersion: 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: WaitForFirstConsumer

PVC Dinamis Menggunakan StorageClass

Kubernetespvc-dynamic.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: app-data-pvc
    namespace: production
spec:
    storageClassName: fast-ssd
    accessModes:
        - ReadWriteOnce
    resources:
        requests:
            storage: 20Gi

Ketika 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.

Implementasi Praktis

Contoh 1: PostgreSQL dengan Static PV

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: DirectoryOrCreate

Contoh 2: Shared NFS Storage untuk Multiple Pod

Ketika multiple Pod perlu membaca dan menulis storage bersama yang sama (misalnya layanan upload file legacy dengan multiple replica), gunakan ReadWriteMany dengan PV berbasis NFS.

Kubernetesnfs-shared-storage.yml
apiVersion: v1
kind: PersistentVolume
metadata:
    name: nfs-shared-pv
spec:
    storageClassName: nfs
    capacity:
        storage: 100Gi
    accessModes:
        - ReadWriteMany
    persistentVolumeReclaimPolicy: Retain
    nfs:
        server: 10.0.0.50
        path: /exports/shared
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: nfs-shared-pvc
    namespace: production
spec:
    storageClassName: nfs
    accessModes:
        - ReadWriteMany
    resources:
        requests:
            storage: 50Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
    name: file-upload-service
    namespace: production
spec:
    replicas: 3
    selector:
        matchLabels:
            app: file-upload
    template:
        metadata:
            labels:
                app: file-upload
        spec:
            containers:
                - name: app
                  image: myapp:latest
                  volumeMounts:
                      - name: shared-uploads
                        mountPath: /var/uploads
            volumes:
                - name: shared-uploads
                  persistentVolumeClaim:
                      claimName: nfs-shared-pvc

Contoh 3: Mengecek Status PV dan PVC

Setelah apply manifest, verifikasi binding:

KubernetesCek Status PV dan PVC
# 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-pvc

Output yang diharapkan untuk PV yang sehat dan terikat:

bash
NAME           CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                    STORAGECLASS   AGE
postgres-pv    20Gi       RWO            Retain           Bound    default/postgres-pvc     manual         5m
nfs-shared-pv  100Gi      RWX            Retain           Bound    production/nfs-shared-pvc nfs           5m

Output yang diharapkan untuk PVC yang sehat dan terikat:

bash
NAMESPACE    NAME              STATUS   VOLUME          CAPACITY   ACCESS MODES   STORAGECLASS   AGE
default      postgres-pvc      Bound    postgres-pv     20Gi       RWO            manual         5m
production   nfs-shared-pvc    Bound    nfs-shared-pv   100Gi      RWX            nfs            5m

Contoh 4: Volume Expansion

Jika aplikasi tumbuh melebihi ukuran PVC awal, kamu bisa memperluas PVC tersebut (jika StorageClass memiliki allowVolumeExpansion: true).

Kubernetesexpand-pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: postgres-pvc
spec:
    storageClassName: fast-ssd
    accessModes:
        - ReadWriteOnce
    resources:
        requests:
            storage: 20Gi
            storage: 50Gi
Perluas dengan mengedit field resources.requests.storage pada PVC

Apply 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.

Kesalahan Umum dan Pitfall

Kesalahan 1: PVC Stuck di State Pending

Gejala: PVC tetap Pending tanpa batas waktu.

Penyebab dan cara fix:

PenyebabFix
Tidak ada PV dengan access mode yang cocokBuat PV dengan access mode yang benar
Kapasitas PV lebih kecil dari request PVCTingkatkan kapasitas PV atau kurangi request PVC
StorageClass tidak cocokPastikan storageClassName di PV dan PVC sama
Tidak ada default StorageClassTambahkan default StorageClass atau tentukan satu secara eksplisit
KubernetesDebug PVC yang Pending
sudo kubectl describe pvc <pvc-name>
# Lihat bagian "Events" untuk root cause-nya

Kesalahan 2: Data Hilang Setelah Pod Restart

Problem: Data menghilang setelah Pod restart.

Root cause: Menggunakan emptyDir atau hostPath alih-alih PVC.

Kubernetesyml
# 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-pvc

Kesalahan 3: Menghapus PVC di Production

Problem: 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.

Kesalahan 4: Namespace yang Salah untuk PVC

Problem: Pod tidak bisa menemukan PVC.

Root cause: PVC bersifat Namespace-scoped. Pod di namespace: production tidak bisa mereferensikan PVC di namespace: default.

KubernetesVerifikasi PVC Berada di Namespace yang Sama dengan Pod
sudo kubectl get pvc -n production
sudo kubectl get pod -n production

Kesalahan 5: Menggunakan hostPath PV di Cluster Multi-Node

Problem: 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).

Kuberneteshostpath-pv-with-node-affinity.yml
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-01

Best Practice

Selalu Set Reclaim Policy Secara Eksplisit

Jangan bergantung pada default. Selalu set persistentVolumeReclaimPolicy secara eksplisit:

Kubernetesyml
# Untuk database production
persistentVolumeReclaimPolicy: Retain
 
# Untuk ephemeral storage dev/test
persistentVolumeReclaimPolicy: Delete

Gunakan StorageClass untuk Dynamic Provisioning

Hindari manajemen PV statis dalam skala besar. Definisikan StorageClass yang jelas untuk tier yang berbeda:

Kubernetesstorageclass-tiered.yml
# 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: true

Beri Label pada PV Kamu

Beri label PV untuk kemudahan filtering dan selector-based binding:

Kubernetesyml
metadata:
    name: my-pv
    labels:
        env: production
        app: postgres
        tier: database

Aktifkan Volume Expansion pada StorageClass

Selalu aktifkan allowVolumeExpansion agar kamu bisa memperbesar PVC tanpa downtime:

Kubernetesyml
allowVolumeExpansion: true

Gunakan WaitForFirstConsumer sebagai Volume Binding Mode

Ini menunda binding PV sampai Pod dijadwal, memastikan PV dibuat di availability zone yang sama dengan Pod:

Kubernetesyml
volumeBindingMode: WaitForFirstConsumer

Important

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.

Monitor Status PV dan PVC

Integrasikan status PV/PVC ke dalam observability stack kamu:

KubernetesPantau Status PV/PVC
# 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 Bound

Kapan TIDAK Menggunakan PersistentVolume

PV dan PVC menambah kompleksitas. Terkadang solusi yang lebih sederhana adalah yang tepat:

  • Aplikasi yang benar-benar stateless — Jangan tambahkan PVC hanya karena bisa. Jika aplikasimu tidak menyimpan apapun, gunakan emptyDir atau tidak perlu volume sama sekali.
  • ConfigMap dan Secret untuk konfigurasi — Jangan persist konfigurasi di PVC. Gunakan volume configMap dan secret.
  • Batch job berumur pendek — Jika sebuah Job memproses data dan outputnya tidak perlu persist setelah job selesai, emptyDir sudah cukup.
  • Database managed eksternal — Jika organisasimu menjalankan database di luar Kubernetes (RDS, Cloud SQL, Managed PostgreSQL), jangan mereplikasinya di dalam cluster dengan PVC. Gunakan layanan managed tersebut dan referensikan melalui connection string.

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.

Penutup

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:

  • PersistentVolume adalah resource storage cluster-scoped yang independen dari Pod manapun
  • PersistentVolumeClaim adalah request storage yang bersifat Namespace-scoped
  • Kubernetes melakukan binding PVC ke PV yang sesuai berdasarkan access mode, kapasitas, dan StorageClass
  • Static provisioning membutuhkan pembuatan PV manual; dynamic provisioning menggunakan StorageClass
  • Reclaim Policy (Retain vs Delete) mengontrol apa yang terjadi pada data ketika PVC dihapus
  • Selalu gunakan Retain untuk database production untuk mencegah kehilangan data yang tidak disengaja
  • Access mode (RWO, ROX, RWX, RWOP) harus didukung oleh storage backend yang mendasarinya
  • Gunakan WaitForFirstConsumer di cluster multi-zone untuk menghindari kegagalan karena zone-mismatch
  • Aktifkan allowVolumeExpansion pada StorageClass untuk pertumbuhan volume secara online

Memahami 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

Episode 22Episode 22

Related Posts