Updated on 2025-08-22 GMT+08:00

StatefulSets

Overview of StatefulSets

All pods under a Deployment are identical except for their names and IP addresses. Deployments can create new pods using a pod template and delete any pod when not needed.

However, Deployments are not suitable for distributed scenarios where each pod requires its own status or independent storage, such as in distributed databases.

Distributed stateful applications often involve different roles and responsibilities. For example, databases may operate in active/standby mode, and pods may depend on each other. To deploy stateful applications in Kubernetes, pods must meet the following requirements:

  • Each pod must have a unique, fixed identifier to be recognized by other pods.
  • Each pod should be configured with separate storage resources to ensure data persistence. This allows the original data to be retained and retrieved even after a pod is deleted and recreated. Without dedicated storage, the pod's data will be lost upon deletion, and the new pod will initialize with a different state.

To address these requirements, Kubernetes provides StatefulSets:

  1. StatefulSets provide a fixed name for each pod, followed by a sequential numeric suffix (for example, pod-0, pod-1, ..., pod-N). After a pod is rescheduled, its name and hostname remain unchanged.
  2. StatefulSets use a headless Service to allocate a fixed domain name for each pod.
  3. StatefulSets create PVCs with fixed identifiers. This ensures that pods can access the original persistent data after being rescheduled.
    Figure 1 StatefulSet

Creating a Headless Service

A headless Service is required by a StatefulSet to access its pods.

Define a headless Service as follows:

  • spec.clusterIP: must be set to None to indicate a headless Service.
  • spec.ports.port: the port for communication between pods.
  • spec.ports.name: the name of the port for communication between pods.
apiVersion: v1
kind: Service       # The object type is Service.
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
    - name: nginx     # The name of the port for communication between pods
      port: 80        # The port for communication between pods
  selector:
    app: nginx        # Select the pods labeled with app:nginx.
  clusterIP: None     # Set this parameter to None to indicate a headless Service.

Create the headless Service.

# kubectl create -f headless.yaml 
service/nginx created

After the Service is created, check the Service information.

# kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
nginx        ClusterIP   None         <none>        80/TCP    5s

Creating a StatefulSet

The YAML definition of a StatefulSet is similar to that of other Kubernetes objects. The differences are as follows:

  • serviceName specifies the headless Service used by the StatefulSet. This parameter is required.
  • volumeClaimTemplates defines a template for requesting PVCs. In this example, a template named data is defined, which creates a PVC for each pod. The storageClassName field specifies the type of persistent storage. For details, see PVs, PVCs, and Storage Classes. The volumeMounts field specifies where the storage is mounted on the pods. If no persistent storage is required, you can delete volumeClaimTemplates and volumeMounts.
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nginx
spec:
  serviceName: nginx                             # The name of the headless Service
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: container-0
          image: nginx:alpine
          resources:
            limits:
              cpu: 100m
              memory: 200Mi
            requests:
              cpu: 100m
              memory: 200Mi
          volumeMounts:                           # The storage to be mounted on the pods
          - name:  data
            mountPath:  /usr/share/nginx/html     # Mount storage to /usr/share/nginx/html.
      imagePullSecrets:
        - name: default-secret
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes:
      - ReadWriteMany
      resources:
        requests:
          storage: 1Gi
      storageClassName: csi-nas                   # The type of persistent storage

Create the StatefulSet.

# kubectl create -f statefulset.yaml 
statefulset.apps/nginx created

After executing the command, verify the StatefulSet and its pods. Pod names will have sequential numeric suffixes starting from 0 and incrementing up to 2.

# kubectl get statefulset
NAME    READY   AGE
nginx   3/3     107s

# kubectl get pods
NAME      READY   STATUS    RESTARTS   AGE
nginx-0   1/1     Running   0          112s
nginx-1   1/1     Running   0          69s
nginx-2   1/1     Running   0          39s

Delete the pod named nginx-1 and recheck the pods. A new pod with the same name (nginx-1) will be created. The value 5s under the AGE field indicates that the new pod was recently created.

# kubectl delete pod nginx-1
pod "nginx-1" deleted

# kubectl get pods
NAME      READY   STATUS    RESTARTS   AGE
nginx-0   1/1     Running   0          3m4s
nginx-1   1/1     Running   0          5s
nginx-2   1/1     Running   0          1m10s

Access the pods and check their hostnames. They remain as nginx-0, nginx-1, and nginx-2.

