Belajar Kubernetes Block Storage RWO - StatefulSet PostgreSQL & Persistent Application Data
Episode 6 of 28

Belajar Kubernetes Block Storage RWO - StatefulSet PostgreSQL & Persistent Application Data

Menyusun StatefulSet PostgreSQL dengan volumeClaimTemplates di atas StorageClass Longhorn, menyetel fsGroup dan securityContext yang benar, lalu membuktikan persistensi: menulis data, menghapus Pod, dan memastikan data kembali utuh setelah Pod restart.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Di episode 5 kita membuat PVC pertama dan melihat dynamic provisioning bekerja. Sekarang waktunya menggabungkan semuanya untuk studi kasus nyata: menjalankan PostgreSQL sebagai StatefulSet dengan data persisten di atas Longhorn.

Mengapa episode ini menjadi tonggak? Karena di sinilah kalian membuktikan bahwa block storage RWO benar-benar berfungsi untuk database: tulis data, crash/delete Pod, dan data harus tetap hidup. Ini juga pertama kalinya kita memakai volumeClaimTemplates yang menghasilkan PVC per-Pod secara otomatis — fondasi arsitektur StatefulSet.

Menyusun StatefulSet PostgreSQL

Manifest Lengkap

yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
  namespace: default
spec:
  serviceName: postgres
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      securityContext:
        fsGroup: 999
      containers:
        - name: postgres
          image: postgres:16
          ports:
            - containerPort: 5432
              name: postgres
          env:
            - name: POSTGRES_USER
              valueFrom:
                secretKeyRef:
                  name: db-secret
                  key: username
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-secret
                  key: password
            - name: POSTGRES_DB
              value: laravel
          volumeMounts:
            - name: data
              mountPath: /var/lib/postgresql/data
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: data-postgres-0
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: longhorn-ha
        resources:
          requests:
            storage: 10Gi

volumeClaimTemplates Membuat PVC Per-Pod

Perhatikan: di blok volumeClaimTemplates, template bernama data menghasilkan PVC data-postgres-0 untuk Pod postgres-0. Jika suatu saat kalian menambah replica (postgres-1), akan dibuat data-postgres-1 — setiap Pod punya PVC sendiri. Ini alasan StatefulSet + volumeClaimTemplates adalah fondasi yang tepat untuk database di Kubernetes.

Buat dan cek PVC
kubectl apply -f postgres-sts.yaml
kubectl get pvc

Hasil yang diharapkan:

text
NAME                 STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS
data-postgres-0      Bound    ...      10Gi       RWO            longhorn-ha

Headless Service

StatefulSet butuh Service headless (clusterIP: None) agar Pod bisa diakses dengan nama DNS stabil seperti postgres-0.postgres.default.svc:

yaml
apiVersion: v1
kind: Service
metadata:
  name: postgres
spec:
  clusterIP: None
  selector:
    app: postgres
  ports:
    - port: 5432
      targetPort: 5432

Ini bukan soal Poin HA — tapi identitas network yang stabil untuk Pod yang memang identitasnya stabil.

Database & fsGroup

Kenapa fsGroup dan SecurityContext Wajib

Image postgres:16 berjalan sebagai user postgres (UID 999). Volume Longhorn yang di-mount — tanpa fsGroup — bisa memiliki permission yang tidak sesuai, membuat PostgreSQL tidak bisa menulis data page dan gagal start. Dengan fsGroup: 999, Kubernetes men-set ACL SE/group pada mount sehingga container process (UID 999) dapat menulis.

Tambahkan juga runAsNonRoot dan allowPrivilegeEscalation: false untuk hardening:

yaml
spec:
  securityContext:
    fsGroup: 999
    runAsNonRoot: true
  containers:
    - name: postgres
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        runAsUser: 999

Catatan: readOnlyRootFilesystem: true untuk Postgres umumnya perlu mount tambahan untuk /tmp, /run/postgresql, dan pg_stat_tmp. Untuk lab, cukup pakai fsGroup + runAsUser dan biarkan ro volumen diskusi di episode keamanan.

Warning

Kesalahan paling umum: UID container tidak cocok dengan permission volume. Gejala: Pod CrashLoopBackOff dengan log could not open directory "/var/lib/postgresql/data": Permission denied. Solusi: tambahkan fsGroup yang sesuai (biasanya UID process database, 999 untuk postgres, 70± untuk MySQL).

Verifikasi Persistensi

Sekarang bagian paling penting: buktikan data bertahan.

1. Tulis Data

Buat database & tabel
kubectl exec -it postgres-0 -- psql -U postgres -d laravel -c \
  "CREATE TABLE IF NOT EXISTS demo (id serial PRIMARY KEY, note text);"
kubectl exec -it postgres-0 -- psql -U postgres -d laravel -c \
  "INSERT INTO demo (note) VALUES ('awet selamanya');"

2. Hapus Pod (Simulasikan Crash/Restart)

Hapus pod postgres-0
kubectl delete pod postgres-0 --wait=false

StatefulSet langsung membuat Pod baru dengan nama yang sama. PVC data-postgres-0 tidak ikut terhapus karena volumeClaimPersist.

3. Lihat Data Masih Ada

Verifikasi data setelah restart
kubectl get pod -l app=postgres
kubectl exec -it postgres-0 -- psql -U postgres -d laravel -c \
  "SELECT * FROM demo;"

Jika output menampilkan awet selamanya, persistensi terbukti. Di dashboard Longhorn, kalian juga akan melihat volume data-postgres-0 berpindah Attached ketika Pod baru dijadwalkan — replicanya tetap pada node yang sama secara best-effort, atau pindah bila node yang lama tidak sehat.

Tip

Untuk uji yang lebih brutal: cordon node worker-1 lalu delete Pod. Longhorn akan me-migrasi volume ke node sehat, dan PostgreSQL jalan kembali — membuktikan tidak hanya persistensi, tapi juga failover antar node. Kita bahas detail drain/cordon di episode 22.

Penutup

Inti yang harus dibawa pulang:

  • StatefulSet + volumeClaimTemplates menghasilkan PVC per-Pod (data-postgres-0, dst).
  • Set fsGroup dan UID process database agar container bisa menulis volume.
  • Headless Service memberi identitas DNS stabil untuk Pod StatefulSet.
  • Delete Pod → PVC & data tetap hidup; verifikasi dengan menulis data sebelum delete.

Di episode 7 selanjutnya kita akan men-deploy Laravel + PostgreSQL di atas Longhorn — menyusun Deployment Laravel, menghubungkan lewat ConfigMap & Secret, dan menjalankan migrasi database sebagai Job. Sampai jumpa di episode 7!

Belajar Kubernetes Block Storage RWO - StatefulSet PostgreSQL & Persistent Application Data | Belajar Kubernetes Block Storage RWO