Tiga Lapis Abstraksi Penyimpanan
StorageClass → cara menyediakan penyimpanan (SSD? HDD? terenkripsi?)
│
PersistentVolume (PV) → potongan penyimpanan yang sebenarnya
│
PersistentVolumeClaim (PVC) → permintaan dari aplikasi
│
Pod → me-mount PVC sebagai direktori
Di kluster modern, Anda hampir tidak pernah membuat PV secara manual. Anda membuat PVC, dan StorageClass menyediakan PV secara dinamis.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-basisdata
spec:
accessModes: [ReadWriteOnce]
storageClassName: ssd-cepat
resources:
requests:
storage: 50Gi
Mode Akses yang Sering Disalahpahami
| Mode | Arti sebenarnya |
|---|---|
| ReadWriteOnce (RWO) | Dapat di-mount baca-tulis oleh satu node — bukan satu pod. Beberapa pod di node yang sama bisa memakainya bersamaan. |
| ReadOnlyMany (ROX) | Dapat di-mount baca-saja oleh banyak node. |
| ReadWriteMany (RWX) | Dapat di-mount baca-tulis oleh banyak node. Butuh penyimpanan berbasis jaringan seperti NFS atau CephFS. |
| ReadWriteOncePod | Benar-benar hanya satu pod. Ini yang sering sebenarnya Anda inginkan untuk database. |
Disk blok dari penyedia cloud (EBS, Persistent Disk) hampir selalu RWO — hanya bisa menempel pada satu node dalam satu waktu. Bila Deployment Anda punya 3 replika yang semuanya me-mount PVC RWO yang sama, hanya pod di node pertama yang berjalan; sisanya menggantung di Pending selamanya. Untuk beban kerja yang butuh penyimpanan bersama antar node, Anda perlu penyimpanan RWX seperti NFS — atau, yang biasanya lebih tepat, rancang ulang agar aplikasi tidak butuh berkas bersama sama sekali.
StatefulSet
Deployment memberi pod nama acak (pesanan-7d4f-x9k2) dan memperlakukan semuanya setara. Beberapa sistem tidak bisa hidup seperti itu — database perlu tahu siapa yang primer, dan tiap replika perlu penyimpanannya sendiri yang tetap.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres-headless # WAJIB
replicas: 3
selector:
matchLabels: { app: postgres }
template:
metadata:
labels: { app: postgres }
spec:
terminationGracePeriodSeconds: 60
containers:
- name: postgres
image: postgres:17-alpine
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates: # PVC terpisah untuk TIAP replika
- metadata:
name: data
spec:
accessModes: [ReadWriteOnce]
storageClassName: ssd-cepat
resources:
requests: { storage: 100Gi }
Yang Diberikan StatefulSet
| Jaminan | Penjelasan |
|---|---|
| Nama yang stabil | postgres-0, postgres-1, postgres-2 — tetap sama setelah restart |
| DNS per pod | postgres-0.postgres-headless.ns.svc.cluster.local |
| Penyimpanan yang melekat | PVC data-postgres-0 selalu kembali ke pod postgres-0 |
| Urutan yang terjamin | Dibuat 0→1→2, dihapus 2→1→0 |
Menghapus StatefulSet tidak menghapus PVC-nya — ini disengaja, agar data tidak hilang karena kesalahan. Konsekuensinya, PVC bisa menumpuk tanpa disadari dan terus menagih biaya penyimpanan. Bersihkan secara sadar:
kubectl delete pvc -l app=postgres
Haruskah Database Dijalankan di Kubernetes?
Menjalankan database stateful di Kubernetes menuntut Anda menangani sendiri: failover dan pemilihan primer, backup beserta uji pemulihan, upgrade versi mayor, penyetelan parameter, dan pemantauan replikasi. Kubernetes tidak menyelesaikan satu pun dari itu — StatefulSet hanya menjamin identitas dan penyimpanan yang stabil.
Untuk sebagian besar tim, basis data terkelola dari penyedia cloud adalah pilihan yang jauh lebih bijak. Bila memang harus di Kubernetes, gunakan operator yang matang (CloudNativePG untuk PostgreSQL, Strimzi untuk Kafka) — jangan menulis StatefulSet mentah untuk database produksi.
Jenis Volume Lain
# emptyDir — hidup selama pod hidup, hilang saat pod mati
volumes:
- name: sementara
emptyDir:
sizeLimit: 1Gi
# emptyDir di memori — sangat cepat, memakai RAM
- name: cache-cepat
emptyDir:
medium: Memory
sizeLimit: 256Mi
# Menyuntikkan metadata pod sebagai berkas
- name: info-pod
downwardAPI:
items:
- path: "nama-pod"
fieldRef: { fieldPath: metadata.name }
# Menggabungkan beberapa sumber dalam satu mount
- name: gabungan
projected:
sources:
- configMap: { name: konfigurasi }
- secret: { name: kredensial }
kubectl get pvc,pv kubectl get storageclass kubectl describe pvc data-basisdata kubectl get statefulset postgres -o wide