Belajar Kubernetes - Episode 27 - Pengenalan dan Penjelasan StatefulSet

Belajar Kubernetes - Episode 27 - Pengenalan dan Penjelasan StatefulSet

Di episode ini kita akan coba bahas Kubernetes StatefulSet untuk managing stateful application. Kita akan mempelajari stable network identity, persistent storage, ordered deployment, dan best practice untuk database dan stateful workload.

Arman Dwi Pangestu
Arman Dwi PangestuApril 2, 2026
0 views
8 min read

Pendahuluan

Catatan

Untuk kalian yang ingin membaca episode sebelumnya, bisa click thumbnail episode 26 di bawah ini

Episode 26Episode 26

Di episode sebelumnya kita sudah belajar tentang Deployment untuk managing stateless application dengan rolling update dan easy scaling. Selanjutnya di episode 27 kali ini, kita akan coba bahas StatefulSet, designed specifically untuk stateful application yang require stable network identity dan persistent storage.

Catatan: Disini saya akan menggunakan Kubernetes Cluster yang di install melalui K3s.

Sementara Deployment work great untuk stateless application, stateful application seperti database, message queue, dan distributed system need guarantee tentang Pod identity, ordering, dan storage persistence. StatefulSet provide guarantee ini.

Apa Itu StatefulSet?

StatefulSet adalah Kubernetes workload resource yang manage stateful application, providing stable network identity, persistent storage, dan ordered deployment dan scaling.

Bayangkan StatefulSet seperti numbered team dimana setiap member punya specific role dan identity - member-0 selalu leader, member-1 selalu backup, dan seterusnya. Tidak seperti Deployment dimana semua Pod interchangeable, StatefulSet Pod punya unique, persistent identity.

Karakteristik kunci StatefulSet:

  • Stable network identity - Setiap Pod dapat predictable hostname
  • Persistent storage - Setiap Pod bisa punya PersistentVolume sendiri
  • Ordered deployment - Pod created sequentially (0, 1, 2...)
  • Ordered scaling - Pod scaled up/down in order
  • Ordered update - Pod updated one at a time in order
  • Stable DNS name - Pod accessible via predictable DNS
  • Sticky identity - Pod identity persist across rescheduling

StatefulSet vs Deployment

Memahami key difference:

AspekStatefulSetDeployment
Pod IdentityStable, unique (web-0, web-1)Random (web-abc123)
Network IdentityStable hostnameRandom hostname
StorageIndividual PVC per PodShared atau no storage
Deployment OrderSequential (0→1→2)Parallel
Scaling OrderSequentialParallel
Use CaseDatabase, stateful appWeb server, API
Pod ReplacementSame identity preservedNew random identity

Kenapa Gunakan StatefulSet?

StatefulSet solve critical challenge untuk stateful application:

  • Database cluster - Master-slave replication dengan stable identity
  • Distributed system - Node need to know each other's address
  • Message queue - Persistent storage untuk message durability
  • Caching system - Stable identity untuk cache distribution
  • Consensus system - Ordered deployment untuk leader election
  • Data persistence - Setiap Pod maintain data sendiri
  • Predictable scaling - Controlled order untuk adding/removing node

Tanpa StatefulSet, managing stateful application akan require complex custom logic untuk identity management, storage allocation, dan ordered operation.

Membuat StatefulSet

Mari kita buat basic StatefulSet.

Basic StatefulSet

Kubernetesweb-statefulset.yml
apiVersion: v1
kind: Service
metadata:
    name: nginx
    labels:
        app: nginx
spec:
    ports:
        - port: 80
          name: web
    clusterIP: None
    selector:
        app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
    name: web
spec:
    serviceName: "nginx"
    replicas: 3
    selector:
        matchLabels:
            app: nginx
    template:
        metadata:
            labels:
                app: nginx
        spec:
            containers:
                - name: nginx
                  image: nginx:1.25
                  ports:
                      - containerPort: 80
                        name: web

Apply StatefulSet:

Kubernetesbash
sudo kubectl apply -f web-statefulset.yml

Watch Pod being created:

Kubernetesbash
sudo kubectl get pods -w -l app=nginx

Output menunjukkan sequential creation:

