Skip to content

StatefulSets

Environment Variables

We are going to use some environment variables in this tutorial. Please make sure you have set them correctly.

# check if the environment variables are set if not set them
export NAMESPACE=<namespace>
echo $NAMESPACE

Stateless applications or applications with a stateful backend can be described as Deployments. However, sometimes your application has to be stateful. Examples would be an application that needs a static, non-changing hostname every time it starts or a clustered application with a strict start/stop order of its services (e.g. RabbitMQ). These features are offered by StatefulSets.

Note

This tutorial does not depend on the previous ones.

Consistent hostnames

While in normal Deployments a hash-based name of the Pods (also represented as the hostname inside the Pod) is generated, StatefulSets create Pods with preconfigured names. An example of a RabbitMQ cluster with three instances (Pods) could look like this:

rabbitmq-0
rabbitmq-1
rabbitmq-2

Scaling

Scaling is handled differently in StatefulSets. When scaling up from 3 to 5 replicas in a Deployment, two additional Pods are started at the same time (based on the configuration). Using a StatefulSet, scaling is done serially:

Let’s use our RabbitMQ example again:

  1. The StatefulSet is scaled up using: kubectl scale deployment rabbitmq --replicas=5 --namespace $NAMESPACE
  2. rabbitmq-3 is started
  3. As soon as Pod rabbitmq-3 is in Ready state the same procedure starts for rabbitmq-4

When scaling down, the order is inverted. The highest-numbered Pod will be stopped first. As soon as it has finished terminating the now highest-numbered Pod is stopped. This procedure is repeated as long as the desired number of replicas has not been reached.

Update procedure

During an update of an application with a StatefulSet the highest-numbered Pod will be the first to be updated and only after a successful start the next Pod follows.

  1. Highest-numbered Pod is stopped
  2. New Pod (with new image tag) is started
  3. If the new Pod successfully starts, the procedure is repeated for the second highest-numbered Pod
  4. And so on

If the start of a new Pod fails, the update will be interrupted so that the architecture of your application won’t break.

Dedicated persistent volumes

A very convenient feature is that unlike a Deployment a StatefulSet makes it possible to attach a different, dedicated persistent volume to each of its Pods. This is done using a so-called VolumeClaimTemplate. This spares you from defining identical Deployments with 1 replica each but different volumes.

Conclusion

The controllable and predictable behavior can be a perfect match for applications such as RabbitMQ or etcd, as you need unique names for such application clusters.

Task 1: Create a StatefulSet

Create a file named sts_nginx-cluster.yaml with the following definition of a StatefulSet:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nginx-cluster
spec:
  serviceName: "nginx"
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginxinc/nginx-unprivileged:1.18-alpine
          ports:
            - containerPort: 80
              name: nginx
          resources:
            limits:
              cpu: 40m
              memory: 64Mi
            requests:
              cpu: 10m
              memory: 32Mi

Create the StatefulSet:

kubectl apply -f sts_nginx-cluster.yaml --namespace $NAMESPACE

To watch the pods’ progress, open a second console and execute the watch command:

kubectl get pods --selector app=nginx -w --namespace $NAMESPACE

Note

Friendly reminder that the kubectl get -w command will never end unless you terminate it with CTRL-c.

Task 2: Scale the StatefulSet

Scale the StatefulSet up:

kubectl scale statefulset nginx-cluster --replicas=3 --namespace $NAMESPACE

You can again watch the pods’ progress like you did in the first task.

Task 3: Update the StatefulSet

In order to update the image tag in use in a StatefulSet, you can use the kubectl set image command. Set the StatefulSet’s image tag to latest:

kubectl set image statefulset nginx-cluster nginx=docker.io/nginxinc/nginx-unprivileged:latest --namespace $NAMESPACE

Task 4: Rollback

Imagine you just realized that switching to the latest image tag was an awful idea (because it is generally not advisable). Rollback the change:

kubectl rollout undo statefulset nginx-cluster --namespace $NAMESPACE

Task 5: Cleanup

As with every other Kubernetes resource you can delete the StatefulSet with:

kubectl delete statefulset nginx-cluster --namespace $NAMESPACE

Further information can be found in the Kubernetes’ StatefulSet documentation or this published article.