StatefulSet + PVC #
StatefulSet dan PVC adalah pasangan yang dirancang untuk bekerja bersama. StatefulSet menyediakan identitas yang stabil; PVC menyediakan storage yang persisten dan terikat ke identitas tersebut. Kombinasi keduanya memungkinkan Kubernetes menjalankan workload stateful dengan cara yang mendekati bagaimana mereka berjalan di VM tradisional, dengan tetap mempertahankan manfaat orkestrasi Kubernetes. Artikel ini membahas cara kerja kombinasi ini secara mendalam dan masalah-masalah yang umum ditemui.
Hubungan StatefulSet dan PVC #
Ketika StatefulSet menggunakan volumeClaimTemplates, Kubernetes membuat PVC secara otomatis untuk setiap Pod dengan nama yang mengikuti pola yang deterministik:
StatefulSet "postgres" dengan volumeClaimTemplate bernama "data":
Pod dibuat → PVC dibuat
─────────────────────────────────────────────
postgres-0 → data-postgres-0
postgres-1 → data-postgres-1
postgres-2 → data-postgres-2
Format nama PVC: <template-name>-<statefulset-name>-<ordinal>
Hubungan ini permanen dan tidak berubah. Jika postgres-1 di-restart, Pod baru yang menggantikannya masih bernama postgres-1 dan masih menggunakan PVC data-postgres-1. Data tidak pernah “tertukar” antar Pod.
Lifecycle PVC dalam StatefulSet #
Memahami lifecycle PVC saat StatefulSet di-scale atau dihapus adalah kunci mencegah kehilangan data yang tidak disengaja:
Scale Up #
StatefulSet postgres, replicas: 1 → 3
Kondisi awal:
postgres-0 berjalan, data-postgres-0 Bound
Scale up:
Kubernetes buat postgres-1 → cek apakah data-postgres-1 sudah ada
→ Belum ada → buat PVC baru data-postgres-1
→ PVC dibuat, menunggu binding
→ Setelah Bound, postgres-1 dimulai
Kubernetes buat postgres-2 → sama seperti postgres-1
Scale Down #
StatefulSet postgres, replicas: 3 → 1
Scale down (dari indeks tertinggi):
Hapus postgres-2 → Pod hilang, tapi data-postgres-2 TETAP ADA
Hapus postgres-1 → Pod hilang, tapi data-postgres-1 TETAP ADA
Kondisi akhir:
postgres-0 berjalan (data-postgres-0 Bound)
data-postgres-1: Bound tapi tidak ada Pod yang menggunakannya
data-postgres-2: Bound tapi tidak ada Pod yang menggunakannya
Ini adalah perilaku yang disengaja — Kubernetes tidak menghapus data saat scale down. Jika StatefulSet di-scale up lagi ke 3, postgres-1 akan me-mount data-postgres-1 yang sudah ada beserta semua datanya.
Hapus StatefulSet #
kubectl delete statefulset postgres
Hasil:
StatefulSet dihapus
Semua Pod dihapus
Semua PVC TETAP ADA (tidak ikut terhapus)
Untuk menghapus PVC secara manual:
kubectl delete pvc data-postgres-0 data-postgres-1 data-postgres-2
Atau gunakan cascade=foreground dengan label selector:
kubectl delete pvc -l app=postgres
Pola: Buat Ulang StatefulSet Tanpa Kehilangan Data #
Skenario ini sering terjadi saat perlu mengubah konfigurasi StatefulSet yang tidak bisa di-update secara langsung (seperti volumeClaimTemplates yang immutable setelah dibuat):
# Step 1: Hapus StatefulSet tapi PERTAHANKAN PVC dan Pod
kubectl delete statefulset postgres --cascade=orphan
# --cascade=orphan: hapus StatefulSet saja, Pod dan PVC tetap berjalan
# Step 2: Update manifest StatefulSet (misalnya ubah image, resource, dll)
# Edit file postgres-statefulset.yaml
# Step 3: Apply StatefulSet baru
kubectl apply -f postgres-statefulset.yaml
# StatefulSet baru mengadopsi Pod yang ada (berdasarkan label selector)
# Pod tidak di-restart karena spec-nya sama
# Step 4: Rolling update jika perlu
kubectl rollout status statefulset postgres
Pola: Migrasi Data ke PVC Baru #
Ketika butuh memindahkan data ke StorageClass yang berbeda (misalnya upgrade dari HDD ke SSD):
# Step 1: Scale down StatefulSet
kubectl scale statefulset postgres --replicas=0
# Step 2: Buat PVC baru dengan StorageClass yang diinginkan
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-postgres-0-new
spec:
accessModes: [ReadWriteOnce]
storageClassName: premium-ssd # StorageClass baru
resources:
requests:
storage: 100Gi
EOF
# Step 3: Buat Job untuk copy data dari PVC lama ke PVC baru
cat <<EOF | kubectl apply -f -
apiVersion: batch/v1
kind: Job
metadata:
name: data-migration
spec:
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: alpine:3.18
command:
- sh
- -c
- "cp -av /source/. /destination/"
volumeMounts:
- name: old-data
mountPath: /source
readOnly: true
- name: new-data
mountPath: /destination
volumes:
- name: old-data
persistentVolumeClaim:
claimName: data-postgres-0 # PVC lama
- name: new-data
persistentVolumeClaim:
claimName: data-postgres-0-new # PVC baru
EOF
# Step 4: Setelah Job selesai, hapus StatefulSet dan buat ulang dengan PVC baru
Troubleshooting Umum #
Pod Stuck di Pending: Volume Node Affinity Conflict #
kubectl describe pod postgres-1
# Events:
# Warning FailedScheduling 0/3 nodes are available:
# 3 node(s) had volume node affinity conflict
# Penyebab: PVC sudah bound ke PV yang punya node affinity ke zone lain
# Solusi: cek PV yang digunakan PVC dan node affinity-nya
kubectl get pvc data-postgres-1 -o yaml | grep volumeName
kubectl describe pv <volume-name> | grep -A 10 "Node Affinity"
PVC Stuck di Terminating #
kubectl get pvc data-postgres-0
# STATUS: Terminating (sudah lama, tidak kunjung hilang)
# Penyebab: PVC masih digunakan oleh Pod
# Kubernetes tidak bisa hapus PVC yang masih di-mount
kubectl get pods -o=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{range .spec.volumes[*]}{.persistentVolumeClaim.claimName}{"\n"}{end}{end}' | grep data-postgres-0
# Temukan Pod yang masih menggunakan PVC ini, hapus Pod-nya dulu
Data Tidak Tersedia Setelah Pod Restart #
# Cek apakah Pod menggunakan PVC yang benar
kubectl describe pod postgres-0 | grep -A 10 Volumes
# Cek apakah PVC Bound ke PV yang benar
kubectl describe pvc data-postgres-0
# Cek apakah data benar-benar ada di volume
kubectl exec postgres-0 -- ls -la /var/lib/postgresql/data
Ringkasan #
- Nama PVC deterministik — format
<template>-<statefulset>-<ordinal>; hubungan antara Pod dan PVC permanen dan tidak berubah.- Scale down tidak menghapus PVC — data aman saat scale down; PVC tetap ada dan bisa di-pakai lagi saat scale up kembali.
- Delete StatefulSet tidak menghapus PVC — proteksi dari kehilangan data; PVC harus dihapus secara eksplisit jika memang ingin menghapus data.
--cascade=orphanuntuk update StatefulSet dengan aman — hapus StatefulSet tanpa menghapus Pod dan PVC; Pod tetap berjalan, StatefulSet baru mengadopsinya.- Migrasi data via Job — gunakan Job dengan dua volume mount (sumber dan tujuan) untuk memindahkan data antar PVC; scale down StatefulSet sebelum migrasi untuk konsistensi.
- Volume node affinity conflict — terjadi ketika PV punya node affinity ke zone yang tidak tersedia untuk Pod; gunakan
WaitForFirstConsumerdi StorageClass untuk mencegah ini.
← Sebelumnya: Database di Kubernetes Berikutnya: Backup & Restore →