Kubernetesbash
NAME    READY   STATUS                  RESTARTS   AGE
web-0   0/1     Pending                 0          0s
web-0   0/1     ContainerCreating       0          0s
web-0   1/1     Running                 0          10s
web-1   0/1     Pending                 0          0s
web-1   0/1     ContainerCreating       0          0s
web-1   1/1     Running                 0          10s
web-2   0/1     Pending                 0          0s
web-2   0/1     ContainerCreating       0          0s
web-2   1/1     Running                 0          10s

Perhatikan:

  • Pod created sequentially: web-0, kemudian web-1, kemudian web-2
  • Setiap Pod punya stable, predictable name
  • Next Pod hanya start setelah previous Running dan Ready

StatefulSet dengan Persistent Storage

Kubernetesstatefulset-storage.yml
apiVersion: v1
kind: Service
metadata:
    name: nginx
spec:
    ports:
        - port: 80
          name: web
    clusterIP: None
    selector:
        app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
    name: web
spec:
    serviceName: "nginx"
    replicas: 3
    selector:
        matchLabels:
            app: nginx
    template:
        metadata:
            labels:
                app: nginx
        spec:
            containers:
                - name: nginx
                  image: nginx:1.25
                  ports:
                      - containerPort: 80
                        name: web
                  volumeMounts:
                      - name: www
                        mountPath: /usr/share/nginx/html
    volumeClaimTemplates:
        - metadata:
              name: www
          spec:
              accessModes: ["ReadWriteOnce"]
              resources:
                  requests:
                      storage: 1Gi

Ini create:

  • 3 Pod: web-0, web-1, web-2
  • 3 PVC: www-web-0, www-web-1, www-web-2
  • Setiap Pod dapat persistent storage sendiri

Headless Service

StatefulSet require Headless Service untuk network identity.

Apa Itu Headless Service?

Headless Service (clusterIP: None) tidak load balance. Instead, dia return IP address individual Pod, enabling direct Pod-to-Pod communication.

Kubernetesheadless-service.yml
apiVersion: v1
kind: Service
metadata:
    name: nginx
spec:
    ports:
        - port: 80
          name: web
    clusterIP: None  # Make it headless
    selector:
        app: nginx

DNS untuk StatefulSet Pod

Setiap Pod dapat predictable DNS name:

plaintext
<pod-name>.<service-name>.<namespace>.svc.cluster.local

Contoh:

  • web-0.nginx.default.svc.cluster.local
  • web-1.nginx.default.svc.cluster.local
  • web-2.nginx.default.svc.cluster.local

Test DNS resolution:

Kubernetesbash
sudo kubectl run -it --rm debug --image=busybox:1.36 --restart=Never -- nslookup web-0.nginx

Scaling StatefulSet

Scale up dan down in order.

Scale Up

Kubernetesbash
sudo kubectl scale statefulset web --replicas=5

Pod created sequentially:

  • web-3 created dan become Ready
  • Kemudian web-4 created

Scale Down

Kubernetesbash
sudo kubectl scale statefulset web --replicas=2

Pod deleted in reverse order:

  • web-4 deleted dulu
  • Kemudian web-3 deleted
  • web-0, web-1, web-2 remain

Important

Penting: Scaling down tidak delete PersistentVolumeClaim. Mereka remain untuk data safety dan bisa reused jika kalian scale back up.

Update Strategy

StatefulSet support dua update strategy.

RollingUpdate (Default)

Update Pod one at a time in reverse order:

Kubernetesrolling-update.yml
spec:
    updateStrategy:
        type: RollingUpdate
        rollingUpdate:
            partition: 0

Partition: Hanya Pod dengan ordinal >= partition yang updated.

Contoh dengan partition=2:

  • web-2, web-3, web-4 updated
  • web-0, web-1 remain on old version

OnDelete

Pod hanya updated ketika manually deleted:

Kubernetesondelete-update.yml
spec:
    updateStrategy:
        type: OnDelete

Useful untuk manual control over update.

Contoh Praktis

Contoh 1: MySQL Master-Slave Replication

Kubernetesmysql-statefulset.yml
apiVersion: v1
kind: Service
metadata:
    name: mysql
