Skip to content
Rahul Shishodiaon GitHub LinkedIn profile

StatefulSets and Headless Services

StatefulSet vs Deployment

DeploymentStatefulSet
Pod namesRandom hashOrdinal: mysql-0, mysql-1
StartupParallelSequential (default)
ShutdownAny orderReverse ordinal
Stable DNSNoYes (via headless Service)
Per-replica storageShared/nonevolumeClaimTemplates
Required-serviceName → headless Service

StatefulSet skeleton

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  replicas: 3
  serviceName: mysql-h      # must match headless Service name
  selector:
    matchLabels:
      app: mysql
  podManagementPolicy: OrderedReady   # or Parallel
  updateStrategy:
    type: RollingUpdate               # default; or OnDelete
    rollingUpdate:
      partition: 0    # only Pods with ordinal >= partition are updated
  template:
    spec:
      containers:
      - name: mysql
        image: mysql
  volumeClaimTemplates:   # PVC per Pod: data-volume-mysql-0, etc.
  - metadata:
      name: data-volume
    spec:
      accessModes: [ReadWriteOnce]
      resources:
        requests:
          storage: 500Mi

updateStrategy

TypeBehaviour
RollingUpdate (default)Updates Pods in reverse ordinal order (highest first)
OnDeleteNo automatic update; Pod updated only when manually deleted
  • partition: only Pods with ordinal ≥ partition are updated → staged rollout
    • e.g. partition: 2 with 3 replicas: only mysql-2 updated; mysql-0, mysql-1 stay on old version

Headless Service

  • clusterIP: None: DNS record per Pod, not one cluster IP
  • Service name must match StatefulSet serviceName
apiVersion: v1
kind: Service
metadata:
  name: mysql-h
spec:
  clusterIP: None
  selector:
    app: mysql
  ports:
  - port: 3306

DNS: mysql-0.mysql-h.default.svc.cluster.local (not load-balanced random Pod)

Exam drill (headless Service)

kubectl expose statefulset db-sts --name=db-headless \
  --port=5432 --target-port=5432 --cluster-ip=None -n q72
# verify: CLUSTER-IP = None, selector matches Pod labels

kubectl

kubectl create -f statefulset.yaml
kubectl scale statefulset mysql --replicas=5
kubectl delete statefulset mysql   # Pods deleted reverse order
kubectl get statefulsets

Exam tips

  • Delete StatefulSet → PVCs remain: delete PVCs manually if needed
  • Parallel policy: faster scale, no guaranteed startup order
  • Regular Service load-balances: wrong for targeting specific DB replica
  • OnDelete updateStrategy: change image via kubectl set image then delete Pods one by one
  • partition lets you canary a StatefulSet update: set to N-1, verify last Pod, then 0