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 23 February 2024. It needs to be reviewed again on 23 August 2024 by the page owner #cloud-platform .
This page was set to be reviewed before 23 August 2024 by the page owner #cloud-platform. This might mean the content is out of date.