Ephemeral vs Persistent Storage #
Di dalam ekosistem Kubernetes, memisahkan logika aplikasi (komputasi) dengan penyimpanan data (status) merupakan prinsip desain fundamental yang membedakan arsitektur modern dari model server tradisional. Saat kita mendeploy aplikasi di Kubernetes, kita tidak lagi menganggap server (node worker) sebagai entitas statis yang menyimpan file kita selamanya. Sebaliknya, node worker dan Pod di dalamnya dapat dimatikan, dipindahkan, atau dihancurkan kapan saja oleh scheduler.
Oleh karena itu, memahami bagaimana data disimpan di dalam kluster menjadi hal yang sangat krusial. Kubernetes membagi dunia penyimpanan menjadi dua kategori utama dengan filosofi operasional yang bertolak belakang: Ephemeral Storage (Penyimpanan Sementara) dan Persistent Storage (Penyimpanan Persisten). Salah memilih pendekatan untuk jenis workload kita dapat mengakibatkan bencana hilangnya data (data loss) di satu sisi, atau pemborosan biaya dan overhead performa yang tidak perlu di sisi lain. Artikel ini akan membangun mental model arsitektural yang matang mengenai kedua kategori penyimpanan ini sebelum kita masuk ke detail teknis PV, PVC, dan StorageClass.
Sifat Dasar Container Filesystem: Immutable Writable Layer #
Sebelum kita membahas perbedaan antara volume ephemeral dan persisten, kita harus memahami bagaimana kontainer Linux mengelola sistem berkas (filesystem) mereka di tingkat dasar.
Ketika container runtime (seperti containerd) menjalankan sebuah kontainer dari image aplikasi (misalnya nginx:latest), kontainer tersebut tidak berjalan pada sistem berkas yang dapat dimodifikasi secara langsung. Kontainer menggunakan teknologi sistem berkas berlapis seperti OverlayFS atau UnionFS:
- Read-Only Image Layers: Lapisan-lapisan penyusun image kontainer bersifat statis dan tidak dapat diubah (read-only).
- Thin Writable Layer: Kontainer menyisipkan lapisan tipis di atas lapisan image yang dapat ditulis (writable layer). Semua operasi pembuatan berkas baru, modifikasi berkas log, atau penyimpanan file unggahan sementara akan ditulis pada lapisan writable ini.
[Struktur Filesystem Kontainer (OverlayFS)]
│
├──► Writable Layer (Tipis & Sementara) ➔ Ditulis saat aplikasi berjalan
│ (Dihapus jika kontainer crash/restart!)
│
└──► Read-Only Image Layers (Statis) ➔ Berkas biner aplikasi, OS base, library
[!WARNING] Lapisan writable ini memiliki daur hidup yang terikat secara absolut pada daur hidup fisik kontainer. Jika kontainer utama kita mengalami crash karena kehabisan memori (OOMKilled) dan Kubelet memicu proses restart kontainer baru, lapisan writable lama akan dihancurkan sepenuhnya. Kontainer baru akan menyala dengan lapisan writable baru yang bersih, dan semua data yang ditulis kontainer lama ke filesystem lokalnya akan hilang tanpa bisa dikembalikan.
Ini adalah keputusan desain yang disengaja (immutability) untuk memastikan aplikasi stateless dapat direplikasi secara paralel tanpa membawa status lokal yang kotor. Namun, bagaimana jika aplikasi kita butuh menyimpan data yang harus bertahan lintas restart, atau berbagi data antar kontainer? Di sinilah peran abstraksi Volume Kubernetes masuk.
Alur Lifecycle Storage: Ephemeral vs Persistent #
Untuk memperjelas perbedaan daur hidup antara volume ephemeral dan persistent, mari kita perhatikan visualisasi alur eksekusi saat Pod mengalami crash atau pemindahan node berikut:
flowchart TD
subgraph Ephemeral ["Volume Ephemeral (emptyDir)"]
E_PodStart["Pod Dijalankan di Node-1"] --> E_DirCreate["Kubelet Membuat Direktori Kosong di Node-1"]
E_DirCreate --> E_Write["Kontainer Menulis Data ke /data"]
E_Write --> E_PodCrash["Pod Crash / Dimatikan"]
E_PodCrash --> E_DirDelete["Kubelet Menghapus Direktori Kosong & Isinya"]
E_DirDelete --> E_Lost["DATA HILANG"]
end
subgraph Persistent ["Volume Persistent (PV/PVC)"]
P_PodStart["Pod Dijalankan di Node-1"] --> P_Claim["Pod Meminta PVC"]
P_Claim --> P_Bind["PVC Di-bind ke PV (Cloud/NFS Disk)"]
P_Bind --> P_Write["Kontainer Menulis Data ke /data (Disk Eksternal)"]
P_Write --> P_PodCrash["Pod Crash / Dipindahkan ke Node-2"]
P_PodCrash --> P_Unmount["Volume Di-unmount dari Node-1"]
P_Unmount --> P_Mount["Volume Di-mount ke Node-2"]
P_Mount --> P_Recover["Pod Baru di Node-2 Membaca Data Lama"]
P_Recover --> P_Safe["DATA TETAP AMAN"]
end
Ephemeral Storage: Penyimpanan Sementara yang Terikat pada Pod #
Volume ephemeral adalah penyimpanan yang siklus hidupnya terikat secara langsung pada siklus hidup Pod. Selama Pod tersebut berjalan di atas node worker, data di dalam volume ini akan tetap aman. Namun, saat objek Pod tersebut dihapus secara deklaratif (misalnya akibat scale-down Deployment atau evakuasi node), seluruh data di dalam volume ephemeral akan dihapus secara permanen.
Kubernetes menyediakan beberapa tipe volume ephemeral bawaan yang umum digunakan:
1. emptyDir: Direktori Kosong Berbasis Disk atau RAM
#
emptyDir adalah tipe volume ephemeral yang paling sederhana dan paling sering digunakan. Saat Pod dijadwalkan pada sebuah node worker, Kubelet akan membuat direktori kosong di dalam sistem berkas lokal node worker tersebut. Semua kontainer di dalam Pod yang sama dapat membaca dan menulis file pada direktori ini secara bersamaan.
Berikut adalah manifest manifes penggunaan emptyDir tingkat produksi:
apiVersion: v1
kind: Pod
metadata:
name: cache-worker-pod
spec:
volumes:
- name: disk-cache
emptyDir: {} # Menyimpan data di HDD/SSD node worker (lokal)
- name: memory-cache
emptyDir:
medium: Memory # Menyimpan data di RAM (tmpfs) — Sangat Cepat!
sizeLimit: 512Mi # Wajib membatasi ukuran memori
containers:
- name: app-runner
image: company/app-image:v1
volumeMounts:
- name: disk-cache
mountPath: /app/tmp-disk
- name: memory-cache
mountPath: /app/tmp-ram
Penggunaan emptyDir Berbasis Memory (RAM) #
Dengan menyetel medium: Memory, Kubernetes akan membuat sistem berkas tmpfs (RAM virtual) di node worker.
- Keuntungan: Kecepatan baca/tulis yang sangat tinggi karena operasi I/O dilakukan langsung di memori fisik tanpa menyentuh disk mekanis/SSD.
- Risiko: Data di dalam tmpfs memotong jatah memori cgroups kontainer. Jika kita tidak menyertakan
sizeLimitdan kontainer menulis file hingga melebihi memori fisik node, node worker akan mengalami kegagalan OOM (Out of Memory).
Use Case emptyDir yang Valid: #
- Penyimpanan cache sementara (caching layer) yang jika hilang tidak merusak fungsi aplikasi utama.
- Workspace bersama antar kontainer di dalam satu Pod (misalnya kontainer init mengunduh file aset, dan kontainer utama menyajikannya).
- Proses rendering video atau kompresi file sementara yang hasilnya akan langsung diunggah ke Object Storage (seperti S3 atau GCS).
2. ConfigMap dan Secret sebagai Volume #
ConfigMap dan Secret juga dikategorikan sebagai ephemeral volume. Kubernetes menyajikan data konfigurasi atau kredensial sensitif sebagai file teks statis yang di-mount ke dalam direktori kontainer. Jika Pod dihancurkan, mount point tersebut hilang, namun data asli ConfigMap dan Secret tetap tersimpan dengan aman di database etcd control plane.
Persistent Storage: Memutus Hubungan Siklus Hidup Pod dan Data #
Untuk beban kerja yang memiliki status (stateful workloads)—seperti database, cluster queue, atau sistem file bersama—kita membutuhkan penyimpanan yang tidak peduli apakah Pod kita mati, di-restart, atau dipindahkan ke node fisik lain di ujung pusat data. Data kita harus tetap bertahan dan dapat diakses kembali oleh Pod pengganti secara transparan.
Di Kubernetes, kita memotong hubungan erat antara daur hidup komputasi (Pod) dengan penyimpanan menggunakan trinitas komponen storage: StorageClass, PersistentVolume (PV), dan PersistentVolumeClaim (PVC).
[Trinitas Storage Kubernetes]
StorageClass ➔ Template "resep" cara meminta storage dari cloud provider.
(Contoh: tipe SSD, enkripsi aktif, multi-zone).
PersistentVolume ➔ Representasi fisik dari storage yang sudah disiapkan di backend.
(Contoh: AWS EBS volume 100GB, GCP PD 50GB, NFS share).
PersistentVolumeClaim ➔ Tiket permintaan storage yang diajukan oleh developer aplikasi.
(Contoh: "Saya minta disk 10GB dengan akses ReadWriteOnce").
Mekanisme ini menerapkan prinsip pemisahan tanggung jawab (separation of concerns):
- Platform Engineer/System Administrator: Mengonfigurasi
StorageClassdan menyiapkan driver integrasi (CSI - Container Storage Interface) ke penyedia infrastruktur (AWS, GCP, VMware, atau Ceph). - Application Developer: Cukup menulis manifest
PersistentVolumeClaimyang meminta kapasitas penyimpanan tertentu. Developer tidak perlu tahu merk hardware storage yang digunakan di belakang layar atau API call cloud provider untuk membuat disk baru.
Memahami Pola Akses: Access Modes #
Ketika kita mengajukan permintaan penyimpanan menggunakan PVC, kita harus mendeklarasikan Access Mode yang menentukan bagaimana volume tersebut akan dipasang (mount) di seluruh node worker di kluster. Kesalahan memilih access mode dapat membuat Pod tertahan di status ContainerCreating selamanya akibat pembatasan fisik hardware.
Berikut adalah 4 tipe Access Mode yang tersedia di Kubernetes:
1. ReadWriteOnce (RWO)
#
Volume hanya dapat dipasang sebagai read-write oleh satu node worker tunggal pada satu waktu.
- Perilaku: Jika Pod A yang terikat ke volume RWO berjalan di
node-1, maka Pod B yang berjalan dinode-2tidak akan bisa membaca atau menulis ke volume tersebut. Namun, jika ada dua Pod berjalan di node worker yang sama (node-1), beberapa cloud provider mengizinkan keduanya menempel pada volume RWO yang sama (meskipun ini tidak disarankan). - Use Case: Database transaksional (PostgreSQL, MySQL, Redis) yang membutuhkan I/O eksklusif untuk mencegah kerusakan data.
2. ReadOnlyMany (ROX)
#
Volume dapat dipasang sebagai read-only oleh banyak node worker secara bersamaan.
- Use Case: Distribusi file statis, repositori kode sumber bersama, atau direktori aset gambar statis yang dibaca oleh banyak Pod web server paralel namun tidak memerlukan penulisan data dari aplikasi.
3. ReadWriteMany (RWX)
#
Volume dapat dipasang sebagai read-write oleh banyak node worker secara bersamaan.
- Perilaku: Seluruh Pod yang tersebar di node worker yang berbeda dapat membaca dan menulis ke folder yang sama secara konkuren.
- Teknologi Backend: Mode ini tidak didukung oleh tipe block storage cloud tradisional seperti AWS EBS atau GCP Persistent Disk. RWX membutuhkan penyimpanan berbasis file terdistribusi seperti NFS, AWS EFS, GCP Filestore, CephFS, atau GlusterFS.
- Use Case: Sistem CMS (seperti WordPress) yang membutuhkan direktori unggahan gambar bersama lintas Pod replica, atau pemrosesan batch log terpusat.
4. ReadWriteOncePod (RWOP - Kubernetes v1.22+)
#
Volume hanya dapat dipasang sebagai read-write oleh satu Pod tunggal di seluruh kluster.
- Perilaku: Ini adalah proteksi yang lebih ketat dari RWO. Jika Pod A telah me-mount volume RWOP, tidak boleh ada Pod lain (bahkan jika berjalan di node worker yang sama) yang boleh menyentuh volume tersebut.
- Use Case: Menjamin keamanan data mutlak untuk aplikasi stateful kritis agar terhindar dari akses konkuren tidak disengaja.
Generic Ephemeral Volumes: Pendekatan Hibrida (Semi-Persistent) #
Kubernetes modern memperkenalkan fitur canggih bernama Generic Ephemeral Volumes. Fitur ini menggabungkan kemudahan daur hidup volume ephemeral dengan kekuatan fitur volume persistent.
Dengan Generic Ephemeral Volume, kita dapat meminta Kubernetes untuk membuatkan volume penyimpanan persisten (misalnya SSD Cloud performa tinggi) secara dinamis saat Pod dibuat, namun Kubernetes akan menghapus volume persistent tersebut secara otomatis ketika Pod dihancurkan.
Berikut adalah manifes penggunaan Generic Ephemeral Volume:
apiVersion: v1
kind: Pod
metadata:
name: batch-analyzer-pod
spec:
containers:
- name: analyzer
image: company/analyzer:v1.0
volumeMounts:
- name: scratch-storage
mountPath: /data/scratch
volumes:
- name: scratch-storage
ephemeral: # Menandakan volume ephemeral hibrida
volumeClaimTemplate:
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "premium-ssd-sc" # Membuat SSD fisik secara dinamis
resources:
requests:
storage: 50Gi
Mengapa Kita Membutuhkan Generic Ephemeral Volumes? #
- Kapasitas Besar: Volume
emptyDirbiasa memakan ruang disk root node worker. Jika kita butuh disk 50GB hanya untuk temporary workspace batch job, menggunakanemptyDirberisiko membuat disk node penuh (disk pressure). - Fitur Storage Kaya: Kita dapat menggunakan fitur StorageClass khusus, seperti enkripsi disk cloud, IOPS tinggi, atau tipe SSD, yang tidak bisa disediakan oleh direktori kosong lokal node biasa.
- Daur Hidup Bersih: Kita tidak perlu repot melakukan bersih-bersih PVC manual setelah batch job kita selesai berjalan; Kubernetes yang akan menghapusnya secara otomatis.
Pertimbangan Produksi: Managed Service vs In-Cluster Storage #
Sebelum kita memutuskan untuk mendeploy seluruh aplikasi stateful kita di dalam Kubernetes menggunakan Persistent Volume, kita harus bersikap realistis secara operasional. Menjalankan status di dalam Kubernetes membutuhkan keahlian tinggi (day-2 operations overhead).
Berikut adalah panduan pengambilan keputusan arsitektural yang dapat kita gunakan:
1. Kapan Menggunakan Managed Database Service (Bypass Kubernetes)? #
Untuk database relasional transaksional utama yang memiliki SLA bisnis tinggi (seperti database keuangan atau data pengguna utama), pertimbangkan untuk tidak mendeploy-nya di dalam Kubernetes.
- Pilihan: Gunakan layanan terkelola cloud provider seperti AWS RDS, GCP Cloud SQL, atau Azure Database.
- Alasan: Layanan managed menyediakan fitur backup otomatis, replikasi multi-zone failover otomatis, dan patching sistem operasi yang dikelola penuh oleh provider. Kita tidak perlu pusing memikirkan kerusakan filesystem disk PV atau koordinasi quorum StatefulSet saat node worker mati.
2. Kapan Menjalankan Database di Dalam Kubernetes (PV/PVC)? #
- Database non-relasional yang memiliki arsitektur terdistribusi bawaan (shared-nothing architecture), seperti kluster Elasticsearch, Cassandra, atau Apache Kafka yang memang didesain tangguh menghadapi matinya node secara acak.
- Lingkungan pengujian (staging / development) di mana kita ingin menekan biaya dengan tidak menyewa database managed cloud yang mahal.
- Aplikasi stateful yang dikemas menggunakan Operator Pattern resmi (misalnya Zalando Postgres Operator atau Confluent Kafka Operator) yang mengotomatisasi proses failover di dalam kluster Kubernetes.
3. Gunakan Object Storage (S3/GCS) untuk Berkas Statis #
Untuk penyimpanan file statis seperti gambar profil pengguna, dokumen PDF, atau aset web, hindari penggunaan volume persistent ReadWriteMany (RWX) karena performa NFS/EFS cloud biasanya lambat untuk operasi file kecil.
- Solusi: Gunakan SDK Object Storage (AWS S3, Google Cloud Storage) langsung di dalam kode aplikasi kita. Ini jauh lebih murah, memiliki skalabilitas tanpa batas, dan performa yang lebih stabil.
Anti-Pattern dalam Manajemen Storage & Solusinya #
Berikut adalah tiga kesalahan fatal yang paling sering menghancurkan data atau mengacaukan performa kluster produksi:
Anti-Pattern 1: Menyimpan Berkas Log Aplikasi Langsung pada Writable Layer Kontainer #
Membiarkan aplikasi menulis file log (seperti log akses Nginx atau log debug Java) langsung ke dalam filesystem internal kontainer tanpa di-mount to volume.
ANTI-PATTERN: Menulis Log ke /var/log/app.log di dalam writable layer kontainer
// KITA MELAKUKAN:
- Menjalankan kontainer aplikasi yang secara konstan membuang baris log ke file lokal kontainer.
// KONSEKUENSI DI PRODUKSI:
- Kebocoran Disk Node: File log di writable layer disimpan di direktori Docker/Kubelet node worker.
Jika file membesar hingga 20GB, disk root node worker akan penuh.
- Eviction Loop: Kubelet mendeteksi disk pressure (DiskPressure) pada node, dan akan mulai
mengusir (evict) Pod secara paksa dari node tersebut untuk membebaskan ruang disk.
- Kehilangan Bukti Audit: Begitu kontainer di-restart, seluruh log historis hilang total,
mempersulit proses analisis insiden keamanan atau bug aplikasi.
✓ SOLUSI YANG BENAR:
- Konfigurasikan aplikasi untuk selalu membuang log ke saluran keluaran standar (stdout/stderr).
- Biarkan daemon pengumpul log tingkat kluster (seperti Fluent Bit atau Loki Promtail) yang berjalan
sebagai DaemonSet menangkap log stdout tersebut dan mengirimkannya ke server log terpusat (Elasticsearch/Loki).
Anti-Pattern 2: Asumsi Volume ReadWriteOnce (RWO) Dapat Dibagi Lintas Node dalam Deployment #
Mendeploy aplikasi web replica 3 (Deployment) yang dipasang ke satu PVC bertipe ReadWriteOnce.
ANTI-PATTERN: spec.replicas: 3 Terikat pada Satu PVC RWO
// KITA MELAKUKAN:
- Kita membuat satu objek PVC bernama `web-pvc` dengan mode `ReadWriteOnce`.
- Kita membuat Deployment dengan `replicas: 3` yang ketiganya menunjuk ke `web-pvc` yang sama.
// KONSEKUENSI DI PRODUKSI:
- Multi-Attach Error: Kube-scheduler menjadwalkan 3 Pod replica tersebut ke node worker yang berbeda
(misal `node-1`, `node-2`, `node-3`).
- Pod pertama di `node-1` akan berjalan sukses karena ia berhasil me-mount volume.
- Pod kedua dan ketiga di `node-2` dan `node-3` akan stuck selamanya di status `ContainerCreating`
dengan pesan error `Multi-Attach error for volume: volume-xyz can only be attached to single node`.
✓ SOLUSI YANG BENAR:
- Jika aplikasi kita benar-benar harus berbagi satu folder penulisan bersama lintas node,
gunakan access mode `ReadWriteMany` (RWX) dan pastikan StorageClass kita mendukung NFS/EFS.
- Jika aplikasi adalah database stateful yang membutuhkan penyimpanan terpisah per Pod,
jangan gunakan Deployment biasa. Gunakan StatefulSet dan manfaatkan properti `volumeClaimTemplates`
untuk membuat PVC unik secara otomatis untuk setiap replika Pod (`db-0`, `db-1`, `db-2`).
Anti-Pattern 3: Menggunakan emptyDir Memory Tanpa Batas Maksimal (sizeLimit) #
Membuat cache volume tipe RAM (Memory) untuk mengejar performa instan tanpa membatasi ukuran data yang boleh ditulis.
ANTI-PATTERN: emptyDir: { medium: Memory } Tanpa sizeLimit
// KITA MELAKUKAN:
- Kita membuat volume tmpfs (RAM) untuk menampung cache session web.
- Kita lupa membatasi ukuran maksimal memori di manifest YAML.
// KONSEKUENSI DI PRODUKSI:
- Monopoli RAM Node: Jika trafik melonjak, aplikasi akan terus menulis data session ke folder tmpfs.
Karena tmpfs menggunakan RAM node worker fisik, RAM node akan terkuras habis.
- Node Crash: Kernel Linux akan mendeteksi kehabisan memori kritis pada node host fisik.
Proses sistem penting (termasuk kubelet atau docker daemon) bisa terbunuh secara acak oleh OOM-killer,
menyebabkan node worker mati total (*Kernel Panic* atau *NotReady*).
✓ SOLUSI YANG BENAR:
- Selalu deklarasikan properti `sizeLimit` ketika menggunakan emptyDir berbasis Memory:
emptyDir:
medium: Memory
sizeLimit: "256Mi"
- Pastikan sizeLimit ini diselaraskan dengan memori request & limit kontainer agar tidak memicu
OOMKilled pada kontainer kita sendiri.
Ringkasan #
- Immutability Writable Layer — Secara default, sistem berkas lokal kontainer bersifat sementara (transient); data di dalamnya akan musnah saat kontainer mengalami crash atau restart.
- Volume Ephemeral (emptyDir) — Hidup dan mati mengikuti daur hidup Pod; sangat cocok untuk cache, scratch space pemrosesan data, atau bertukar file antar kontainer dalam satu Pod.
- Tmpfs untuk Performa — Gunakan
emptyDirberbasismedium: Memoryuntuk pemrosesan file berkecepatan tinggi, namun wajib batasi ukurannya dengan propertisizeLimit.- Volume Persistent (PV/PVC) — Menyimpan data secara independen di luar siklus hidup Pod; data aman meskipun Pod dihapus atau berpindah node worker fisik.
- Access Modes — Tentukan akses secara presisi:
ReadWriteOnce(RWO) untuk penguncian satu node worker (db), danReadWriteMany(RWX) untuk folder bersama lintas node (NFS).- Generic Ephemeral Volumes — Manfaatkan pola hibrida ini untuk meminta SSD cloud dinamis berukuran besar yang otomatis dihapus saat Pod selesai bekerja.
- Hindari Writable Layer untuk Log — Jangan menulis file log ke disk internal kontainer; gunakan stdout agar log dapat dikumpulkan secara terpusat tanpa memenuhi disk node.
- Gunakan Managed DB untuk Kredibilitas — Untuk database utama produksi, gunakan layanan managed cloud provider (RDS/Cloud SQL) guna meminimalkan risiko kehilangan data.