PersistentVolumeClaim #

Di dalam ekosistem Kubernetes, pengembang aplikasi (application developers) idealnya tidak perlu direpotkan oleh detail teknis infrastruktur penyimpanan—seperti jenis hardware SAN yang digunakan, alamat IP server NFS, atau kredensial API cloud provider untuk membuat disk baru. Pengembang hanya perlu mendeklarasikan kebutuhan penyimpanan aplikasi mereka secara logis: berapa kapasitas yang dibutuhkan, bagaimana data akan diakses, dan tipe performa storage yang diinginkan.

Untuk mewujudkan kebutuhan tersebut, Kubernetes menyediakan objek PersistentVolumeClaim (PVC). PVC bertindak sebagai tiket permintaan atau “klaim” atas ruang penyimpanan yang diajukan oleh pengguna di tingkat namespace (namespaced resource). Kubernetes kemudian akan bertanggung jawab penuh untuk mencari PersistentVolume (PV) fisik yang cocok, atau membuatkannya secara otomatis melalui StorageClass. Artikel ini akan membedah secara mendalam anatomi manifes PVC, algoritma pencocokan binding, implementasi di dalam Pod dan StatefulSet, panduan troubleshooting PVC yang stuck, hingga mekanisme penskalan kapasitas (volume expansion).


Alur Binding: Bagaimana PVC Menemukan PV Fisik #

Ketika kita membuat objek PVC baru, ia berada dalam kondisi awal Pending. Kontroler internal Kubernetes (khususnya volume binding controller) secara berkala memantau PVC baru dan berupaya memasangkannya dengan objek PersistentVolume (PV) fisik yang berstatus Available.

Berikut adalah alur keputusan logis yang dilalui Kubernetes untuk menghubungkan PVC ke PV:

flowchart TD
    PVC_Create["PVC Dibuat (Pending)"] --> SC_Check{"Apakah\nstorageClassName\ndidefinisikan?"}
    
    SC_Check -- "Tidak / Kosong" --> StaticSearch["Cari PV Available yang Cocok Manual"]
    StaticSearch --> MatchCheck{"Apakah PV yang\ncocok ditemukan?"}
    
    MatchCheck -- "Ya" --> StaticBind["Lakukan Binding (Status: Bound)"]
    MatchCheck -- "Tidak" --> StuckPending["Stuck di Pending (Menunggu PV Dibuat Admin)"]
    
    SC_Check -- "Ya (Nama Class)" --> DynamicCheck{"Apakah StorageClass\nmendukung dynamic\nprovisioning?"}
    
    DynamicCheck -- "Ya" --> ProvisionStart["Kirim API Request ke CSI Driver"]
    ProvisionStart --> CloudDisk["Cloud Provider Membuat Disk Fisik"]
    CloudDisk --> PV_Auto["Kubernetes Membuat Objek PV Secara Otomatis"]
    PV_Auto --> DynamicBind["Lakukan Binding Otomatis (Status: Bound)"]
    
    DynamicCheck -- "Tidak" --> StaticSearch

Kriteria Pencocokan yang Ketat: #

Sebuah PV hanya dapat dinyatakan cocok untuk di-bind ke PVC jika memenuhi seluruh kriteria berikut:

  1. Kecocokan StorageClass: Nilai storageClassName pada PVC harus cocok dengan storageClassName yang tertulis pada PV.
  2. Kapasitas Cukup: Kapasitas penyimpanan fisik PV harus lebih besar atau sama dengan (>=) nilai request yang diminta PVC.
  3. Akses Kompatibel: Nilai accessModes yang didukung oleh PV harus mencakup mode akses yang diminta oleh PVC (misalnya, jika PVC meminta ReadWriteMany, maka PV juga wajib mendukung ReadWriteMany).
  4. Status Siap: Status PV harus berada dalam fase Available (belum terikat ke PVC lain).

