StatefulSet

Identitas jaringan dan penyimpanan yang stabil pada StatefulSet beserta deployment terurutnya.

Daftar isi

StatefulSet adalah objek workload Kubernetes yang menjalankan sekelompok Pod dengan identitas jaringan dan penyimpanan yang stabil. Berbeda dari Deployment yang memperlakukan replikanya sebagai salinan yang saling menggantikan, StatefulSet memberi setiap Pod nama tetap, nama DNS tetap, dan PersistentVolumeClaim (PVC) tersendiri. Topik ini membahas perbedaan mendasar dengan Deployment, struktur manifest beserta volumeClaimTemplates, serta aturan pembuatan dan penghentian yang terurut. Simulasi di bagian akhir menampilkan identitas Pod tersebut langsung dari berkas manifest yang nyata.

Perbedaan dengan Deployment#

Deployment menjalankan replika yang setara, dan nama Pod yang dihasilkannya mengandung angka acak, misalnya catatan-7d9f8b6c5-k2xqp. Apabila Pod itu dihapus dan dibuat ulang, nama barunya hampir pasti berbeda, sehingga tidak ada satu nama pun yang dapat dijadikan alamat jangka panjang. StatefulSet memberi setiap Pod nama yang berisi ordinal, yaitu indeks urutan bilangan bulat mulai dari nol, sehingga tiga replika menghasilkan Pod bernama web-0, web-1, dan web-2. Apabila Pod web-1 hilang, Pod penggantinya selalu bernama web-1 kembali.

Nama tetap tersebut dibarengi penyimpanan yang turut terpisah. Bidang volumeClaimTemplates pada StatefulSet berfungsi sebagai template PersistentVolumeClaim yang disalin satu per satu untuk setiap Pod, sehingga web-0 dan web-1 masing-masing memegang klaimnya sendiri. Pada Deployment, semua replika biasanya berbagi satu volume yang sama atau hanya menggunakan volume efemeral, sehingga model penyimpanan per Pod tidak terbentuk. Penghapusan Pod StatefulSet tidak menghapus PersistentVolumeClaim miliknya, sehingga Pod pengganti menempel kembali ke penyimpanan yang sama.

AspekDeploymentStatefulSet
Nama PodMengandung angka acak dan berganti saat dibuat ulangOrdinal tetap, misalnya web-0 sampai web-2
Identitas PodSemua replika setaraSetiap Pod berbeda dan dikenali satu per satu
PenyimpananVolume dibagi bersama atau efemeralSatu PersistentVolumeClaim per Pod
Pembuatan dan berhentiParalel tanpa urutanTerurut dari ordinal terkecil ke terbesar
Penskalaan turunPod mana pun dapat dihapusOrdinal terbesar dihentikan lebih dahulu

Pilihan antara keduanya mengikuti sifat aplikasinya. Layanan tanpa keadaan seperti aplikasi web sederhana cukup ditangani Deployment, karena replikanya dapat ditambah dan dihapus sewaktu-waktu tanpa akibat. Basis data, antrean pesan, dan sistem dengan pembagian peran antara anggota justru memerlukan kepastian nama dan penyimpanan, sehingga StatefulSet menjadi pilihan yang tepat.

Manifest StatefulSet dan volumeClaimTemplates#

Struktur manifest StatefulSet menyerupai Deployment, yaitu selector, template, dan replicas. Dua bidang membedakannya. Bidang spec.serviceName menunjuk Service headless, yaitu Service tanpa IP virtual yang menyediakan nama DNS khusus untuk setiap Pod anggota. Bidang spec.volumeClaimTemplates berisi template PersistentVolumeClaim yang disalin controller untuk setiap Pod yang dibuat.

Nama template menentukan nama klaim yang dihasilkan. Template bernama data menghasilkan data-web-0, data-web-1, dan seterusnya mengikuti nama Pod, sehingga penskalaan ke atas otomatis membuat klaim baru tanpa menulis daftar klaim manual. Bidang accessModes dan resources pada template diwarisi seluruh klaim, dan apabila bidang storageClassName tidak ditulis, StorageClass default kluster yang digunakan. Karena setiap Pod memegang klaim ReadWriteOnce miliknya sendiri, tidak ada perselisihan penulisan antara replika pada penyimpanan yang sama.

Berkas statefulset.yaml berikut memuat dua objek dalam satu berkas, yaitu Service headless bernama web dan StatefulSet yang merujuknya melalui serviceName:

yaml statefulset.yaml
apiVersion: v1
kind: Service
metadata:
  name: web
  labels:
    app: aplikasi-catatan
spec:
  clusterIP: None
  selector:
    app: aplikasi-catatan
  ports:
    - name: http
      port: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
  labels:
    app: aplikasi-catatan