spec:
    ports:
        - port: 3306
          name: mysql
    clusterIP: None
    selector:
        app: mysql
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
    name: mysql
spec:
    serviceName: mysql
    replicas: 3
    selector:
        matchLabels:
            app: mysql
    template:
        metadata:
            labels:
                app: mysql
        spec:
            containers:
                - name: mysql
                  image: mysql:8.0
                  ports:
                      - containerPort: 3306
                        name: mysql
                  env:
                      - name: MYSQL_ROOT_PASSWORD
                        valueFrom:
                            secretKeyRef:
                                name: mysql-secret
                                key: password
                  volumeMounts:
                      - name: data
                        mountPath: /var/lib/mysql
                  resources:
                      requests:
                          memory: "512Mi"
                          cpu: "500m"
                      limits:
                          memory: "1Gi"
                          cpu: "1000m"
    volumeClaimTemplates:
        - metadata:
              name: data
          spec:
              accessModes: ["ReadWriteOnce"]
              resources:
                  requests:
                      storage: 10Gi

Contoh 2: PostgreSQL Cluster

Kubernetespostgres-statefulset.yml
apiVersion: v1
kind: ConfigMap
metadata:
    name: postgres-config
data:
    POSTGRES_DB: "myapp"
    POSTGRES_USER: "appuser"
---
apiVersion: v1
kind: Secret
metadata:
    name: postgres-secret
type: Opaque
stringData:
    POSTGRES_PASSWORD: "secretpassword"
---
apiVersion: v1
kind: Service
metadata:
    name: postgres
spec:
    ports:
        - port: 5432
          name: postgres
    clusterIP: None
    selector:
        app: postgres
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
    name: postgres
spec:
    serviceName: postgres
    replicas: 3
    selector:
        matchLabels:
            app: postgres
    template:
        metadata:
            labels:
                app: postgres
        spec:
            containers:
                - name: postgres
                  image: postgres:15
                  ports:
                      - containerPort: 5432
                        name: postgres
                  envFrom:
                      - configMapRef:
                            name: postgres-config
                      - secretRef:
                            name: postgres-secret
                  volumeMounts:
                      - name: data
                        mountPath: /var/lib/postgresql/data
                  livenessProbe:
                      exec:
                          command:
                              - pg_isready
                              - -U
                              - appuser
                      initialDelaySeconds: 30
                      periodSeconds: 10
                  readinessProbe:
                      exec:
                          command:
                              - pg_isready
                              - -U
                              - appuser
                      initialDelaySeconds: 5
                      periodSeconds: 5
    volumeClaimTemplates:
        - metadata:
              name: data
          spec:
              accessModes: ["ReadWriteOnce"]
              resources:
                  requests:
                      storage: 20Gi

Contoh 3: Redis Cluster

Kubernetesredis-statefulset.yml
apiVersion: v1
kind: ConfigMap
metadata:
    name: redis-config
data:
    redis.conf: |
        appendonly yes
        appendfilename "appendonly.aof"
---
apiVersion: v1
kind: Service
metadata:
    name: redis
spec:
    ports:
        - port: 6379
          name: redis
    clusterIP: None
    selector:
        app: redis
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
    name: redis
spec:
    serviceName: redis
    replicas: 3
    selector:
        matchLabels:
            app: redis
    template:
        metadata:
            labels:
                app: redis
        spec:
            containers:
                - name: redis
                  image: redis:7.2
                  ports:
                      - containerPort: 6379
                        name: redis
                  command:
                      - redis-server
                      - /etc/redis/redis.conf
                  volumeMounts:
                      - name: data
                        mountPath: /data
                      - name: config
                        mountPath: /etc/redis
                  resources:
                      requests:
                          memory: "256Mi"
                          cpu: "250m"
                      limits:
                          memory: "512Mi"
                          cpu: "500m"
            volumes:
                - name: config
                  configMap:
                      name: redis-config
    volumeClaimTemplates:
        - metadata:
              name: data
          spec:
              accessModes: ["ReadWriteOnce"]
              resources:
                  requests:
                      storage: 5Gi

Contoh 4: Kafka Cluster

