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