StatefulSet

Applicativi stateful gestiscono dati e hanno bisogno di tracciarli continuamente, per esempio MySQL, Oracle e PostgreSQL.

Uno StatefulSet è il controller appropriato per un applicativo stateful.

Ad ogni pod gestito viene assegnato un numero identificativo ordinale anzichè casuale e i pod vengono creati in ordine e cancellati in ordine inverso. Un nuovo pod è creato clonando il pod precedente solo quando è nello stato Running.

Le richieste di lettura da un volume associato vengono inviate a tutti i pod dello StatefulSet. Le richieste di scrittura sul volume associato vengono inviate solo al primo pod, e i dati modificati sono sincronizzati agli altri pod.

Cancellare un pod di uno StatefulSet non rimuove i volumi associati con l'applicativo.

Per l'esercizio che segue occorrono due Persistent Volumes, che i pod dello Stateful Set useranno. Normalmente un Provisioner creerebbe volumi dinamici all'occorrenza.

Il manifest che segue combina la definizione dei due persistent volumes con quella dello StatefulSet:

vim ~/scripts/stateful.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: www-volume-1
spec:
  storageClassName: standard
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /data/www/
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: www-volume-2
spec:
  storageClassName: standard
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /data/www/
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: registry.k8s.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

Aprire due finestre di terminale.

Nella prima digitare:

kubectl get pods --watch -l app=nginx

Nella seconda digitare:

kubectl apply -f ~/scripts/stateful.yml

Mentre nella seconda viene generato l'applicativo, il rapporto nella prima finestra è:

NAME    READY   STATUS    RESTARTS   AGE
web-0   0/1     Pending   0          0s
web-0   0/1     Pending   0          1s
web-0   0/1     ContainerCreating   0          1s
web-0   1/1     Running             0          5s
web-1   0/1     Pending             0          0s
web-1   0/1     Pending             0          2s
web-1   0/1     ContainerCreating   0          3s
web-1   1/1     Running             0          9s

I pod sono generati in sequenza.

Il pod web-1 viene generato solo quando il pod web-0 è nello stato di Running.

Il servizio è dato da:

kubectl get service nginx

Lo StatefulSet è dato da:

kubectl get statefulset web

I pod dell'applicativo si possono vedere con:

kubectl get pods -l app=nginx

Ogni pod ha uno hostname basato sul suo numero ordinale. Si possono vedere con:

for i in 0 1; do kubectl exec "web-$i" -- sh -c 'hostname'; done

Il loro nome è risolto al DNS di cluster. Si può compire un'interrogazione lanciando un pod aggiuntivo nel cluster:

kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm

E al pronto risultante dare:

nslookup web-0.nginx

Proviamo a cancellare esplicitamente i pod dello StatefulSet.

In una finestra digitare:

kubectl get pod --watch -l app=nginx

E nell'altra finestra digitare:

kubectl delete pod -l app=nginx

I pod vengono cancellati, ma subito ricreati, in ordine.

Scalare uno StatefulSet

In una finestra dare:

kubectl get pods --watch -l app=nginx

E nell'altra finestra:

kubectl scale sts web --replicas=5

Si nota che la creazione di nuovi pod procede per gradi, il nuovo pod sequenziale non viene creato finchè il precedente non è nello ststo di Running.

Si può anche scalare a decrescere, applicando una patch:

kubectl patch sts web -p '{"spec":{"replicas":3}}'

E' da notare che i Persistent Volume Claims sono ancora 5:

kubectl get pvc -l app=nginx

I PVC creati da uno StatefulSet non vengono cancellati anche quando sono cancellati i pod che li usano.

E' da notare che durante l'operazione di scale sono stati costruiti tre ulteriori Persistent Volumes, ma con una policy di Delete, anzichè Retain.

Cancellazione di uno StatefulSet

Tramite il manifest:

kubectl delete -f ~/scripts/stateful.yml

I PVC e i loro PV non vengono cancellati. I PV creati da manifest entrano in stato Terminating ma non sono rimossi finchè i corrispondenti PVC non vengono cancellati.

Cancellare a mano i PVC:

kubectl delete pvc www-web-0 www-web-1 www-web-2 www-web-3 www-web-4

I rimanenti PV sono temporaneamente visibili, ma vengono presto cancellati.