[!IMPORTANT] Sistem binding Kubernetes menggunakan algoritma pencocokan pertama yang memuaskan syarat (first-fit algorithm), bukan pencocokan yang paling efisien (best-fit). Jika kita membuat PVC yang meminta kapasitas 10Gi, dan di kluster terdapat dua PV Available: pv-a berukuran 100Gi dan pv-b berukuran 10Gi, Kubernetes bisa saja mengikat PVC kita ke pv-a 100Gi. Hal ini mengakibatkan pemborosan sisa kapasitas 90GiB yang akhirnya tidak bisa digunakan oleh siapa pun. Untuk menghindari ini, kita dapat mengunci binding ke PV tertentu menggunakan properti spec.volumeName di PVC.


Anatomi Manifes PersistentVolumeClaim #

Berikut adalah manifes PVC tingkat produksi yang meminta volume penyimpanan kelas premium:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pg-data-pvc
  namespace: production              # PVC bersifat namespaced
spec:
  accessModes:
    - ReadWriteOnce                  # Mode akses yang diinginkan
  volumeMode: Filesystem             # Filesystem | Block
  resources:
    requests:
      storage: 50Gi                  # Kapasitas minimum yang diminta
  storageClassName: premium-ssd-sc   # Menunjuk ke StorageClass dynamic provisioning
  selector:                          # Opsional: Memilih PV statis dengan label
    matchLabels:
      tier: fast

Penjelasan Parameter Kunci: #

  • namespace: PVC dikunci di dalam namespace tertentu. Hanya Pod yang berjalan di namespace yang sama dengan PVC yang diperbolehkan untuk me-mount volume tersebut.
  • resources.requests.storage: Batas kapasitas minimal yang diminta oleh aplikasi. Jika StorageClass memicu pembuatan disk cloud secara dinamis, disk fisik yang dibuat akan memiliki ukuran sesuai nilai ini.
  • selector: Digunakan jika kita ingin melakukan pencocokan PV secara statis (static provisioning) berdasarkan label penanda pada PV, bukan menggunakan dynamic provisioning dari StorageClass.

Menggunakan PVC di dalam Pod Spec #

Setelah status PVC berubah menjadi Bound, kita dapat langsung menggunakannya sebagai volume di dalam manifes spesifikasi Pod. Kita melakukan ini dengan menambahkan blok spec.volumes[*].persistentVolumeClaim dan menunjuk ke nama PVC kita:

apiVersion: v1
kind: Pod
metadata:
  name: postgres-app
  namespace: production
spec:
  containers:
  - name: postgres
    image: postgres:15
    ports:
    - containerPort: 5432
    volumeMounts:
    - name: pg-storage               # Menunjuk ke nama volume di bawah
      mountPath: /var/lib/postgresql/data
  volumes:
  - name: pg-storage
    persistentVolumeClaim:
      claimName: pg-data-pvc         # Nama PVC yang kita buat sebelumnya
      readOnly: false

Daur Hidup Independen: #

Satu hal yang wajib dipahami: PVC dan Pod memiliki daur hidup yang terpisah secara mutlak.

  • Jika Pod postgres-app di atas dihapus, objek PVC pg-data-pvc dan seluruh data transaksi di dalam disk fisik tetap aman.
  • Ketika kita membuat Pod baru yang menunjuk ke pg-data-pvc yang sama, Pod baru tersebut akan langsung membaca data lama yang diwariskan. Ini adalah fondasi ketahanan data aplikasi stateful.

Pola Otomatisasi PVC pada StatefulSet: volumeClaimTemplates #

Jika kita menggunakan objek Deployment untuk mendeploy aplikasi dengan replika banyak (misalnya 3 replika), dan Deployment tersebut me-mount sebuah PVC biasa, ketiga replika Pod tersebut akan mencoba me-mount PVC yang sama persis. Jika PVC bertipe ReadWriteOnce, Pod kedua dan ketiga akan stuck gagal menyala karena pembatasan akses.

Untuk mengatasi kebutuhan kluster database terdistribusi yang membutuhkan satu disk unik untuk masing-masing replika Pod, Kubernetes menyediakan fitur StatefulSet dengan properti volumeClaimTemplates:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mariadb-cluster
  namespace: database
