A special kind of controller for Pods

Hamel: you probably don’t need this. JUST SKIP THESE NOTES

[[StatefulSet]] is a Pod controller, just like [[5. ReplicaSets]] or [[DaemonSets]]

When you deploy a StatefulSet, it creates Pods with predictable names, which can be individually accessed over DNS, and starts them in order; the first Pod needs to be up and running before the second Pod is created.

If you are trying to model database on K8s, you might use StatefulSet. However, don’t put DBs on K8s - use a managed service for that instead. StatefulSet just gives you determinstic Pod names and networking, you have to take care of synching your apps yourself. That would be outside the scope of what DS should do IMO.

Here is kind: StatefulSet

 % cat todo-list/db/todo-db.yaml
apiVersion: apps/v1
kind: StatefulSet
name: todo-db
labels:
kiamol: ch08
spec:
selector:
matchLabels:
app: todo-db
serviceName: todo-db
replicas: 2
template:
labels:
app: todo-db

When you get pods, they will be incremented from 0, this allows you network/communicate with them deterministically.

% kl get po
todo-db-0   1/1     Running   0          23s
todo-db-1   1/1     Running   0          21s

StatefulSet is a controller, so if you delete a pod the StatefulSet will recreate it.

InitContainers

You can bootstrap pods with initcontainers and stateful sets. For example.

apiVersion: apps/v1
kind: StatefulSet
...
initContainers:
- name: wait-service
image: kiamol/ch03-sleep
envFrom:
- configMapRef:
name: todo-db-env
command: ['/scripts/wait-service.sh']

The script says if its running in Pod 0 do nothing, but if its running in Pod 1 then replicate the master. This is just an idea, you probably should never do this yourself.

Networking In StatefulSets

You need to have a special configuration “headless Service” to setup newtworking for StatefulSets, by setting ClusterIP: None

%cat todo-list/db/todo-db-service.yaml
apiVersion: v1
kind: Service
name: todo-db
labels:
kiamol: ch08
spec:
ports:                  # 5432 is the port Postgres uses
- port: 5432
targetPort: 5432
name: postgres
selector:
app: todo-db
clusterIP: None             # Note how this is None

The pod will be reachable at pod-name.service-name.cluster-domain-suffix. for example:

todo-db-0.todo-db.default.svc.cluster.local

A StatefulSet can use a Headless Service to control the domain of its Pods. The domain managed by this Service takes the form: $$(service name).$$(namespace).svc.cluster.local, where “cluster.local” is the cluster domain. As each Pod is created, it gets a matching DNS subdomain, taking the form: $$(podname).$$(governing service domain), where the governing service is defined by the serviceName field on the StatefulSet.

This is also related to the serviceName field on the StatefulSet

You can lookup the cluster-domain-suffix like this :

kl exec deploy/sleep -- sh -c 'nslookup todo-db'

The headless service still does load balancing, but lets you access the Pod

Storage

For DBs you want each pod to have its own persistent disk, there is a shortcut: using volumeClaimTemplates which makes sure each Pod in the stateful set always gets its own persistent volume.

% cat sleep/sleep-with-pvc.yaml
...
kind: StatefulSet
template:
...
volumeClaimTemplates:
name: data
labels:
kiamol: ch08
spec:
accessModes:
resources:
requests:
storage: 5Mi

Each pod will get a PVC created dynamically, which will create a Persistent Volume using the default storage class (or the requested storage class if included in the spec).

The link b/w each pod and its PVC is maintained when pods are replaced. For example:

# create a file
kl exec sleep-with-pvc-0 -- sh -c 'echo pod 0 > /data/pod.txt'
#delete the pod
kl delete po sleep-with-pvc-0
# this will show the right contents
kl exec sleep-with-pvc-0 -- cat /data/pod.txt`