Skip to main content

Lab 6.4 - StatefulSet with Persistent Storage

Lab Objectives​

By the end of this lab, you will be able to:

  • Create a StatefulSet with persistent volumes.
  • Understand volume provisioning for each Pod.
  • Verify stable Pod and volume identities.
  • Test data persistence during restarts.
  • Understand the differences between StatefulSet and Deployment for storage.

Estimated Duration​

60-75 minutes

Prerequisites​

  • kubectl installed and configured.
  • Functional local Kubernetes cluster (minikube or kind).
  • Knowledge of StatefulSets and persistent storage (Chapters 6.4 and 7.1).

Part 1: StorageClass Preparation​

For a StatefulSet, each Pod needs its own persistent volume. We will use a StorageClass for dynamic provisioning.

Step 1.1: Create a StorageClass​

Create a file storageclass-statefulset.yaml:

# storageclass-statefulset.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: statefulset-storage
provisioner: k8s.io/minikube-hostpath # For minikube, or kubernetes.io/no-provisioner for kind
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
allowVolumeExpansion: false

Note: If using kind or a cluster without a provisioner, you will need to create PVs manually. For this lab, we assume minikube.

Apply the StorageClass:

kubectl apply -f storageclass-statefulset.yaml

Part 2: Creating a StatefulSet with Storage​

We will create a simple StatefulSet that stores data in persistent volumes.

Step 2.1: Create the StatefulSet​

Create a file statefulset-with-storage.yaml:

# statefulset-with-storage.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web-app
spec:
serviceName: web-app-service # Headless Service name (required for StatefulSet)
replicas: 3 # Create 3 Pods
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: nginx
image: nginx:1.20
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html # Persistent volume mount
command: ["/bin/sh", "-c"]
args:
- |
echo "Pod $(hostname) - $(date)" > /usr/share/nginx/html/index.html
echo "Persistent data from Pod $(hostname)" >> /usr/share/nginx/html/data.txt
nginx -g "daemon off;"
volumeClaimTemplates: # Template to create a PVC for each Pod
- metadata:
name: www # Volume name (used in volumeMounts)
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: statefulset-storage # Uses our StorageClass
resources:
requests:
storage: 100Mi # Volume size for each Pod

Explanation:

  • serviceName: web-app-service: A StatefulSet requires a headless Service for DNS discovery.
  • volumeClaimTemplates: Automatically creates a PVC for each Pod in the StatefulSet. Each Pod will have its own persistent volume.
  • Pods will be named web-app-0, web-app-1, web-app-2 in order.

Step 2.2: Create the Headless Service​

A StatefulSet requires a headless Service (ClusterIP with clusterIP: None) for DNS discovery:

# service-headless.yaml
apiVersion: v1
kind: Service
metadata:
name: web-app-service
spec:
clusterIP: None # Headless service
selector:
app: web-app
ports:
- port: 80
name: web

Apply the Service:

kubectl apply -f service-headless.yaml

Apply the StatefulSet:

kubectl apply -f statefulset-with-storage.yaml

Part 3: Observing Provisioning​

Step 3.1: Verify Pod Creation​

StatefulSet Pods are created sequentially (one by one):

kubectl get pods -l app=web-app -w

You should see Pods created in order: web-app-0, then web-app-1, then web-app-2.

Step 3.2: Verify Created PVCs​

Each Pod has its own PVC:

kubectl get pvc

You should see three PVCs:

  • www-web-app-0
  • www-web-app-1
  • www-web-app-2

Each PVC is bound to its own PV:

kubectl get pv

Step 3.3: Verify Data in Each Pod​

Each Pod has its own persistent data:

# Pod 0
kubectl exec web-app-0 -- cat /usr/share/nginx/html/index.html
kubectl exec web-app-0 -- cat /usr/share/nginx/html/data.txt

# Pod 1
kubectl exec web-app-1 -- cat /usr/share/nginx/html/index.html
kubectl exec web-app-1 -- cat /usr/share/nginx/html/data.txt

# Pod 2
kubectl exec web-app-2 -- cat /usr/share/nginx/html/index.html
kubectl exec web-app-2 -- cat /usr/share/nginx/html/data.txt

Each Pod should display its own hostname and data.


Part 4: Persistence Test​

Step 4.1: Add Unique Data​

Add unique data to each Pod:

kubectl exec web-app-0 -- sh -c "echo 'Unique data Pod 0 - $(date)' >> /usr/share/nginx/html/data.txt"
kubectl exec web-app-1 -- sh -c "echo 'Unique data Pod 1 - $(date)' >> /usr/share/nginx/html/data.txt"
kubectl exec web-app-2 -- sh -c "echo 'Unique data Pod 2 - $(date)' >> /usr/share/nginx/html/data.txt"

Verify the data:

kubectl exec web-app-0 -- cat /usr/share/nginx/html/data.txt

Step 4.2: Delete a Pod​

Delete a Pod to test persistence:

kubectl delete pod web-app-0

The StatefulSet will automatically recreate the Pod with the same name (web-app-0).

Wait for the new Pod to be Running:

kubectl get pods -l app=web-app

Step 4.3: Verify Persistence​

Verify that data is still present after the restart:

kubectl exec web-app-0 -- cat /usr/share/nginx/html/data.txt

You should see all previous data, including "Unique data Pod 0", proving that the persistent volume was remounted with the same data.


Part 5: StatefulSet Scaling​

Step 5.1: Increase Replica Count​

Increase the number of Pods:

kubectl scale statefulset web-app --replicas=5

Verify that new Pods and PVCs are created:

kubectl get pods -l app=web-app
kubectl get pvc

You should see web-app-3 and web-app-4 with their respective PVCs.

Step 5.2: Decrease Replica Count​

Reduce the number of Pods:

kubectl scale statefulset web-app --replicas=2

Verify that Pods are deleted in reverse order (from highest to lowest):

kubectl get pods -l app=web-app -w

Important: PVCs are NOT automatically deleted. Verify:

kubectl get pvc

The PVCs from deleted Pods (www-web-app-2, www-web-app-3, www-web-app-4) are still present, preserving the data.


Part 6: Cleanup​

Delete the created resources:

kubectl delete statefulset web-app
kubectl delete service web-app-service
kubectl delete pvc -l app=web-app # Delete all StatefulSet PVCs
# PVs will be automatically deleted if reclaimPolicy: Delete
kubectl delete -f storageclass-statefulset.yaml

Lab Summary​

In this lab, you explored using StatefulSets with persistent storage. You learned to create a StatefulSet with volumeClaimTemplates, observe sequential Pod and volume provisioning, test data persistence during restarts, and understand scaling behavior with persistent volumes.


Next Steps​

This storage module is now complete. You can proceed to Module 7 on Advanced Workloads.

Module 7: Advanced Workloads


Lab created: December 2024