spec:
  serviceName: "mariadb-headless"
  replicas: 3
  selector:
    matchLabels:
      app: mariadb
  template:
    metadata:
      labels:
        app: mariadb
    spec:
      containers:
      - name: mariadb
        image: mariadb:10.11
        volumeMounts:
        - name: data-disk            # Harus cocok dengan nama di template bawah
          mountPath: /var/lib/mysql
  volumeClaimTemplates:              # Otomatis membuat PVC unik per Pod!
  - metadata:
      name: data-disk
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "gcp-ssd-sc"
      resources:
        requests:
          storage: 20Gi

Mekanisme Penamaan Otomatis StatefulSet PVC: #

Ketika StatefulSet di atas dinyalakan, kontroler Kubernetes secara otomatis akan membuat 3 PVC independen di API Server dengan format nama: $$\text{Nama PVC} = \text{{nama-template}}-\text{{nama-statefulset}}-\text{{indeks-ordinal}}$$

PVC yang terbuat secara otomatis:
  - data-disk-mariadb-cluster-0 (di-mount secara eksklusif oleh Pod mariadb-cluster-0)
  - data-disk-mariadb-cluster-1 (di-mount secara eksklusif oleh Pod mariadb-cluster-1)
  - data-disk-mariadb-cluster-2 (di-mount secara eksklusif oleh Pod mariadb-cluster-2)

Jika Pod mariadb-cluster-1 mengalami crash dan berpindah node, ia dijamin akan selalu dipasangkan kembali dengan PVC data-disk-mariadb-cluster-1. Data tidak akan pernah tertukar antar anggota kluster database.


Panduan Troubleshooting: Mengatasi PVC Stuck di Status Pending #

Masalah operasional yang paling sering dikeluhkan oleh tim pengembang adalah ketika PVC yang mereka buat tertahan lama di status Pending. Kita harus melakukan investigasi sistematis untuk menemukan akar masalahnya.

1. Jalankan Perintah Diagnostik Awal #

Gunakan perintah describe untuk membaca log kejadian (events) yang dicatat oleh API Server pada objek PVC tersebut:

kubectl describe pvc <nama-pvc> -n <namespace>

Arahkan pandangan kita ke bagian paling bawah output, tepatnya pada kolom Events. Di sini kita akan menemukan pesan diagnostik yang menjelaskan penyebab kegagalan binding.

2. Membaca Pesan Error dan Solusinya #

Berikut adalah pola pesan error kejadian PVC yang umum terjadi beserta solusinya:

Kasus A: “no persistent volumes available for this claim and no storage class is set” #

  • Arti: Kita tidak mendefinisikan storageClassName di manifest PVC, sementara di kluster tidak ada satu pun PersistentVolume (PV) berstatus Available yang berukuran cukup, dan kluster tidak memiliki default StorageClass yang aktif.
  • Solusi: Buat PV fisik secara manual, atau hubungi Platform Engineer untuk mendaftarkan salah satu StorageClass sebagai default di kluster menggunakan anotasi: storageclass.kubernetes.io/is-default-class: "true"

Kasus B: “waiting for a volume to be created, either by external provisioner… or manually created by system administrator” #

  • Arti: PVC berhasil menunjuk ke StorageClass yang valid, namun proses provisioning otomatis tertahan. Ini biasanya disebabkan karena pod driver CSI (seperti ebs.csi.aws.com) di namespace kube-system mengalami crash atau tidak memiliki izin akses IAM (kredensial cloud) untuk membuat disk baru.
  • Solusi: Periksa status kesehatan pod CSI Driver: kubectl get pods -n kube-system -l app.kubernetes.io/name=aws-ebs-csi-driver Periksa log pod driver tersebut untuk melihat kegagalan otorisasi cloud API.

Kasus C: “volume node affinity conflict” #

  • Arti: Kasus ini terjadi pada Local PV atau volume storage multi-zone. Kube-scheduler mendeteksi bahwa PV yang tersedia secara fisik berada di Zona A, namun Pod yang mengajukan PVC hanya bisa dijadwalkan di Zona B karena alasan taints atau kelangkaan CPU di Zona A.
  • Solusi: Longgarkan batasan afinitas node pada Pod, atau pastikan kita membuat volume storage baru di zona yang sama dengan lokasi node worker yang sehat.

