StatefulSet + PVC #
Di dalam arsitektur sistem terdistribusi, menjalankan aplikasi yang memiliki status (stateful workloads)—seperti database PostgreSQL, MySQL Galera cluster, Apache Kafka, atau Zookeeper—memerlukan jaminan kestabilan ganda. Kita tidak hanya membutuhkan kestabilan identitas jaringan dan nama pod, tetapi juga kestabilan hubungan antara masing-masing replika Pod dengan media penyimpanan data fisiknya. Jika salah satu Pod database mati dan dibangun kembali oleh control plane, ia harus dijamin terhubung kembali ke volume penyimpanan yang sama persis dengan yang ia gunakan sebelumnya.
Untuk mewujudkan jaminan ganda ini, Kubernetes menyinergikan secara erat antara kontroler StatefulSet dengan PersistentVolumeClaim (PVC). Sinergi ini dikonfigurasikan secara dinamis melalui mekanisme volumeClaimTemplates. Artikel ini akan membedah secara mendalam bagaimana StatefulSet berpasangan dengan PVC, siklus hidup penyimpanan saat terjadi penskalan (scaling) kluster database, taktik memodifikasi parameter StatefulSet yang bersifat immutable menggunakan metode orphan deletion, langkah detail migrasi data antar-StorageClass, hingga solusi dari masalah hak akses filesystem (permission denied).
Hubungan StatefulSet dan PVC: Skema Penamaan Deterministik #
Ketika kita membuat StatefulSet, kita dilarang mendeklarasikan PVC secara statis di dalam blok spec.template.spec.volumes. Jika kita melakukannya, seluruh replika Pod yang tercipta akan me-mount objek PVC yang sama persis, yang akan berujung pada kegagalan mount jika volume bertipe block storage (RWO).
Sebagai gantinya, kita menggunakan blok volumeClaimTemplates. Properti ini bertindak sebagai pabrik pembuatan PVC otomatis yang terintegrasi secara langsung dengan proses startup masing-masing Pod. Setiap kali StatefulSet membuat Pod baru dengan nomor indeks ordinal $i$, kontroler StatefulSet secara paralel akan membuat objek PVC baru di API Server dengan penamaan yang deterministik:
$$\text{Format Nama PVC} = \text{{nama-template}}-\text{{nama-statefulset}}-\text{{indeks-ordinal}}$$
Untuk memahami bagaimana Kubelet menjaga konsistensi hubungan ini ketika terjadi peristiwa crash atau pemindahan node, mari kita perhatikan diagram daur hidup PVC saat terjadi proses scale-down dan scale-up kembali berikut:
flowchart TD
subgraph ScaleDown ["Siklus Scale-Down (Replicas: 3 ➔ 1)"]
SD_Start["Scale-Down Dimulai"] --> SD_Pod2["1. Hapus Pod mariadb-2"]
SD_Pod2 --> SD_PVC2{"Apakah PVC\ndata-mariadb-2\nikut dihapus?"}
SD_PVC2 -- "TIDAK (Sengaja)" --> SD_Retain2["PVC Tetap Bertahan (Bound) & Data Aman"]
SD_Retain2 --> SD_Pod1["2. Hapus Pod mariadb-1"]
SD_Pod1 --> SD_PVC1{"Apakah PVC\ndata-mariadb-1\nikut dihapus?"}
SU_Start["Scale-Up Dimulai"] --> SU_Pod1["1. Buat Pod mariadb-1"]
SD_PVC1 -- "TIDAK" --> SD_Retain1["PVC Tetap Bertahan (Bound) & Data Aman"]
end
subgraph ScaleUp ["Siklus Scale-Up Kembali (Replicas: 1 ➔ 3)"]
SU_Pod1 --> SU_Mount1["Kubelet Otomatis Mount PVC Lama data-mariadb-1"]
SU_Mount1 --> SU_Ready1["Pod mariadb-1 Siap Dengan Data Lama Utuh"]
SU_Ready1 --> SU_Pod2["2. Buat Pod mariadb-2"]
SU_Pod2 --> SU_Mount2["Kubelet Otomatis Mount PVC Lama data-mariadb-2"]
SU_Mount2 --> SU_Ready2["Pod mariadb-2 Siap Dengan Data Lama Utuh"]
end
SD_Retain1 --> SU_Start
Hubungan antara Pod mariadb-1 dengan PVC data-mariadb-1 bersifat permanen dan tidak pernah berubah sepanjang masa hidup StatefulSet. Jika VM node worker tempat mariadb-1 berjalan mati total, scheduler akan memindahkan Pod tersebut ke node worker lain yang sehat. Pod baru yang menyala akan tetap memiliki nama mariadb-1 dan dijamin akan me-mount PVC data-mariadb-1 yang sama. Data kita tetap konsisten dan aman lintas kegagalan hardware.
Siklus Hidup PVC di dalam StatefulSet #
Perilaku daur hidup PVC di dalam StatefulSet dirancang dengan tingkat proteksi data yang sangat tinggi. Berikut adalah detail mekanismenya pada tiga skenario operasional:
1. Skenario Scale Up (Penambahan Replika) #
Saat kita menaikkan jumlah replika (misalnya dari replicas: 1 menjadi replicas: 3), StatefulSet Controller akan memproses pembuatan Pod secara berurutan (OrderedReady):
- Kontroler melihat
db-1akan dibuat. Ia memeriksa apakah PVCdata-db-1sudah ada di API Server. - Jika belum ada, ia membuat objek PVC
data-db-1dan menunggu hingga statusnya menjadiBound. - Setelah disk fisik berhasil dibuat oleh CSI driver dan status PVC menjadi
Bound, Poddb-1baru akan dijadwalkan dan dinyalakan. - Proses ini diulang secara berurutan untuk Pod
db-2.
2. Skenario Scale Down (Pengurangan Replika) #
Saat kita menurunkan jumlah replika (misalnya dari 3 menjadi 1), StatefulSet Controller akan menghapus Pod mulai dari indeks ordinal tertinggi (db-2 kemudian db-1 secara berurutan).
- Penting: Ketika Pod
db-2dihapus, objek PVCdata-db-2dan disk fisiknya di cloud TIDAK IKUT DIHAPUS. - Alasan Desain: Ini adalah pengaman bawaan (safety rails) dari Kubernetes untuk melindungi data berharga kita. Jika kita secara tidak sengaja menurunkan replika database, data kita tetap utuh. Saat kita menaikkan kembali replika ke 3, Pod
db-2baru akan otomatis menyambung kembali ke PVCdata-db-2yang lama, lengkap dengan seluruh data historisnya.
3. Skenario Penghapusan StatefulSet #
Jika kita menjalankan perintah kubectl delete statefulset db, Kubernetes akan menghapus objek pengontrol StatefulSet beserta seluruh Pod di dalamnya. Namun, seluruh objek PVC tetap dipertahankan.
- Untuk benar-benar membuang data dan menghentikan tagihan disk cloud, kita harus menghapus PVC secara manual:
kubectl delete pvc -l app=mariadb -n database
Taktik Modifikasi Parameter Immutable: Metode Cascade Orphan #
Di dalam operasional produksi, kita sering kali dihadapkan pada situasi di mana kita harus mengubah properti di dalam volumeClaimTemplates (misalnya mengubah kapasitas disk default dari 50Gi menjadi 100Gi atau mengganti nama storageClassName).
- Tantangan: Kubernetes memblokir modifikasi langsung pada properti
volumeClaimTemplateskarena dianggap sebagai objek yang bersifat immutable (tidak dapat diubah setelah dibuat). Jika kita memaksakan perintahkubectl apply, API Server akan menolak request tersebut dengan error.
Untuk memodifikasi parameter ini tanpa memicu kegagalan database (zero-downtime database downtime), kita dapat menggunakan taktik Cascade Orphan Deletion:
Langkah 1: Hapus StatefulSet dengan Kebijakan Orphan #
Kita menghapus objek StatefulSet saja, namun menginstruksikan Kubernetes untuk membiarkan Pod database dan PVC-nya tetap hidup secara independen di kluster:
kubectl delete statefulset mariadb-cluster --cascade=orphan -n database
Setelah perintah ini dijalankan, objek StatefulSet hilang, namun Pod mariadb-cluster-0, mariadb-cluster-1, dan mariadb-cluster-2 tetap aktif berjalan melayani kueri SQL seperti biasa.
Langkah 2: Ubah Spesifikasi di Repositori Manifest YAML #
Buka berkas manifest YAML StatefulSet kita, lalu ubah spesifikasi di dalam volumeClaimTemplates sesuai kebutuhan baru (misalnya mengubah StorageClass atau ukuran disk).
Langkah 3: Terapkan Manifest StatefulSet yang Baru #
Jalankan perintah apply untuk membuat kembali objek StatefulSet:
kubectl apply -f mariadb-statefulset-updated.yaml
StatefulSet Controller yang baru menyala akan memindai kluster. Ia mendeteksi bahwa Pod mariadb-cluster-0 s.d. 2 sudah berjalan dan memiliki label selector yang cocok. Kontroler akan langsung mengadopsi (adopt) Pod-Pod tersebut ke dalam kepemilikannya secara transparan tanpa melakukan restart kontainer. Prosedur ini sangat aman dan menghindari risiko downtime database.
Pola Migrasi Data Antar-PVC (Upgrade HDD ke SSD / Lintas SC) #
Ketika kita ingin meng-upgrade performa storage database (misalnya dari kelas HDD lambat standard-hdd ke kelas SSD premium premium-ssd), kita tidak bisa sekadar mengubah nama StorageClass pada YAML PVC yang sedang berjalan karena properti tersebut immutable.
Kita harus melakukan proses migrasi data secara manual menggunakan bantuan Job migrasi khusus:
Langkah 1: Hentikan Trafik Penulisan Database #
Turunkan replika StatefulSet menjadi 0 agar database menghentikan seluruh proses I/O dan melepas ikatan volume disk fisik:
kubectl scale statefulset mariadb-cluster --replicas=0 -n database
Langkah 2: Buat PVC Baru dengan StorageClass SSD #
Buat objek PVC baru yang akan menampung data migrasi kita:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-disk-mariadb-cluster-0-new
namespace: database
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "premium-ssd-sc" # StorageClass baru (SSD)
resources:
requests:
storage: 50Gi # Kapasitas baru
Langkah 3: Jalankan Job Migrasi Menggunakan rsync #
Kita membuat objek Job sementara yang bertugas me-mount PVC lama (sumber) dan PVC baru (tujuan) secara bersamaan, lalu menyalin seluruh struktur data bytes menggunakan rsync untuk menjaga integritas file permissions dan kepemilikan owner:
apiVersion: batch/v1
kind: Job
metadata:
name: database-migration-job
namespace: database
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrator
image: instrumentisto/rsync:latest
command:
- sh
- -c
# Menyalin data secara rekursif, menjaga timestamps, permissions, dan progress log:
- "rsync -avz --progress /mnt/source/ /mnt/dest/"
volumeMounts:
- name: source-vol
mountPath: /mnt/source
readOnly: true # Mengunci PVC lama sebagai read-only
- name: dest-vol
mountPath: /mnt/dest
volumes:
- name: source-vol
persistentVolumeClaim:
claimName: data-disk-mariadb-cluster-0 # PVC lama (HDD)
- name: dest-vol
persistentVolumeClaim:
claimName: data-disk-mariadb-cluster-0-new # PVC baru (SSD)
Langkah 4: Bersihkan dan Ganti Nama PVC #
Setelah Job selesai berjalan sukses (Completed), hapus PVC lama dan ubah nama PVC baru (atau buat kembali StatefulSet dengan menunjuk ke template PVC yang baru).
Troubleshooting Umum Integrasi StatefulSet dan PVC #
1. Pod Stuck Pending: “Volume Node Affinity Conflict” #
- Gejala: Pod
mariadb-cluster-1stuck di statusPendingsetelah kita melakukan restart node. Log events menunjukkan error:1 node(s) had volume node affinity conflict. - Penyebab: Ini sering terjadi pada kluster multi-zona cloud. PVC
data-disk-mariadb-cluster-1telah terikat ke PV fisik yang secara permanen dikunci di Zona A (misalnya karena StorageClass menggunakan modeImmediate). Namun, scheduler mencoba menjadwalkan Podmariadb-cluster-1ke node worker di Zona B karena Zona A sedang penuh. - Solusi: Ubah konfigurasi
StorageClasskita agar menggunakanvolumeBindingMode: WaitForFirstConsumer. Jika PV sudah terlanjur dibuat di zona yang salah, kita terpaksa menghapus PVC dan membiarkan StatefulSet membuat ulang di zona yang benar (dengan konsekuensi kehilangan data jika data tidak di-backup terlebih dahulu).
2. PVC Stuck di Status Terminating #
- Gejala: Kita menjalankan perintah
kubectl delete pvc data-disk-mariadb-cluster-0, namun status PVC stuck di statusTerminatingselama berjam-jam. - Penyebab: Kubernetes memiliki fitur pengaman bernama PVC Protection Finalizer (
kubernetes.io/pvc-protection). Kubernetes menolak keras menghapus metadata PVC dari etcd selama PVC tersebut masih di-mount oleh Pod aktif (meskipun Pod tersebut sedang dalam kondisi Error atau CrashLoopBackOff). - Solusi: Cari Pod mana yang masih mengunci PVC tersebut, lalu hapus atau matikan Pod tersebut. Begitu Pod mati, PVC akan langsung terhapus otomatis secara bersih dari sistem etcd.
Anti-Pattern Terkait StatefulSet & PVC #
Berikut adalah tiga kesalahan konfigurasi fatal yang sering mengacaukan operasional stateful kluster produksi:
Anti-Pattern 1: Mengabaikan fsGroup di SecurityContext, Memicu Permission Denied saat DB Tulis Data #
Mendeploy database (seperti PostgreSQL) di mana kontainer berjalan sebagai user non-root (ID 999), namun Kubelet memasang volume dengan hak akses root (0000 atau read-only).
ANTI-PATTERN: Mendeploy Postgres StatefulSet Tanpa fsGroup
// KITA MELAKUKAN:
- Kita mendeploy postgres StatefulSet. Di dalam image Docker, postgres berjalan sebagai user 999.
- Kita lupa menuliskan konfigurasi `fsGroup` di tingkat spesifikasi Pod securityContext.
// KONSEKUENSI DI PRODUKSI:
- Permission Denied: Kubelet akan me-mount volume disk fisik baru dengan owner default root (user ID 0).
- Ketika proses postgres mencoba menulis file inisialisasi kluster ke `/var/lib/postgresql/data`,
sistem operasi Linux akan menolak transaksi tersebut dengan error:
`initdb: error: directory "/var/lib/postgresql/data/pgdata" exists but is not writable`.
- Pod database kita akan stuck di status CrashLoopBackOff selamanya.
✓ SOLUSI YANG BENAR:
- Selalu setel properti `fsGroup` di dalam blok `securityContext` Pod StatefulSet, disamakan dengan
user ID internal database kita (untuk Postgres, biasanya menggunakan user ID 999):
spec:
securityContext:
fsGroup: 999 # Kubelet akan otomatis mengubah owner volume mount ke user 999 saat startup
Anti-Pattern 2: Menghapus StatefulSet Secara Terburu-buru untuk Reset Data, Menyisakan Tagihan Cloud #
Mengasumsikan bahwa menghapus objek StatefulSet akan ikut menghapus PVC dan disk fisik di cloud provider.
ANTI-PATTERN: kubectl delete statefulset mariadb-cluster untuk membersihkan data
// KITA MELAKUKAN:
- Kita selesai melakukan pengujian beban kerja database besar (misalnya mengalokasikan disk 500GB per Pod).
- Kita menghapus StatefulSet menggunakan perintah `kubectl delete statefulset mariadb-cluster`.
// KONSEKUENSI DI PRODUKSI:
- Kebocoran Anggaran Cloud: Kubernetes akan menghapus Pod-Pod database kita secara patuh.
Namun, objek-objek PVC 500GB beserta volume fisik cloud-nya tetap menyala secara aktif di console cloud.
- Kita akan terus ditagih ribuan dolar per bulan oleh cloud provider untuk disk-disk kosong
yang sebenarnya tidak terhubung ke Pod mana pun.
✓ SOLUSI YANG BENAR:
- Selalu pastikan kita menghapus PVC secara eksplisit setelah menghapus StatefulSet jika kita memang
berniat membuang data tersebut:
`kubectl delete pvc -l app=mariadb -n database`
Anti-Pattern 3: Menyetel podManagementPolicy: Parallel pada Database Berbasis Quorum #
Mengubah kebijakan manajemen Pod StatefulSet menjadi Parallel demi mempercepat startup kluster database.
ANTI-PATTERN: podManagementPolicy: Parallel pada Kluster Cassandra / Consul
// KITA MELAKUKAN:
- Kita mendeploy kluster database Apache Cassandra dengan 3 replika.
- Kita menyetel `podManagementPolicy: Parallel` agar ketiga Pod menyala bersamaan tanpa perlu mengantre.
// KONSEKUENSI DI PRODUKSI:
- Kegagalan Bootstrap: Tiga node database akan menyala di waktu yang sama persis.
Karena belum ada satu pun node master yang siap untuk bertindak sebagai *seed node*,
masing-masing node akan saling menolak koneksi sinkronisasi awal.
- Kluster database kita akan mengalami split-state sejak startup, gagal membentuk konsensus quorum,
dan berujung pada hang sistem.
✓ SOLUSI YANG BENAR:
- Gunakan kebijakan default `podManagementPolicy: OrderedReady` untuk database yang membutuhkan bootstrap quorum.
- Hanya gunakan mode `Parallel` jika aplikasi kita hanya membutuhkan nama ordinal dan PVC stabil,
namun tidak memiliki ketergantungan urutan startup (misalnya untuk workers komputasi paralel).
Ringkasan #
- Jaminan Hubungan Stabil — Sinergi StatefulSet dan PVC menjamin setiap Pod dengan indeks ordinal $i$ selalu terhubung kembali ke PVC yang sama persis lintas restart.
- Proteksi PVC Otomatis — Kubernetes secara sengaja tidak menghapus objek PVC ketika StatefulSet di-scale down atau dihapus guna melindungi data dari kesalahan manusia.
- Trik Cascade Orphan — Gunakan perintah
kubectl delete --cascade=orphanuntuk memodifikasi konfigurasivolumeClaimTemplatesyang immutable tanpa downtime database.- Migrasi via Job rsync = Gunakan Job dengan tool
rsyncuntuk memindahkan data antar-PVC ketika ingin melakukan upgrade StorageClass (misalnya HDD ke SSD).- Kunci fsGroup untuk Akses — Selalu setel parameter
fsGrouppadasecurityContextPod agar Kubelet secara otomatis menyinkronkan hak akses owner volume ke user kontainer.- Pembersihan Manual PVC — Ingatlah untuk selalu menghapus PVC secara eksplisit setelah menghapus StatefulSet guna menghindari pemborosan tagihan sewa storage cloud.
- OrderedReady untuk Quorum — Pertahankan kebijakan
podManagementPolicy: OrderedReadyuntuk database clustering guna memastikan proses sinkronisasi bootstrap berjalan sukses.
← Sebelumnya: Database di Kubernetes Berikutnya: Backup & Restore →