Kuberneteskafka-statefulset.yml
apiVersion: v1
kind: Service
metadata:
    name: kafka
spec:
    ports:
        - port: 9092
          name: kafka
    clusterIP: None
    selector:
        app: kafka
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
    name: kafka
spec:
    serviceName: kafka
    replicas: 3
    selector:
        matchLabels:
            app: kafka
    template:
        metadata:
            labels:
                app: kafka
        spec:
            containers:
                - name: kafka
                  image: confluentinc/cp-kafka:latest
                  ports:
                      - containerPort: 9092
                        name: kafka
                  env:
                      - name: KAFKA_BROKER_ID
                        valueFrom:
                            fieldRef:
                                fieldPath: metadata.name
                      - name: KAFKA_ZOOKEEPER_CONNECT
                        value: "zookeeper:2181"
                      - name: KAFKA_ADVERTISED_LISTENERS
                        value: "PLAINTEXT://$(POD_NAME).kafka:9092"
                      - name: POD_NAME
                        valueFrom:
                            fieldRef:
                                fieldPath: metadata.name
                  volumeMounts:
                      - name: data
                        mountPath: /var/lib/kafka/data
    volumeClaimTemplates:
        - metadata:
              name: data
          spec:
              accessModes: ["ReadWriteOnce"]
              resources:
                  requests:
                      storage: 10Gi

Pod Management Policy

Control bagaimana Pod managed during operation.

OrderedReady (Default)

Pod created/deleted sequentially, waiting untuk setiap Ready:

Kubernetesyml
spec:
    podManagementPolicy: OrderedReady

Parallel

Pod created/deleted in parallel (seperti Deployment):

Kubernetesyml
spec:
    podManagementPolicy: Parallel

Useful ketika Pod order tidak matter tapi kalian masih need stable identity.

Menghapus StatefulSet

Delete StatefulSet (Keep Pod)

Kubernetesbash
sudo kubectl delete statefulset web --cascade=orphan

Pod remain running tapi tidak longer managed.

Delete StatefulSet (Delete Pod)

Kubernetesbash
sudo kubectl delete statefulset web

Pod deleted in reverse order.

Delete PVC

Kubernetesbash
sudo kubectl delete pvc -l app=nginx

Warning

Peringatan: Menghapus PVC permanently delete data. Selalu backup sebelum delete.

Kesalahan Umum dan Pitfall

Kesalahan 1: No Headless Service

Problem: StatefulSet require headless Service.

Solusi: Selalu create headless Service dulu:

Kubernetesyml
apiVersion: v1
kind: Service
metadata:
    name: nginx
spec:
    clusterIP: None  # Required untuk StatefulSet
    selector:
        app: nginx

Kesalahan 2: Wrong Service Name

Problem: serviceName tidak match Service metadata.name.

Solusi: Ensure name match:

Kubernetesyml
# Service
metadata:
    name: nginx
 
# StatefulSet
spec:
    serviceName: "nginx"  # Harus match

Kesalahan 3: No Storage Class

Problem: PVC tidak bisa provisioned.

Solusi: Ensure StorageClass exist atau specify one:

Kubernetesyml
volumeClaimTemplates:
    - metadata:
          name: data
      spec:
          storageClassName: fast-ssd
          accessModes: ["ReadWriteOnce"]
          resources:
              requests:
                  storage: 10Gi

Kesalahan 4: Delete PVC Accidentally

Problem: Data lost ketika scaling down.

Solusi: PVC preserved by design. Delete manually hanya ketika certain:

Kubernetesbash
# Scaling down tidak delete PVC
sudo kubectl scale statefulset web --replicas=1
 
# PVC remain untuk web-1, web-2
sudo kubectl get pvc

Kesalahan 5: Tidak Set Resource Limit

Problem: Pod bisa consume unlimited resource.

Solusi: Selalu set limit untuk stateful workload:

Kubernetesyml
resources:
    requests:
        memory: "512Mi"
        cpu: "500m"
    limits:
        memory: "1Gi"
        cpu: "1000m"

Best Practice

Gunakan Appropriate Storage

Pilih storage based on requirement:

Kubernetesyml
# Fast SSD untuk database
storageClassName: fast-ssd
 
