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:
- The StatefulSet is scaled up using:
kubectl scale deployment rabbitmq --replicas=5 --namespace $NAMESPACE
rabbitmq-3
is started- As soon as Pod
rabbitmq-3
is inReady
state the same procedure starts forrabbitmq-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.
- Highest-numbered Pod is stopped
- New Pod (with new image tag) is started
- If the new Pod successfully starts, the procedure is repeated for the second highest-numbered Pod
- 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.