TL;DR (Quick Summary)#
- StatefulSet vs Deployment:
- Deployment: Pods get random hashes (
app-75b69bd654-2x9lq), share storage claims, and start/stop in parallel. - StatefulSet: Pods get persistent ordinal names (
db-0,db-1,db-2), unique DNS hostnames, separate PVs viavolumeClaimTemplates, and start/stop sequentially.
- Deployment: Pods get random hashes (
- Headless Service (
clusterIP: None): Disables load balancing VIP so DNS queries directly resolve individual Pod IPs for direct node-to-node replication.
1. StatefulSet Architecture & Ordinal Indexing#
graph TD
subgraph StatefulSet["StatefulSet: redis-cluster (replicas: 3)"]
P0["Pod: redis-cluster-0
DNS: redis-cluster-0.redis-service"]
P1["Pod: redis-cluster-1
DNS: redis-cluster-1.redis-service"]
P2["Pod: redis-cluster-2
DNS: redis-cluster-2.redis-service"]
end
subgraph VolumeClaimTemplates["volumeClaimTemplates"]
PVC0["PVC: data-redis-cluster-0"] -->|Binds| PV0["PV 0 (10Gi)"]
PVC1["PVC: data-redis-cluster-1"] -->|Binds| PV1["PV 1 (10Gi)"]
PVC2["PVC: data-redis-cluster-2"] -->|Binds| PV2["PV 2 (10Gi)"]
end
P0 -.-> PVC0
P1 -.-> PVC1
P2 -.-> PVC2
2. Headless Service Manifest (clusterIP: None)#
A Headless Service does not assign a single Virtual IP. Instead, it creates A-records in CoreDNS for each individual Pod endpoint matching the selector.
Create headless-service.yaml:
apiVersion: v1
kind: Service
metadata:
name: redis-headless
namespace: default
labels:
app: redis
spec:
clusterIP: None # Headless Service
selector:
app: redis
ports:
- port: 6379
name: redis-port3. Complete StatefulSet Manifest#
Create statefulset.yaml:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-cluster
namespace: default
spec:
serviceName: "redis-headless"
replicas: 3
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7.0-alpine
ports:
- containerPort: 6379
name: redis-port
volumeMounts:
- name: redis-data
mountPath: /data
# Dynamically creates a PVC for EACH ordinal Pod replica!
volumeClaimTemplates:
- metadata:
name: redis-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1GiApply both manifests:
kubectl apply -f headless-service.yaml
kubectl apply -f statefulset.yaml4. Observing Sequential Deployment & DNS Names#
Watch the Pod creation process in real-time:
kubectl get pods -l app=redis --watchSequential Output:
NAME READY STATUS RESTARTS AGE
redis-cluster-0 0/1 ContainerCreating 0 2s
redis-cluster-0 1/1 Running 0 8s
redis-cluster-1 0/1 ContainerCreating 0 1s
redis-cluster-1 1/1 Running 0 7s
redis-cluster-2 0/1 ContainerCreating 0 1s
redis-cluster-2 1/1 Running 0 6sNotice how redis-cluster-1 was NOT started until redis-cluster-0 became fully Ready!
Checking Individual DNS Hostnames#
Each Pod gets a deterministic, predictable network address:
redis-cluster-0.redis-headless.default.svc.cluster.localredis-cluster-1.redis-headless.default.svc.cluster.localredis-cluster-2.redis-headless.default.svc.cluster.local
List dynamically created PVCs:
kubectl get pvc -l app=redisNAME STATUS VOLUME CAPACITY ACCESS MODES
redis-data-redis-cluster-0 Bound pvc-11111111-2222-3333-4444-555555555555 1Gi RWO
redis-data-redis-cluster-1 Bound pvc-66666666-7777-8888-9999-000000000000 1Gi RWO
redis-data-redis-cluster-2 Bound pvc-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee 1Gi RWO5. Summary & Next Steps#
StatefulSets guarantee ordering, persistent identities, and dedicated storage volumes per replica for database systems.
In Episode 10: DaemonSets & Cluster Node Agents, we will shift gears to node-level background workloads, learning how DaemonSets deploy log collectors and metric agents automatically across every worker node in your cluster!

