PV dan PVC

PersistentVolume dan PersistentVolumeClaim beserta alur binding dari claim ke penyimpanan nyata.

Daftar isi

PersistentVolume dan PersistentVolumeClaim adalah pasangan objek yang memisahkan penyediaan penyimpanan dari kebutuhan aplikasi, sehingga Pod tidak perlu mengetahui detail teknis di belakang disknya. Topik ini membahas peran masing-masing objek, alur binding dari claim ke volume nyata, arti setiap accessModes, serta siklus hidup lengkap dari provisioning sampai reclaim. Alur bindingnya juga disimulasikan secara terprogram agar aturan pencocokannya terlihat jelas.

PersistentVolume sebagai sumber daya kluster#

PersistentVolume, yang selanjutnya disebut PV, adalah sumber daya tingkat kluster berupa kapasitas penyimpanan nyata yang sudah tersedia. Objek ini dibuat oleh pengelola kluster atau oleh sistem provisioning otomatis, dan hidup terlepas dari Pod maupun namespace yang menggunakannya. Manifest PV memuat tiga kelompok informasi yang menentukan kecocokannya, yaitu kapasitas, accessModes, dan penyimpanan di belakangnya:

yaml pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-arsip-1
  labels:
    jenis: arsip
spec:
  capacity:
    storage: 2Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  hostPath:
    path: /data/pv/arsip-1
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-arsip-2
  labels:
    jenis: arsip
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  hostPath:
    path: /data/pv/arsip-2
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-bersama
  labels:
    jenis: bersama
spec:
  capacity:
    storage: 3Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  hostPath:
    path: /data/pv/bersama

Ketiga PV di atas menyusun kapasitas kluster sebesar 10 Gi dalam tiga bagian. Penyimpanan di belakangnya berupa hostPath yang cocok untuk lab lokal, sedangkan kluster produksi menggantinya dengan backend jaringan seperti NFS atau driver CSI. Label jenis tidak menuntut aturan apa pun dari Kubernetes, tetapi berguna sebagai penanda yang dirujuk selector pada claim.

Status PV melewati Available ketika siap diikat, Bound ketika terikat pada claim, dan Released setelah claimnya dihapus. Nilai-nilai tersebut terbaca langsung pada kolom STATUS keluaran kubectl, sehingga keadaan kapasitas kluster dapat ditinjau tanpa masuk ke backend penyimpanannya.

PersistentVolumeClaim sebagai permintaan#

PersistentVolumeClaim, yang selanjutnya disebut PVC, adalah permintaan kapasitas dari sisi pengguna, dan pembuatannya berada di dalam namespace. Permintaan menyatakan ukuran minimum, accessModes yang diinginkan, dan selektor label untuk mempersempit kandidat PV:

yaml pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: arsip-catatan
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  selector:
    matchLabels:
      jenis: arsip
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: arsip-besar
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 4Gi
  selector:
    matchLabels:
      jenis: arsip
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: laporan-bersama
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 2Gi
  selector:
    matchLabels:
      jenis: laporan

Pod tidak menyentuh PV secara langsung. Bidang volumes pada Pod menunjuk PVC melalui persistentVolumeClaim.claimName, dan sistem menerjemahkan permintaan itu menjadi mount pada container. Lapisan tidak langsung ini membuat aplikasi tetap berjalan meskipun penyimpanan di belakangnya diganti, karena yang berubah hanya PVC tanpa menyentuh manifest Pod.

Bidang storageClassName pada claim juga menentukan jalannya pencocokan. Claim tanpa bidang tersebut dicocokkan dengan PV yang tidak membawa class, sedangkan claim dengan class hanya menunggu PV dari class yang sama, dan jalur kedua inilah yang dibahas pada topik StorageClass. Selain itu, resources.requests.storage menyatakan kapasitas minimum, sehingga PV dengan kapasitas lebih besar tetap sah sebagai pasangannya.

Alur binding dari claim ke volume#

Pencocokan dilakukan oleh controller PVC pada control plane. Setiap claim yang menunggu diperiksa terhadap PV yang berstatus Available, dan aturan pencocokannya menuntut kapasitas PV yang memenuhi permintaan, accessModes yang beririsan, serta label yang memenuhi selector. Satu PV hanya dapat terikat pada satu PVC pada satu waktu, sehingga claim yang lebih dulu cocok memenangkan volume tersebut. Diagram berikut merangkum alurnya:

Alur binding PersistentVolumeClaim ke PersistentVolumepencocokan kapasitas, accessModes, dan labelPersistentVolumeClaimarsip-catatan1Gi, ReadWriteOncePencocokankapasitasaccessModeslabelbindingPersistentVolumepv-arsip-12Gi, ReadWriteOncePod catatanpersistentVolumeClaim: arsip-catatanclaimNamesetelah terikat, pod yangmenggunakan claim berbagi satu PV
Alur binding dari PersistentVolumeClaim ke PersistentVolume melalui pencocokan kapasitas, accessModes, dan label

Simulasi berikut menerapkan aturan pencocokan tersebut pada ketiga claim dan ketiga PV di atas. Kapasitas dibandingkan dengan mengonversi satuan Gi dan Mi, accessModes dibandingkan sebagai himpunan, dan selector dibandingkan terhadap label PV:

python
import yaml

def ukuran_gi(nilai):
    # ubah "2Gi" atau "512Mi" menjadi angka GiB
    angka = float(nilai[:-2])
    return angka / 1024 if nilai.endswith("Mi") else angka

