Skip to main content

StatefulSets (Pods with Persistent Volumes)

Introduction

Like a Deployment, a StatefulSet manages Pods that are based on an identical container spec. Unlike a Deployment, a StatefulSet maintains a sticky identity for each of their Pods. These pods are created from the same spec, but are not interchangeable: each has a persistent identifier (UID) that it maintains across any rescheduling. It also provides guarantees on the ordering and uniqueness of the Pods.

StatefulSets also require a headless service to manage network identities for pods. Each Pod gets a sticky network subdomain and SRV record that does not change if a Pod is deleted and recreated.

You can see an example below where a Pod is deleted, but then recreated with the same name using the StatefulSet spec.

kubectl get pods
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          3d
web-1   1/1     Running   0          3d

kubectl delete pod web-0
pod "web-0" deleted

kubectl get pods
NAME    READY   STATUS              RESTARTS   AGE
web-0   0/1     ContainerCreating   0          7s
web-1   1/1     Running             0          3d

Headless Service

As mentioned above, a headless service is required for StatefulSets, where the network type clusterIP has a value of None. This does not predefine an IP for the service.

apiVersion: v1
kind: Service
metadata:
  name: nginx-service #Name of Service
  labels:
    app: nginx # Pod label
spec:
  ports:
  - port: 8080 # Mapped port used by service
    name: web # Named port
  clusterIP: None # Headless service setting
  selector:
    app: nginx # Selector should match the name of the app being deployed as the Statefulset

StatefulSet creation

Below is an example of a StatefulSet definition:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web # Name of StatefulSet
spec:
  selector:
    matchLabels:
      app: nginx # defines a set of pods to be managed by the StatefulSet
  serviceName: "nginx-service" #  The name of the headless service
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx # This should be the same of selector:matchLabels:app
    spec:
      containers: # Container images with their corresponding settings
      - name: nginx
        image: bitnami/nginx:latest
        ports:
        - containerPort: 8080
          name: web
        volumeMounts:
        - name: www
          mountPath: "/usr/share/nginx/html"
        securityContext:
          runAsNonRoot: true
          runAsUser: 1000
          runAsGroup: 1000
          fsGroup: 1000
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "gp2-expand" # StorageClass name used to create PV
      resources:
        requests:
          storage: 4Gi # Storage resource request size

Please use StorageClass gp2-expand to be able to expand PersistentVolumes in the future.

Once all pods are created for the StatefulSet, they have a unique identity based on the metadata name of the StatefulSet. The pods are named using the following: [StatefulSet_Name]-[Ordinal_Index] e.g. web-0

kubectl get pods
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          22m
web-1   1/1     Running   0          22m
web-2   1/1     Running   0          21m
web-3   1/1     Running   0          21m
web-4   1/1     Running   0          20m
web-5   1/1     Running   0          20m
web-6   1/1     Running   0          20m

All PersistentVolumeClaims would have all been created also. The PersistentVolumeClaims are named using the following: [VolumeClaim_Name]-[StatefulSet_Name]-[Ordinal_Index]

kubectl get pvc
NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-web-0   Bound    pvc-0ca662d1-9299-11e9-b4c5-0a5b8815a91e   4Gi        RWO            gp2-expand      29m
www-web-1   Bound    pvc-1cc800d2-9299-11e9-b4c5-0a5b8815a91e   4Gi        RWO            gp2-expand      28m
www-web-2   Bound    pvc-2abbbeb6-9299-11e9-b4c5-0a5b8815a91e   4Gi        RWO            gp2-expand      28m
www-web-3   Bound    pvc-40e2bc95-9299-11e9-b4c5-0a5b8815a91e   4Gi        RWO            gp2-expand      27m
www-web-4   Bound    pvc-4ebb8afd-9299-11e9-b4c5-0a5b8815a91e   4Gi        RWO            gp2-expand      27m
www-web-5   Bound    pvc-641f5a61-9299-11e9-b4c5-0a5b8815a91e   4Gi        RWO            gp2-expand      26m
www-web-6   Bound    pvc-6c2a187c-9299-11e9-b4c5-0a5b8815a91e   4Gi        RWO            gp2-expand      26m

If a Pod is deleted, it will be rescheduled, with the same name, and the corresponding above PersistentVolumeClaims will be attached. The Pod also has and will keep a network ID that persists through restarts.

If a StatefulSet is deleted, the PersistentVolumeClaims (PVCs) remain along with PersistentVolumes (PVs). PVCs and PVs have to be deleted separately.

Advisory note: Expanding Persistent Volumes

It is currently not possible to expand PVs using the StatefulSet template. There is a manual way to do this, which the Cloud Platform team can help with if required.

This page was last reviewed on 2 September 2024. It needs to be reviewed again on 2 March 2025 by the page owner #cloud-platform .
This page was set to be reviewed before 2 March 2025 by the page owner #cloud-platform. This might mean the content is out of date.