spec:
  serviceName: web
  replicas: 3
  selector:
    matchLabels:
      app: aplikasi-catatan
  updateStrategy:
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: aplikasi-catatan
    spec:
      containers:
        - name: web
          image: nginx:1.26
          ports:
            - name: http
              containerPort: 80
          volumeMounts:
            - name: data
              mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 1Gi

Urutan pembuatan dan penghentian#

StatefulSet dengan replicas: 3 tidak membuat seluruh Pod sekaligus. Pod web-0 dibuat terlebih dahulu, dan pembuatan web-1 menunggu sampai web-0 mencapai keadaan Running dan Ready, demikian pula web-2 menunggu web-1. Aturan ini disebut kebijakan OrderedReady dan menjadi bawaan controller. Penghentian berjalan pada arah yang berlawanan, yaitu ordinal terbesar berhenti lebih dahulu, sehingga penghapusan dan penskalaan turun selalu menyentuh web-2 sebelum web-1.

Urutan tersebut berguna bagi aplikasi yang membentuk anggotanya secara berantai. Basis data yang memilih satu anggota sebagai pemimpin memerlukan kepastian bahwa anggota sebelumnya sudah siap sebelum anggota berikutnya bergabung. Kebijakan updateStrategy bawaan RollingUpdate menerapkan template baru per Pod secara berurutan dari ordinal terbesar, sedangkan strategi OnDelete hanya mengganti Pod apabila Pod itu dihapus secara manual.

Perintah berikut menerapkan manifest tersebut lalu menampilkan Pod yang dihasilkan. Kolom AGE pada keluaran menunjukkan jeda antara pembuatan yang menandai urutan OrderedReady:

bash
# tidak-dijalankan
# terapkan manifest Service dan StatefulSet ke kluster
kubectl apply -f statefulset.yaml
# tampilkan pod StatefulSet beserta statusnya
kubectl get pods -l app=aplikasi-catatan
text Contoh keluaran
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          56s
web-1   1/1     Running   0          31s
web-2   1/1     Running   0          5s

Identitas yang melekat pada setiap Pod#

Diagram berikut merangkum identitas yang melekat pada setiap Pod StatefulSet web, mulai dari nama Pod, nama DNS, sampai PersistentVolumeClaim yang mengikutinya. Ketiganya dihitung dari nama objek dan bidang manifest, sehingga pola yang sama berlaku untuk replika dalam jumlah berapa pun.

StatefulSet webreplicas: 3, serviceName: webPod web-0web-0.web.default.svc.cluster.localPVC data-web-01Gi, ReadWriteOncePod web-1web-1.web.default.svc.cluster.localPVC data-web-11Gi, ReadWriteOncePod web-2web-2.web.default.svc.cluster.localPVC data-web-21Gi, ReadWriteOnceNama pod, nama DNS, dan PVC tetap stabil walaupun pod dibuat ulang
Identitas stabil setiap Pod StatefulSet beserta nama DNS dan PVC-nya

Keadaan pada diagram tetap berlaku ketika Pod dibuat ulang. Apabila web-1 mati karena Node bermasalah, controller membuat Pod web-1 baru dengan nama yang sama, menempelkannya ke data-web-1 yang tersisa. Nama DNS yang sama pun kembali aktif. Jaminan yang diberikan StatefulSet bersifat identitas, sedangkan isi penyimpanan tetap ditentukan oleh aplikasi yang berjalan di atasnya. Dengan demikian, kehilangan Pod tidak pernah berarti kehilangan alamat atau klaim. Nama yang muncul kembali adalah nama yang sama yang dihitung dari ordinal yang sama, bukan nama baru yang kebetulan mirip.

Simulasi identitas StatefulSet#

Skrip berikut membaca statefulset.yaml menggunakan PyYAML, mengambil nama StatefulSet, jumlah replika, dan nama template klaim dari manifest, lalu menyusun nama Pod, nama DNS, dan nama PersistentVolumeClaim yang dihasilkan controller. Baris terakhirnya menampilkan urutan pembuatan dan urutan berhenti sesuai kebijakan OrderedReady. Seluruh nama dihitung hanya dari bidang manifest, sehingga pola tersebut dapat diprediksi sebelum manifest diterapkan.

python simulasi-statefulset.py
"""Nama pod, DNS stabil, dan urutan siklus hidup StatefulSet dari manifest."""
import yaml

NAMESPACE = "default"  # manifest tanpa metadata.namespace berjalan di namespace default

with open("statefulset.yaml", encoding="utf-8") as berkas:
    dokumen = list(yaml.safe_load_all(berkas))

service, sts = dokumen[0], dokumen[1]
spec = sts["spec"]
nama_sts = sts["metadata"]["name"]
replika = spec["replicas"]
nama_service = spec["serviceName"]
nama_klaim = spec["volumeClaimTemplates"][0]["metadata"]["name"]
penyimpanan = spec["volumeClaimTemplates"][0]["spec"]["resources"]["requests"]["storage"]