Volume Expansion: Penskalan Kapasitas Storage Secara Online #

Seiring berjalannya waktu, data database kita pasti akan membesar. Kubernetes menyediakan fitur Volume Expansion yang memungkinkan kita memperbesar ukuran storage secara langsung (online resizing) tanpa harus menghancurkan Pod atau mematikan layanan.

1. Verifikasi Dukungan pada StorageClass #

Sebelum melakukan perubahan, pastikan StorageClass yang mengelola PVC kita telah mengizinkan fitur perluasan dengan memiliki parameter allowVolumeExpansion: true:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: premium-ssd-sc
provisioner: pd.csi.storage.gke.io   # Contoh GCE PD CSI
allowVolumeExpansion: true            # Wajib bernilai true!

2. Mengubah Ukuran PVC Secara Deklaratif #

Kita cukup mengedit manifest PVC atau menjalankan perintah patch untuk menaikkan kapasitas request:

kubectl patch pvc pg-data-pvc -n production \
  -p '{"spec":{"resources":{"requests":{"storage":"100Gi"}}}}'

3. Pantau Proses Resizing Filesystem #

Setelah API cloud provider selesai memperbesar kapasitas disk fisik, Kubelet di node worker akan mendeteksi perubahan tersebut dan menjalankan operasi perluasan filesystem internal kontainer. Kita dapat memantau status ini melalui events:

kubectl describe pvc pg-data-pvc -n production

Cari bagian Conditions pada output. Jika proses resize berhasil, kita akan melihat pesan: VolumeExpansionSuccessful: Mount volume expanded successfully on node.

[!WARNING] Penskalan volume hanya dapat dilakukan satu arah: MEMPERBESAR. Kita tidak akan pernah bisa memperkecil (shrink) ukuran PVC (misalnya menurunkan dari 100Gi ke 50Gi) karena filesystem Linux (seperti ext4/xfs) tidak mendukung penciutan ukuran secara online dan berisiko merusak seluruh integritas data di dalam disk.


Anti-Pattern dalam Manajemen PVC & Solusinya #

Berikut adalah tiga kesalahan konfigurasi PVC yang paling sering menimbulkan kegagalan operasional di produksi:

Anti-Pattern 1: Mengabaikan Pengisian storageClassName (Menggunakan Default Tersembunyi) #

Membiarkan properti storageClassName kosong pada PVC tanpa menyadari konsekuensi binding default.

ANTI-PATTERN: Menulis storageClassName Tanpa Verifikasi Default Class
// KITA MELAKUKAN:
- Kita menulis manifest PVC untuk database PostgreSQL tanpa menyertakan field `storageClassName`.

// KONSEKUENSI DI PRODUKSI:
- Binding Tidak Terduga: Kubernetes akan otomatis mengikat PVC kita ke default StorageClass kluster. 
  Di cloud managed (seperti GKE/EKS), default StorageClass biasanya menggunakan tipe disk standar lambat (HDD).
- I/O Degradation: Database kita berjalan lambat sejak hari pertama dideploy karena menggunakan storage HDD, 
  mengakibatkan waktu respon API melambat saat diakses banyak user.
✓ SOLUSI YANG BENAR:
- Selalu deklarasikan properti `storageClassName` secara eksplisit pada setiap manifest PVC:
  storageClassName: "premium-ssd-sc"
- Jika kita sengaja ingin menggunakan PV statis yang dibuat manual, setel properti tersebut ke string kosong secara eksplisit:
  storageClassName: ""

Anti-Pattern 2: Berbagi PVC ReadWriteOnce (RWO) Lintas Node pada Deployment Stateless #

Mendeploy Deployment web API dengan replica 3 menggunakan PVC bertipe RWO untuk direktori cache log bersama.

ANTI-PATTERN: Replicas 3 Web API Menggunakan PVC RWO Bersama
// KITA MELAKUKAN:
- Kita mendeploy web API dengan 3 replica Pod.
- Ketiga Pod dikonfigurasi untuk me-mount satu PVC RWO bernama `web-log-pvc`.