def cocok(pvc, pv):
    permintaan = ukuran_gi(pvc["spec"]["resources"]["requests"]["storage"])
    kapasitas = ukuran_gi(pv["spec"]["capacity"]["storage"])
    if permintaan > kapasitas:
        return f"kapasitas {pv['spec']['capacity']['storage']} kurang dari permintaan"
    if not set(pvc["spec"]["accessModes"]) & set(pv["spec"]["accessModes"]):
        return "accessModes tidak bertemu"
    selektor = pvc["spec"].get("selector", {}).get("matchLabels", {})
    label = pv["metadata"].get("labels", {})
    for kunci, nilai in selektor.items():
        if label.get(kunci) != nilai:
            return f"label {kunci}={nilai} tidak ada pada PV"
    return "cocok"

with open("pv.yaml") as f:
    pv_semua = list(yaml.safe_load_all(f))
with open("pvc.yaml") as f:
    pvc_semua = list(yaml.safe_load_all(f))

terikat = set()
for pvc in pvc_semua:
    nama = pvc["metadata"]["name"]
    dipilih = None
    alasan = "tidak ada PV yang cocok"
    for pv in pv_semua:
        if pv["metadata"]["name"] in terikat:
            continue
        alasan = cocok(pvc, pv)
        if alasan == "cocok":
            dipilih = pv["metadata"]["name"]
            break
    if dipilih:
        terikat.add(dipilih)
        print(f"{nama} -> BOUND ke {dipilih}")
    else:
        print(f"{nama} -> PENDING ({alasan})")
text Keluaran
arsip-catatan -> BOUND ke pv-arsip-1
arsip-besar -> BOUND ke pv-arsip-2
laporan-bersama -> PENDING (label jenis=laporan tidak ada pada PV)

Dua claim pertama berhasil terikat karena semua syaratnya terpenuhi, sedangkan laporan-bersama tetap Pending karena tidak ada PV berlabel jenis=laporan. Kluster yang sesungguhnya menampilkan keadaan yang sama pada kolom STATUS ketika kubectl get pvc dijalankan, dan claim yang Pending terus-menerus hampir selalu berarti pencocokan yang tidak menemukan pasangannya.

AccessModes dan artinya#

AccessModes menjelaskan cara penyimpanan dapat diakses dari sisi Node, bukan sisi Pod:

ModeSingkatanMakna
ReadWriteOnceRWOvolume dapat di-mount untuk baca dan tulis oleh satu Node
ReadOnlyManyROXvolume dapat di-mount hanya baca oleh banyak Node
ReadWriteManyRWXvolume dapat di-mount untuk baca dan tulis oleh banyak Node

Singkatan RWO, ROX, dan RWX muncul pada kolom ACCESS MODES keluaran kubectl, sehingga ketiganya perlu dikenali. Satu nuansa yang sering terlewat adalah satuan ReadWriteOnce berupa Node dan bukan Pod, sehingga beberapa Pod yang dijadwalkan pada Node yang sama tetap dapat berbagi volume RWO. Mode tambahan ReadWriteOncePod mempersempit batasnya hingga satu Pod, dan ketersediaannya bergantung pada versi kluster.

Mode yang tertulis pada PV sifatnya deklaratif, yaitu penyedia penyimpanan yang menjamin kebenarannya, dan Kubernetes tidak menguji bahwa backend sungguh mendukung mode yang diiklankan. Manifest hostPath pada lab, misalnya, dapat memuat mode apa pun tanpa penolakan, sehingga kepercayaan pada mode menuntut kepercayaan pada penyedianya.

Siklus hidup dan kebijakan reclaim#

Umur hidup sebuah PV melewati empat fase, yaitu provisioning saat kapasitas dibuat, binding saat claim terikat, using selama Pod membaca dan menulisnya, serta reclaim setelah claim dilepas. Setelah pengguna menghapus PVC, volume tidak langsung hilang, dan nasibnya ditentukan persistentVolumeReclaimPolicy pada PV:

KebijakanPerlakuan setelah claim dihapus
RetainPV tetap ada dengan status Released, datanya dipertahankan, dan pembersihannya dilakukan manual
DeletePV beserta penyimpanan di belakangnya dihapus

Kebijakan Retain menjadi default pada PV statis, sehingga kehilangan data akibat salah hapus claim dapat dihindari dengan biaya pembersihan manual. Kebijakan Delete lebih praktis untuk penyimpanan sementara, tetapi perlu dipertimbangkan dengan sengaja karena penghapusan berjalan tanpa konfirmasi.

Ringkasan#

  • PersistentVolume adalah kapasitas penyimpanan tingkat kluster, sedangkan PersistentVolumeClaim adalah permintaan kapasitas dari sisi namespace yang dirujuk Pod melalui persistentVolumeClaim.claimName.
  • Binding terjadi ketika kapasitas, accessModes, dan label PV memenuhi permintaan claim, dan satu PV hanya terikat pada satu claim.
  • AccessModes berupa RWO, ROX, dan RWX yang menyatakan akses dari sisi Node, sehingga satu volume RWO tetap dapat dibagikan antara beberapa Pod pada Node yang sama.
  • Kebijakan reclaim menentukan nasib volume setelah claim dihapus, dengan Retain mempertahankan data untuk pembersihan manual dan Delete menghapusnya.

Referensi#