# Standard HDD untuk log
storageClassName: standard

Set Pod Disruption Budget

Protect against voluntary disruption:

Kubernetespdb.yml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
    name: web-pdb
spec:
    minAvailable: 2
    selector:
        matchLabels:
            app: nginx

Gunakan Init Container

Prepare environment sebelum main container start:

Kubernetesyml
initContainers:
    - name: init-config
      image: busybox:1.36
      command:
          - sh
          - -c
          - |
              echo "Initializing..."
              # Setup configuration

Add Health Check

Monitor Pod health:

Kubernetesyml
livenessProbe:
    tcpSocket:
        port: 3306
    initialDelaySeconds: 30
    periodSeconds: 10
readinessProbe:
    exec:
        command:
            - mysqladmin
            - ping
    initialDelaySeconds: 5
    periodSeconds: 5

Gunakan Anti-Affinity

Spread Pod across node:

Kubernetesyml
affinity:
    podAntiAffinity:
        requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                  matchExpressions:
                      - key: app
                        operator: In
                        values:
                            - mysql
              topologyKey: kubernetes.io/hostname

Backup Data Regularly

Implement backup strategy:

Kubernetesbash
# Contoh: Backup MySQL
sudo kubectl exec mysql-0 -- mysqldump -u root -p$PASSWORD --all-databases > backup.sql

Melihat Detail StatefulSet

Get StatefulSet

Kubernetesbash
sudo kubectl get statefulsets
sudo kubectl get statefulsets -o wide

Describe StatefulSet

Kubernetesbash
sudo kubectl describe statefulset web

Get Pod

Kubernetesbash
sudo kubectl get pods -l app=nginx

Get PVC

Kubernetesbash
sudo kubectl get pvc

Check Pod DNS

Kubernetesbash
sudo kubectl run -it --rm debug --image=busybox:1.36 --restart=Never -- nslookup web-0.nginx

Troubleshooting StatefulSet

Check StatefulSet Status

Kubernetesbash
sudo kubectl get statefulset web
sudo kubectl describe statefulset web

Check Pod

Kubernetesbash
sudo kubectl get pods -l app=nginx
sudo kubectl describe pod web-0
sudo kubectl logs web-0

Check PVC

Kubernetesbash
sudo kubectl get pvc
sudo kubectl describe pvc www-web-0

Check Service

Kubernetesbash
sudo kubectl get service nginx
sudo kubectl describe service nginx

Check Event

Kubernetesbash
sudo kubectl get events --sort-by='.lastTimestamp'

Penutup

Pada episode 27 ini, kita telah membahas StatefulSet di Kubernetes secara mendalam. Kita sudah belajar cara manage stateful application dengan stable identity, persistent storage, dan ordered operation.

Key takeaway:

  • StatefulSet manage stateful application dengan unique identity
  • Setiap Pod dapat stable network identity (web-0, web-1, web-2)
  • Headless Service required untuk DNS-based Pod discovery
  • volumeClaimTemplates create individual PVC per Pod
  • Pod created/deleted sequentially by default
  • Predictable DNS name enable direct Pod communication
  • Scaling preserve PVC untuk data safety
  • Dua update strategy: RollingUpdate dan OnDelete
  • Pod Management Policy control parallel vs ordered operation
  • Gunakan untuk database, message queue, distributed system
  • Selalu set resource limit untuk stateful workload
  • Implement Pod Disruption Budget untuk availability
  • Gunakan anti-affinity untuk spread Pod across node
  • Backup data regularly untuk disaster recovery
  • Berbeda dari Deployment: stable identity vs random identity

StatefulSet essential untuk running stateful application di Kubernetes. Dengan memahami StatefulSet, kalian bisa confidently deploy dan manage database, distributed system, dan stateful workload lainnya dengan guaranteed identity dan storage persistence.

Bagaimana, makin jelas kan tentang StatefulSet di Kubernetes? Jadi, pastikan tetap semangat belajar dan nantikan episode selanjutnya!

Catatan

Untuk kalian yang ingin melanjutkan ke episode selanjutnya, bisa click thumbnail episode 28 di bawah ini

Episode 28Episode 28

Related Posts