print(f"StatefulSet : {nama_sts}")
print(f"serviceName : {nama_service} (Service headless, clusterIP: {service['spec']['clusterIP']})")
print(f"replicas    : {replika}")
print()
print("Identitas stabil per pod:")
for i in range(replika):
    pod = f"{nama_sts}-{i}"
    dns = f"{pod}.{nama_service}.{NAMESPACE}.svc.cluster.local"
    pvc = f"{nama_klaim}-{pod}"
    print(f"  {pod}  DNS: {dns}  PVC: {pvc} ({penyimpanan})")
print()
print("Urutan pembuatan :", " -> ".join(f"{nama_sts}-{i}" for i in range(replika)))
print("                   OrderedReady: menunggu pod sebelumnya Running dan Ready")
print("Urutan berhenti  :", " -> ".join(f"{nama_sts}-{i}" for i in range(replika - 1, -1, -1)))
print("                   penghapusan dan penskalaan turun mulai dari ordinal terbesar")
text Keluaran
StatefulSet : web
serviceName : web (Service headless, clusterIP: None)
replicas    : 3

Identitas stabil per pod:
  web-0  DNS: web-0.web.default.svc.cluster.local  PVC: data-web-0 (1Gi)
  web-1  DNS: web-1.web.default.svc.cluster.local  PVC: data-web-1 (1Gi)
  web-2  DNS: web-2.web.default.svc.cluster.local  PVC: data-web-2 (1Gi)

Urutan pembuatan : web-0 -> web-1 -> web-2
                   OrderedReady: menunggu pod sebelumnya Running dan Ready
Urutan berhenti  : web-2 -> web-1 -> web-0
                   penghapusan dan penskalaan turun mulai dari ordinal terbesar

Keluaran tersebut memperlihatkan pola penamaan yang selalu dapat dihitung sebelum manifest diterapkan ke kluster. Nama DNS menempatkan nama Pod di awal dan nama Service headless pada segmen berikutnya, sedangkan nama klaim menempelkan nama template di depan nama Pod. Urutan pembuatan naik dari ordinal nol, sedangkan urutan berhenti turun dari ordinal terbesar, sehingga kedua arah siklus hidup bekerja secara simetris. Urutan pada baris terakhir juga menegaskan bahwa penghapusan tidak pernah menyentuh web-0 lebih dahulu, karena Pod itu memegang klaim yang dibuat paling awal. Pola tersebut memungkinkan klien menyiapkan alamat tujuan bahkan sebelum Pod dibuat, karena seluruh nama bersifat deterministik terhadap nama StatefulSet, nama Service, dan nama template klaim.

Kesalahan umum#

Tiga kesalahan berikut paling sering muncul ketika StatefulSet baru diperkenalkan pada sebuah aplikasi. Gejalanya berbeda satu sama lain, sehingga penyebabnya perlu diperiksa satu per satu. Setiap butir menyebut gejala yang terlihat, penyebab yang paling umum, dan perbaikan yang dapat langsung dijalankan.

  • Mengubah spec.selector setelah StatefulSet dibuat. Bidang selector tidak dapat diubah setelah objek terbentuk, sehingga API server menolak perubahan tersebut dengan pesan error. Perbaikannya adalah membuat objek baru dengan selector yang benar, lalu memindahkan beban kerja ke objek tersebut.
  • Memilih StatefulSet untuk aplikasi tanpa keadaan. Pembuatan terurut membuat penerapan lebih lambat, sedangkan identitas per Pod tidak memberi manfaat apa pun bagi replika yang setara. Perbaikannya adalah menggunakan Deployment, yang menjalankan replika secara paralel dan mengganti Pod mana pun dengan leluasa.
  • Mengira PersistentVolumeClaim ikut terhapus. Menghapus StatefulSet menghentikan Pod tetapi membiarkan klaim bertahan, sehingga penyimpanan menumpuk tanpa disadari setelah beberapa kali penerapan. Perbaikannya adalah memeriksa kubectl get pvc dan menghapus klaim secara eksplisit apabila datanya memang tidak diperlukan lagi.

Ringkasan#

  • StatefulSet memberi setiap Pod nama ordinal tetap, nama DNS stabil, dan satu PersistentVolumeClaim per Pod melalui volumeClaimTemplates.
  • Pembuatan berjalan dari ordinal terkecil dengan menunggu keadaan siap, sedangkan penghentian dan penskalaan turun berjalan dari ordinal terbesar.
  • Pod yang dibuat ulang menempel kembali ke nama dan klaim yang sama, sehingga identitas bertahan di seluruh siklus hidup.
  • Service headless pada serviceName menjadi penyedia nama DNS per Pod bagi seluruh anggota StatefulSet.

Referensi#