# kubectl exec nginx-0 -- sh -c 'hostname'
nginx-0
# kubectl exec nginx-1 -- sh -c 'hostname'
nginx-1
# kubectl exec nginx-2 -- sh -c 'hostname'
nginx-2

Verify the PVCs created by the StatefulSet. These PVCs are named in the format "PVC name-StatefulSet name-Number" and are in the Bound state.

# kubectl get pvc
NAME           STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
data-nginx-0   Bound    pvc-f58bc1a9-6a52-4664-a587-a9a1c904ba29   1Gi        RWX            csi-nas        2m24s
data-nginx-1   Bound    pvc-066e3a3a-fd65-4e65-87cd-6c3fd0ae6485   1Gi        RWX            csi-nas        101s
data-nginx-2   Bound    pvc-a18cf1ce-708b-4e94-af83-766007250b0c   1Gi        RWX            csi-nas        71s

Network Identifier of a StatefulSet

After a StatefulSet is created, each of its pods is assigned a fixed name. The headless Service used by the StatefulSet provides a fixed domain name for each pod via DNS. This allows pods to communicate with each other using their domain names. These names remain unchanged even if a pod's IP address changes after the pod is recreated.

After a headless Service is created, it allocates a domain name in the following format to each pod:

<pod-name>.<svc-name>.<namespace>.svc.cluster.local

For example, the domain names for the preceding three pods are as follows:

  • nginx-0.nginx.default.svc.cluster.local
  • nginx-1.nginx.default.svc.cluster.local
  • nginx-2.nginx.default.svc.cluster.local

In practice, .<namespace>.svc.cluster.local can be omitted when accessing pods.

To verify DNS resolution, create a pod using the tutum/dnsutils image. Access the container and run nslookup to resolve the pod's domain name. The pod's IP address should be correctly resolved. The IP address of the DNS server is 10.247.3.10. When a CCE cluster is created, the CoreDNS add-on is installed by default to provide the DNS service. For details, see Kubernetes Networks.

$ kubectl run -i --tty --image tutum/dnsutils dnsutils --restart=Never --rm /bin/sh 
If you don't see a command prompt, try pressing enter.
/ # nslookup nginx-0.nginx
Server:         10.247.3.10
Address:        10.247.3.10#53
Name:   nginx-0.nginx.default.svc.cluster.local
Address: 172.16.0.31

/ # nslookup nginx-1.nginx
Server:         10.247.3.10
Address:        10.247.3.10#53
Name:   nginx-1.nginx.default.svc.cluster.local
Address: 172.16.0.18

/ # nslookup nginx-2.nginx
Server:         10.247.3.10
Address:        10.247.3.10#53
Name:   nginx-2.nginx.default.svc.cluster.local
Address: 172.16.0.19

Manually delete the two pods. After the StatefulSet recreates the pods, check their IP addresses. Then, run nslookup to resolve the domain names of the pods. You should still be able to resolve nginx-0.nginx and nginx-1.nginx. This ensures that the network identity of the StatefulSet remains unchanged.

Storage Status of a StatefulSet

As mentioned above, StatefulSets use PVCs for persistent storage. This ensures that the original data remains accessible even after pods are rescheduled. When pods are deleted, the PVCs are retained.

Figure 2 StatefulSet pod recreation process

The following uses an example to illustrate the process. Write data to the /usr/share/nginx/html directory of nginx-1. For example, modify the content of index.html to display hello world by running the following command:

# kubectl exec nginx-1 -- sh -c 'echo hello world > /usr/share/nginx/html/index.html'

After modifying the content, access https://localhost. The response should be hello world.

# kubectl exec -it nginx-1 -- curl localhost
hello world

Delete the pod named nginx-1 and recheck the pods. A new pod with the same name (nginx-1) will be created. The value 4s under the AGE field indicates that the new pod was recently created.

# kubectl delete pod nginx-1
pod "nginx-1" deleted

# kubectl get pods
NAME       READY   STATUS    RESTARTS   AGE
nginx-0    1/1     Running   0          14m
nginx-1    1/1     Running   0          4s
nginx-2    1/1     Running   0          13m

Re-access the index.html of the newly created nginx-1 pod. The content hello world is still returned. This confirms that the new pod continues to access the original storage data.

# kubectl exec -it nginx-1 -- curl localhost
hello world