// KONSEKUENSI DI PRODUKSI:
- Kegagalan Startup: Scheduler menjadwalkan `pod-0` di `node-1`, `pod-1` di `node-2`, dan `pod-2` di `node-3`.
- `pod-0` di `node-1` akan berjalan normal karena ia berhasil mengunci volume cloud disk.
- `pod-1` dan `pod-2` di node worker lain akan stuck di status `ContainerCreating` selamanya dengan pesan error:
  `Multi-Attach error for volume: volume-xyz can only be attached to single node`.
✓ SOLUSI YANG BENAR:
- Jika log web API ingin dikumpulkan, buang log ke stdout/stderr, jangan simpan di disk bersama.
- Jika terpaksa butuh folder bersama lintas node worker, gunakan PVC dengan access mode `ReadWriteMany` (RWX) 
  dan pastikan menggunakan backend storage jaringan seperti AWS EFS atau NFS.

Anti-Pattern 3: Mencoba Menurunkan Ukuran Kapasitas PVC via GitOps/YAML Patch #

Mencoba memperkecil ukuran storage PVC (misalnya dari 100Gi menjadi 50Gi) melalui pembaruan repositori GitOps.

ANTI-PATTERN: Mengubah storage: "100Gi" menjadi storage: "50Gi" pada PVC YAML
// KITA MELAKUKAN:
- Kita menyadari alokasi 100Gi terlalu besar dan ingin menghemat biaya cloud dengan mengubah manifes ke 50Gi.

// KONSEKUENSI DI PRODUKSI:
- Penolakan API Server: API Server Kubernetes akan menolak pengiriman konfigurasi tersebut secara instan.
- Pipeline GitOps Macet: Sistem CI/CD (seperti ArgoCD) akan stuck di status `OutOfSync` atau `Error` 
  karena Kubernetes menolak menerapkan perubahan properti storage yang lebih kecil, menghentikan seluruh alur deployment otomatis.
✓ SOLUSI YANG BENAR:
- Terima kenyataan bahwa volume fisik tidak bisa diperkecil.
- Jika ingin mengurangi kapasitas, kita harus membuat PVC baru berukuran 50Gi, mendeploy Pod migrasi data 
  sementara untuk memindahkan data dari PVC 100Gi ke PVC 50Gi (misalnya menggunakan rsync), 
  lalu menghapus PVC 100Gi yang lama setelah aman.

Ringkasan #

  • Tiket Klaim Namespace — PersistentVolumeClaim (PVC) adalah representasi abstrak dari permintaan storage dari developer yang dikunci di dalam namespace tertentu.
  • First-Fit Matching — Kubernetes menggunakan algoritma first-fit untuk mencocokkan PVC dengan PV Available; gunakan volumeName jika ingin mengunci binding ke PV tertentu.
  • Daur Hidup Terpisah — Pod yang menggunakan PVC dapat dihapus dan dibuat ulang secara bebas tanpa memengaruhi integritas objek PVC maupun data fisik di disk.
  • Otomatisasi StatefulSet — Gunakan blok volumeClaimTemplates pada StatefulSet untuk menghasilkan PVC unik secara otomatis bagi masing-masing replika Pod.
  • describe PVC untuk Diagnosis — Selalu jalankan kubectl describe pvc di bagian Events untuk mengidentifikasi penyebab PVC tertahan di status Pending.
  • allowVolumeExpansion — Aktifkan parameter allowVolumeExpansion: true pada StorageClass agar PVC dapat diperbesar secara online tanpa downtime.
  • Volume Hanya Bisa Membesar — Jangan pernah mencoba menurunkan kapasitas storage PVC pada manifes YAML guna menghindari error penolakan dari API Server.
  • Default Class Alert — Selalu tentukan storageClassName secara eksplisit pada PVC untuk menghindari salah alokasi ke default StorageClass berkinerja rendah.

← Sebelumnya: PersistentVolume   Berikutnya: StorageClass →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact